學成在線(第10天)

 課程發佈

 需求分析

課程發佈後將生成正式的課程詳情頁面,課程發佈後用戶便可瀏覽課程詳情頁面,並開始課程的學習。
課程發佈生成課程詳情頁面的流程與課程預覽業務流程相同,以下:
一、用戶進入教學管理中心,進入某個課程的管理界面
二、點擊課程發佈,前端請求到課程管理服務
三、課程管理服務遠程調用CMS生成課程發佈頁面,CMS將課程詳情頁面發佈到服務器
四、課程管理服務修改課程發佈狀態爲 「已發佈」,並向前端返回發佈成功
五、用戶在教學管理中心點擊「課程詳情頁面」連接,查看課程詳情頁面內容html

 CMS 一鍵發佈接口

根據需求分析內容,須要在 cms服務增長頁面發佈接口供課程管理服務調用,此接口的功能以下:
一、接收課程管理服務發佈的頁面信息
二、將頁面信息添加到 數據庫(mongodb)
三、對頁面信息進行靜態化
四、將頁面信息發佈到服務器前端

接口定義

一、建立響應結果類型
頁面發佈成功cms返回頁面的url
頁面Url= cmsSite.siteDomain+cmsSite.siteWebPath+ cmsPage.pageWebPath + cmsPage.pageNameweb

@Data
@NoArgsConstructor//無參構造器註解
public class CmsPostPageResult extends ResponseResult  {
    String pageUrl;
    public CmsPostPageResult(ResultCode resultCode,String pageUrl) {
        super(resultCode);
        this.pageUrl = pageUrl;
    }
}

二、在api工程定義頁面發佈接口spring

@ApiOperation("一鍵發佈頁面")
public CmsPostPageResult postPageQuick(CmsPage cmsPage);

Dao

一、站點dao
接口中須要獲取站點的信息(站點域名、站點訪問路徑等mongodb

public interface CmsSiteRepository extends MongoRepository<CmsSite,String> {
}

Service

一、添加頁面,若是已存在則更新頁面數據庫

//添加頁面,若是已存在則更新頁面
public CmsPageResult save(CmsPage cmsPage){
    //校驗頁面是否存在,根據頁面名稱、站點Id、頁面webpath查詢
    CmsPage cmsPage1 =
cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(),
cmsPage.getSiteId(), cmsPage.getPageWebPath());
    if(cmsPage1 !=null){
        //更新
        return this.update(cmsPage1.getPageId(),cmsPage);
    }else{
        //添加
        return this.add(cmsPage);
    }
}

二、頁面發佈方法apache

//一鍵發佈頁面
    public CmsPostPageResult postPageQuick(CmsPage cmsPage){
        //添加頁面
        CmsPageResult save = this.save(cmsPage);
        if(!save.isSuccess()){
            return new CmsPostPageResult(CommonCode.FAIL,null);
        }
        CmsPage cmsPage1 = save.getCmsPage();
        //要布的頁面id
        String pageId = cmsPage1.getPageId();
        //發佈頁面
        ResponseResult responseResult = this.postPage(pageId);
        if(!responseResult.isSuccess()){
            return new CmsPostPageResult(CommonCode.FAIL,null);
        }
        //獲得頁面的url
        //頁面url=站點域名+站點webpath+頁面webpath+頁面名稱
        //站點id
        String siteId = cmsPage1.getSiteId();
        //查詢站點信息
        CmsSite cmsSite = findCmsSiteById(siteId);
        //站點域名
        String siteDomain = cmsSite.getSiteDomain();
        //站點web路徑
        String siteWebPath = cmsSite.getSiteWebPath();
        //頁面web路徑
        String pageWebPath = cmsPage1.getPageWebPath();
        //頁面名稱
        String pageName = cmsPage1.getPageName();
        //頁面的web訪問地址
        String pageUrl = siteDomain+siteWebPath+pageWebPath+pageName;
        return new CmsPostPageResult(CommonCode.SUCCESS,pageUrl);
    }
//根據id查詢站點信息
    public CmsSite findCmsSiteById(String siteId){
        Optional<CmsSite> optional = cmsSiteRepository.findById(siteId);
        if(optional.isPresent()){
            return optional.get();
        }
        return null;
    }
View Code

Controller

@Override
@PostMapping("/postPageQuick")
public CmsPostPageResult postPageQuick(@RequestBody CmsPage cmsPage) {
    return pageService.postPageQuick(cmsPage);
}

