課程發佈後將生成正式的課程詳情頁面,課程發佈後用戶便可瀏覽課程詳情頁面,並開始課程的學習。
課程發佈生成課程詳情頁面的流程與課程預覽業務流程相同,以下:
一、用戶進入教學管理中心,進入某個課程的管理界面
二、點擊課程發佈,前端請求到課程管理服務
三、課程管理服務遠程調用CMS生成課程發佈頁面,CMS將課程詳情頁面發佈到服務器
四、課程管理服務修改課程發佈狀態爲 「已發佈」,並向前端返回發佈成功
五、用戶在教學管理中心點擊「課程詳情頁面」連接,查看課程詳情頁面內容html
根據需求分析內容,須要在 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
接口中須要獲取站點的信息(站點域名、站點訪問路徑等mongodb
public interface CmsSiteRepository extends MongoRepository<CmsSite,String> { }
一、添加頁面,若是已存在則更新頁面數據庫
//添加頁面,若是已存在則更新頁面 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; }
@Override @PostMapping("/postPageQuick") public CmsPostPageResult postPageQuick(@RequestBody CmsPage cmsPage) { return pageService.postPageQuick(cmsPage); }
此Api接口由課程管理提供,由課程管理前端調用此Api接口,實現課程發佈。
在api工程下課程管理包下定義接口:json
@ApiOperation("發佈課程") public CoursePublishResult publish(@PathVariable String id);
在課程管理工程建立CMS服務頁面發佈的Feign Clientapi
@FeignClient(value = XcServiceList.XC_SERVICE_MANAGE_CMS) public interface CmsPageClient { //一鍵發佈頁面 @PostMapping("/cms/page/postPageQuick") public CmsPostPageResult postPageQuick(CmsPage cmsPage); }
一、配置課程發佈頁面參數
在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; }
@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
一、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的索引庫是一個邏輯概念,它包括了分詞列表及文檔列表,同一個索引庫中存儲了相同類型的文檔。它就至關於
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測試:
在添加文檔時會進行分詞,索引中存放的就是一個一個的詞(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" }
本教程準備採用 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>
二、配置文件
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(); } }
四、啓動類
@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); } }
@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); } }
//添加文檔 @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); }
//查詢文檔 @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); }