課程發佈接口

Api接口

此Api接口由課程管理提供,由課程管理前端調用此Api接口,實現課程發佈。
在api工程下課程管理包下定義接口:json

@ApiOperation("發佈課程")
public CoursePublishResult publish(@PathVariable String id);

建立Feign Client

在課程管理工程建立CMS服務頁面發佈的Feign Clientapi

@FeignClient(value = XcServiceList.XC_SERVICE_MANAGE_CMS)
public interface CmsPageClient {
    //一鍵發佈頁面
    @PostMapping("/cms/page/postPageQuick")
    public CmsPostPageResult postPageQuick(CmsPage cmsPage);
}

Service

一、配置課程發佈頁面參數
在application.yml中配置數組

course‐publish:
  siteId: 5b30cba5f58b4411fc6cb1e5
  templateId: 5ad9a24d68db5239b8fef199
  previewUrl: http://www.xuecheng.com/cms/preview/
  pageWebPath: /course/detail/
  pagePhysicalPath: /course/detail/
  dataUrlPre: http://localhost:31200/course/courseview/

siteId:站點id
templateId:模板id
dataurlPre:數據url的前綴
pageWebPath: 頁面的web訪問路徑
pagePhysicalPath:頁面的物理存儲路徑。

二、Service方法以下

@Value("${course‐publish.dataUrlPre}")
private String publish_dataUrlPre;
@Value("${course‐publish.pagePhysicalPath}")
private String publish_page_physicalpath;
@Value("${course‐publish.pageWebPath}")
private String publish_page_webpath;
@Value("${course‐publish.siteId}")
private String publish_siteId;
@Value("${course‐publish.templateId}")
private String publish_templateId;
@Value("${course‐publish.previewUrl}")
private String previewUrl;
@Autowired
CmsPageClient cmsPageClient;
 //課程發佈
    @Transactional
    public CoursePublishResult publish(String courseId){
        //課程信息
        CourseBase one = this.findCourseBaseById(courseId);
        //發佈課程詳情頁面
        CmsPostPageResult cmsPostPageResult = publish_page(courseId);
        if(!cmsPostPageResult.isSuccess()){
            ExceptionCast.cast(CommonCode.FAIL);
}
        //更新課程狀態
        CourseBase courseBase = saveCoursePubState(courseId);
        //課程索引...
        //課程緩存...
        //頁面url
        String pageUrl = cmsPostPageResult.getPageUrl();
        return new CoursePublishResult(CommonCode.SUCCESS,pageUrl);
    }
    //更新課程發佈狀態
    private CourseBase saveCoursePubState(String courseId){
        CourseBase courseBase this.findCourseBaseById(courseId);
        //更新發布狀態
        courseBase.setStatus("202002");
        CourseBase save = courseBaseRepository.save(courseBase);
        return save;
    }
    //發佈課程正式頁面
    public CmsPostPageResult publish_page(String courseId){
        CourseBase one this.findCourseBaseById(courseId);
        //發佈課程預覽頁面
        CmsPage cmsPage = new CmsPage();
        //站點
        cmsPage.setSiteId(publish_siteId);//課程預覽站點
        //模板
        cmsPage.setTemplateId(publish_templateId);
        //頁面名稱
        cmsPage.setPageName(courseId+".html");
        //頁面別名
        cmsPage.setPageAliase(one.getName());
        //頁面訪問路徑
        cmsPage.setPageWebPath(publish_page_webpath);
        //頁面存儲路徑
        cmsPage.setPagePhysicalPath(publish_page_physicalpath);
        //數據url
        cmsPage.setDataUrl(publish_dataUrlPre+courseId);
        //發佈頁面
        CmsPostPageResult cmsPostPageResult = cmsPageClient.postPageQuick(cmsPage);
        return cmsPostPageResult;
    }
View Code

Controller

@Override
    @PostMapping("/publish/{id}")
    public CoursePublishResult publish(@PathVariable String id) {
        return courseService.publish(id);
    }

新增站點和模板

一、新增課程詳情頁面的站點信息
若是已增長課程詳情頁面的站點則忽略此步驟。
向cms_site中新增以下信息

{
    "_id" : ObjectId("5b30b052f58b4411fc6cb1cf"),
    "_class" : "com.xuecheng.framework.domain.cms.CmsSite",
    "siteName" : "課程詳情站點",
    "siteDomain" : "http://www.xuecheng.com",
    "sitePort" : "80",
    "siteWebPath" : "",
    "siteCreateTime" : ISODate("2018‐02‐03T02:34:19.113+0000")
}

二、新增課程詳情模板信息
可直接使用前邊章節制做的課程詳情信息模板。
能夠GridFS的測試代碼添加模板,若是已添加則不用重複添加。
使用測試GridFS Api將模板文件存儲到mongodb:

//文件存儲2
@Test
public void testStore2() throws FileNotFoundException {
    File file new File("C:\\Users\\admin\\Desktop\\coursedetail_t.html");
    FileInputStream inputStream new FileInputStream(file);
    //保存模版文件內容
    GridFSFile gridFSFile = gridFsTemplate.store(inputStream, "測試文件","");
    String fileId = gridFSFile.getId().toString();
    System.out.println(fileId);
}

單元測試

一、啓動RabbitMQ服務
二、啓動cms服務
三、啓動cms_client,注意配置routingKey和隊列名稱

xuecheng:
  mq:
  #cms客戶端監控的隊列名稱(不一樣的客戶端監控的隊列不能重複)
    queue: queue_cms_postpage_03
    routingKey: 5b30b052f58b4411fc6cb1cf  #此routingKey爲門戶站點ID

 

 

 全文檢索 Elasticearch 研究

ElasticSearch 介紹

一、elasticsearch是一個基於Lucene的高擴展的分佈式搜索服務器,支持開箱即用。
二、elasticsearch隱藏了Lucene的複雜性,對外提供Restful 接口來操做索引、搜索。

突出優勢:

1. 擴展性好,可部署上百臺服務器集羣,處理PB級數據。
2.近實時的去索引數據、搜索數據。

es和solr選擇哪一個?
1.若是你公司如今用的solr能夠知足需求就不要換了。
2.若是你公司準備進行全文檢索項目的開發,建議優先考慮elasticsearch,由於像Github這樣大規模的搜索都在用
它。

es在項目中的應用方式:

1)用戶在前端搜索關鍵字
2)項目前端經過http方式請求項目服務端
3)項目服務端經過Http RESTful方式請求ES集羣進行搜索
4)ES集羣從索引庫檢索數據。

ES 快速入門

建立索引庫

ES的索引庫是一個邏輯概念,它包括了分詞列表及文檔列表,同一個索引庫中存儲了相同類型的文檔。它就至關於
MySQL中的表,或至關於Mongodb中的集合。
關於索引這個語:
索引(名詞):ES是基於Lucene構建的一個搜索服務,它要從索引庫搜索符合條件索引數據。
索引(動詞):索引庫剛建立起來是空的,將數據添加到索引庫的過程稱爲索引。

1)使用postman或curl這樣的工具建立:

put http://localhost:9200/索引庫名稱

{
  "settings":{
  "index":{
      "number_of_shards":1,
      "number_of_replicas":0
   }    
  }
}

number_of_shards:設置分片的數量,在集羣中一般設置多個分片,表示一個索引庫將拆分紅多片分別存儲不一樣
的結點,提升了ES的處理能力和高可用性,入門程序使用單機環境,這裏設置爲1。
number_of_replicas:設置副本的數量,設置副本是爲了提升ES的高可靠性,單機環境設置爲0.

 2)使用head插件建立

 建立映射

在索引中每一個文檔都包括了一個或多個field,建立映射就是向索引庫中建立field的過程,下邊是document和field
與關係數據庫的概念的類比:
文檔(Document)----------------Row記錄
字段(Field)-------------------Columns 列

咱們要把課程信息存儲到ES中,這裏咱們建立課程信息的映射,先來一個簡單的映射,以下:
發送:post http://localhost:9200/索引庫名稱 /類型名稱/_mapping
建立類型爲xc_course的映射,共包括三個字段:name、description、studymondel
因爲ES6.0版本尚未將type完全刪除,因此暫時把type起一個沒有特殊意義的名字。
post 請求:http://localhost:9200/xc_course/doc/_mapping
表示:在 xc_course索引庫下的doc類型下建立映射。doc是類型名,能夠自定義,在ES6.0中要弱化類型的概念,
給它起一個沒有具體業務意義的名稱。

{
  "properties": {   
           "name": {
              "type": "text"
           },
           "description": {
              "type": "text"
           },
           "studymodel": {
              "type": "keyword"
           }
        }
}

映射建立成功,查看head界面:

  建立文檔

ES中的文檔至關於MySQL數據庫表中的記錄。
發送:put 或Post http://localhost:9200/xc_course/doc/id值
(若是不指定id值ES會自動生成ID)
http://localhost:9200/xc_course/doc/4028e58161bcf7f40161bcf8b77c0000

{
  "name":"Bootstrap開發框架",
  "description":"Bootstrap是由Twitter推出的一個前臺頁面開發框架,在行業之中使用較爲普遍。此開發框架包
含了大量的CSS、JS程序代碼,能夠幫助開發者(尤爲是不擅長頁面開發的程序人員)輕鬆的實現一個不受瀏覽器限制的
精美界面效果。",
  "studymodel":"201001"
}

使用postman測試:

 搜索文檔

一、根據課程id查詢文檔
發送:get http://localhost:9200/xc_course/doc/4028e58161bcf7f40161bcf8b77c0000
使用 postman測試:

 IK 分詞器

在添加文檔時會進行分詞,索引中存放的就是一個一個的詞(term),當你去搜索時就是拿關鍵字去匹配詞,最終
找到詞關聯的文檔。
測試當前索引庫使用的分詞器:
post 發送:localhost:9200/_analyze
{"text":"測試分詞器,後邊是測試內容:spring cloud實戰"}
結果以下:

會發現分詞的效果將 「測試」 這個詞拆分紅兩個單字「測」和「試」,這是由於當前索引庫使用的分詞器對中文就是單字
分詞。

兩種分詞模式

ik分詞器有兩種分詞模式:ik_max_word和ik_smart模式。
一、ik_max_word
會將文本作最細粒度的拆分,好比會將「中華人民共和國人民大會堂」拆分爲「中華人民共和國、中華人民、中華、
華人、人民共和國、人民、共和國、大會堂、大會、會堂等詞語。
二、ik_smart
會作最粗粒度的拆分,好比會將「中華人民共和國人民大會堂」拆分爲中華人民共和國、人民大會堂。
測試兩種分詞模式:

發送: post localhost:9200/_analyze
{"text":"中華人民共和國人民大會堂","analyzer":"ik_smart" }

 索引管理

搭建工程

ES客戶端

本教程準備採用 Java High Level REST Client,若是它有不支持的功能,則使用Java Low Level REST Client。
添加依賴:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch‐rest‐high‐level‐client</artifactId>
    <version>6.2.1</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>6.2.1</version>
</dependency>

建立搜索工程

建立搜索工程(maven工程):xc-service-search,添加RestHighLevelClient依賴及junit依賴。
pom.xml

<?xml version="1.0" encoding="UTF‐8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven‐4.0.0.xsd">
    <parent>
        <artifactId>xc‐framework‐parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>1.0‐SNAPSHOT</version>
        <relativePath>../xc‐framework‐parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>xc‐service‐search</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc‐framework‐model</artifactId>
            <version>1.0‐SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc‐framework‐common</artifactId>
            <version>1.0‐SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc‐service‐api</artifactId>
            <version>1.0‐SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring‐boot‐starter‐web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring‐boot‐starter‐web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch‐rest‐high‐level‐client</artifactId>
            <version>6.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>6.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring‐boot‐starter‐test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
 </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons‐io</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons‐lang3</artifactId>
        </dependency>
    </dependencies>
</project>
View Code

二、配置文件
application.yml

server:
  port: ${port:40100}
spring:
  application:
    name: xc‐search‐service
xuecheng:
  elasticsearch:
    hostlist: ${eshostlist:127.0.0.1:9200} #多個結點中間用逗號分隔

三、配置類
建立com.xuecheng.search.config包
在其下建立配置類

package com.xuecheng.search.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticsearchConfig {
    @Value("${xuecheng.elasticsearch.hostlist}")
    private String hostlist;
    @Bean
    public RestHighLevelClient restHighLevelClient(){
//解析hostlist配置信息
        String[] split = hostlist.split(",");
        //建立HttpHost數組,其中存放es主機和端口的配置信息
        HttpHost[] httpHostArray = new HttpHost[split.length];
        for(int i=0;i<split.length;i++){
            String item = split[i];
            httpHostArray[i] new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")
[1]), "http");
        }
        //建立RestHighLevelClient客戶端
        return new RestHighLevelClient(RestClient.builder(httpHostArray));
    }
//項目主要使用RestHighLevelClient,對於低級的客戶端暫時不用    
    @Bean
    public RestClient restClient(){
        //解析hostlist配置信息
        String[] split = hostlist.split(",");
        //建立HttpHost數組,其中存放es主機和端口的配置信息
        HttpHost[] httpHostArray = new HttpHost[split.length];
        for(int i=0;i<split.length;i++){
            String item = split[i];
            httpHostArray[i] new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")
[1]), "http");
        }
        return RestClient.builder(httpHostArray).build();
    }
}
View Code

四、啓動類

@SpringBootApplication
@EntityScan("com.xuecheng.framework.domain.search")//掃描實體類
@ComponentScan(basePackages={"com.xuecheng.api"})//掃描接口
@ComponentScan(basePackages={"com.xuecheng.search"})//掃描本項目下的全部類
@ComponentScan(basePackages={"com.xuecheng.framework"})//掃描common下的全部類
public class SearchApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(SearchApplication.class, args);
    }
}

 建立索引庫

Java Client

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestIndex {
    @Autowired
    RestHighLevelClient client;
    @Autowired
    RestClient restClient;
   //建立索引庫
    @Test
    public void testCreateIndex() throws IOException {
        //建立索引請求對象,並設置索引名稱
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("xc_course");
        //設置索引參數
        createIndexRequest.settings(Settings.builder().put("number_of_shards",1)
                                            .put("number_of_replicas",0));
        //設置映射
        createIndexRequest.mapping("doc"," {\n" +
                " \t\"properties\": {\n" +
                "           \"name\": {\n" +
                "              \"type\": \"text\",\n" +
                "              \"analyzer\":\"ik_max_word\",\n" +
                "              \"search_analyzer\":\"ik_smart\"\n" +
                "           },\n" +
                "           \"description\": {\n" +
                "              \"type\": \"text\",\n" +
                "              \"analyzer\":\"ik_max_word\",\n" +
                "              \"search_analyzer\":\"ik_smart\"\n" +
                "           },\n" +
                "           \"studymodel\": {\n" +
                "              \"type\": \"keyword\"\n" +
                "           },\n" +
                "           \"price\": {\n" +
                "              \"type\": \"float\"\n" +
                "           }\n" +
                "        }\n" +
                "}", XContentType.JSON);
        //建立索引操做客戶端
        IndicesClient indices = client.indices();
        //建立響應對象
        CreateIndexResponse createIndexResponse = indices.create(createIndexRequest);
        //獲得響應結果
        boolean acknowledged = createIndexResponse.isAcknowledged();
        System.out.println(acknowledged);
    }
    //刪除索引庫
    @Test
    public void testDeleteIndex() throws IOException {
        //刪除索引請求對象
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("xc_course");
        //刪除索引
        DeleteIndexResponse deleteIndexResponse = client.indices().delete(deleteIndexRequest);
        //刪除索引響應結果
        boolean acknowledged = deleteIndexResponse.isAcknowledged();
        System.out.println(acknowledged);
    }
}
View Code

 

添加文檔

//添加文檔
    @Test
    public void testAddDoc() throws IOException {
       //準備json數據
        Map<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("name", "spring cloud實戰");
        jsonMap.put("description", "本課程主要從四個章節進行講解: 1.微服務架構入門 2.spring cloud
基礎入門 3.實戰Spring Boot 4.註冊中心eureka。");
        jsonMap.put("studymodel", "201001");
        SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy‐MM‐dd HH:mm:ss");
        jsonMap.put("timestamp", dateFormat.format(new Date()));
        jsonMap.put("price", 5.6f);
        //索引請求對象
        IndexRequest indexRequest = new IndexRequest("xc_course","doc");
        //指定索引文檔內容
        indexRequest.source(jsonMap);
        //索引響應對象
        IndexResponse indexResponse = client.index(indexRequest);
//獲取響應結果
        DocWriteResponse.Result result = indexResponse.getResult();
        System.out.println(result);
    }
View Code

 

查詢文檔

//查詢文檔
@Test
public void getDoc() throws IOException {
    GetRequest getRequest new GetRequest(
            "xc_course",
            "doc",
            "4028e581617f945f01617f9dabc40000");
    GetResponse getResponse = client.get(getRequest);
    boolean exists = getResponse.isExists();
    Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
    System.out.println(sourceAsMap);
}

相關文章
相關標籤/搜索