文章目錄
- 一.頁面靜態化流程
- 二.數據模型
- 1.輪播圖DataUrl接口
- 1)需求分析
- 2)接口定義
- 3)Dao
- 4)Service
- 5)Controller
- 6)測試
- 2.遠程請求接口
- 1)添加依賴
- 2)配置RestTemplate
- 3)測試RestTemplate
- 三.模板管理
- 1.模板管理業務流程
- 2.模板制作
- 1)編寫模板文件
- 2)模板測試
- 3.GridFS研究
- 1)
- 2)
- 4.模板存儲
- 1)
- 2)
- 四.靜態化測試
- 1.填寫頁面DataUrl
- 1)修改頁面管理前端的page_edit.vue
- 2)修改頁面管理服務端PageService
- 2.靜態化程序
一.頁面靜態化流程
頁面靜態化需要準備數據模型和模板,先知道數據模型的結構才可以編寫模板,因為在模板中要引用數據模型中的數據,本節將系統講解CMS頁面數據模型獲取、模板管理及靜態化的過程。
下邊討論一個問題:如何獲取頁面的數據模型?
CMS管理了各種頁面,CMS對頁面進行靜態化時需要數據模型,但是CMS并不知道每個頁面的數據模型的具體內
容,它只管執行靜態化程序便可對頁面進行靜態化,所以CMS靜態化程序需要通過一種通用的方法來獲取數據模
型。
在編輯頁面信息時指定一個DataUrl,此DataUrl便是獲取數據模型的Url,它基于Http方式,CMS對頁面進行靜態
化時會從頁面信息中讀取DataUrl,通過Http遠程調用的方法請求DataUrl獲取數據模型。
管理員怎么知道DataUrl的內容呢?舉例說明:
此頁面是輪播圖頁面,它的DataUrl由開發輪播圖管理的程序員提供。
此頁面是精品課程推薦頁面,它的DataUrl由精品課程推薦的程序員提供。
此頁面是課程詳情頁面,它的DataUrl由課程管理的程序員提供。
頁面靜態化流程如下圖:
1、靜態化程序首先讀取頁面獲取DataUrl。
2、靜態化程序遠程請求DataUrl得到數據模型。
3、獲取頁面模板。
4、執行頁面靜態化。
二.數據模型
1.輪播圖DataUrl接口
1)需求分析
CMS中有輪播圖管理、精品課程推薦的功能,以輪播圖管理為例說明:輪播圖管理是通過可視化的操作界面由管理員指定輪播圖圖片地址,最后將輪播圖圖片地址保存在cms_config集合中,下邊是輪播圖數據模型:
針對首頁的輪播圖信息、精品推薦等信息的獲取統一提供一個Url供靜態化程序調用,這樣我們就知道了輪播圖頁面、精品課程推薦頁面的DataUrl,管理員在頁面配置中將此Url配置在頁面信息中。
本小節開發一個查詢輪播圖、精品推薦信息的接口,此接口供靜態化程序調用獲取數據模型。
2)接口定義
輪播圖信息、精品推薦等信息存儲在MongoDB的cms_config集合中
cms_config有固定的數據結構,如下:
文件位置:xcEduService01\xc-framework-model\src\main\java\com\xuecheng\framework\domain\cms\CmsConfig.java
package com.xuecheng.framework.domain.cms;import lombok.Data;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;import java.util.List;@Data
@ToString
@Document(collection = "cms_config")
public class CmsConfig {@Idprivate String id;private String name;private List<CmsConfigModel> model;}
數據模型項目內容如下:
文件位置:xcEduService01\xc-framework-model\src\main\java\com\xuecheng\framework\domain\cms\CmsConfigModel.java
package com.xuecheng.framework.domain.cms;import lombok.Data;
import lombok.ToString;import java.util.Map;@Data
@ToString
public class CmsConfigModel {private String key;private String name;private String url;private Map mapValue;private String value;
}
上邊的模型結構可以對照cms_config中的數據進行分析。其中,在mapValue 中可以存儲一些復雜的數據模型內容。
根據配置信息Id查詢配置信息,定義接口如下:
文件位置xcEduService01\xc-service-api\src\main\java\com\xuecheng\api\cms\CmsConfigControllerApi.java
package com.xuecheng.api.cms;import com.xuecheng.framework.domain.cms.CmsConfig;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;@Api(value="cms配置管理接口",description = "cms配置管理接口,提供數據模型的管理、查詢接口")
public interface CmsConfigControllerApi {@ApiOperation("根據id查詢CMS配置信息")public CmsConfig getmodel(String id);
}
3)Dao
定義CmsConfig的dao接口:
文件位置:xcEduService01\xc-service-manage-cms\src\main\java\com\xuecheng\manage_cms\dao\CmsConfigRepository.java
package com.xuecheng.manage_cms.dao;import com.xuecheng.framework.domain.cms.CmsConfig;
import org.springframework.data.mongodb.repository.MongoRepository;public interface CmsConfigRepository extends MongoRepository<CmsConfig,String> {
}
4)Service
定義Service實現根據id查詢CmsConfig信息:
文件位置:xcEduService01\xc-service-manage-cms\src\main\java\com\xuecheng\manage_cms\service\PageService.java
//根據id查詢cmsConfigpublic CmsConfig getConfigById(String id){Optional<CmsConfig> optional = cmsConfigRepository.findById(id);if(optional.isPresent()){CmsConfig cmsConfig = optional.get();return cmsConfig;}return null;}
5)Controller
文件位置:xcEduService01\xc-service-manage-cms\src\main\java\com\xuecheng\manage_cms\controller\CmsConfigController.java
package com.xuecheng.manage_cms.controller;import com.xuecheng.api.cms.CmsConfigControllerApi;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.manage_cms.service.PageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/cms/config")
public class CmsConfigController implements CmsConfigControllerApi {@AutowiredPageService pageService;@Override@GetMapping("/getmodel/{id}")public CmsConfig getmodel(@PathVariable("id") String id) {return pageService.getConfigById(id);}
}
6)測試
使用postman測試接口:
get請求:http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f (輪播圖信息)
2.遠程請求接口
SpringMVC提供 RestTemplate請求http接口,RestTemplate的底層可以使用第三方的http客戶端工具實現http 的
請求,常用的http客戶端工具有Apache HttpClient、OkHttpClient等,本項目使用OkHttpClient完成http請求,
原因也是因為它的性能比較出眾。
1)添加依賴
文件位置:xcEduService01\xc-service-manage-cms\pom.xml
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId>
</dependency
2)配置RestTemplate
在SpringBoot啟動類中配置 RestTemplate
文件位置:xcEduService01\xc-service-manage-cms\src\main\java\com\xuecheng\manage_cms\ManageCmsApplication.java
...
public class ManageCmsApplication {
public static void main(String[] args) {
SpringApplication.run(ManageCmsApplication.class,args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
3)測試RestTemplate
根據url獲取數據,并轉為map格式
文件位置:xcEduService01\xc-service-manage-cms\src\test\java\com\xuecheng\manage_cms\RestTemplateTest.java
@Test
public void testRestTemplate(){
ResponseEntity<Map> forEntity =
restTemplate.getForEntity("http://localhost:31001/cms/config/get/5a791725dd573c3574ee333f", Map.class);
System.out.println(forEntity);
}
三.模板管理
1.模板管理業務流程
CMS提供模板管理功能,業務流程如下:
1)要增加新模板首先需要制作模板,模板的內容就是Freemarker ftl模板內容。
2)通過模板管理模塊功能新增模板、修改模板、刪除模板。
3)模板信息存儲在MongoDB數據庫,其中模板信息存儲在cms_template集合中,模板文件存儲在GridFS文件系
統中。
cms_template集合:
下邊是一個模板的例子,其中templateFileId是模板文件的ID,此ID對應GridFS文件系統中文件ID。
2.模板制作
1)編寫模板文件
1、輪播圖頁面原型
在門戶的靜態工程目錄有輪播圖的靜態頁面,路徑是:/include/index_banner.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="/plugins/normalize-css/normalize.css" /><link rel="stylesheet" href="/plugins/bootstrap/dist/css/bootstrap.css" /><link rel="stylesheet" href="/css/page-learing-index.css" /><link rel="stylesheet" href="/css/page-header.css" />
</head>
<body>
<div class="banner-roll"><div class="banner-item"><div class="item" style="background-image: url(../img/widget-bannerB.jpg);"></div><div class="item" style="background-image: url(../img/widget-bannerA.jpg);"></div><div class="item" style="background-image: url(../img/widget-banner3.png);"></div><div class="item" style="background-image: url(../img/widget-bannerB.jpg);"></div><div class="item" style="background-image: url(../img/widget-bannerA.jpg);"></div><div class="item" style="background-image: url(../img/widget-banner3.png);"></div></div><div class="indicators"></div>
</div>
<script type="text/javascript" src="/plugins/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="/plugins/bootstrap/dist/js/bootstrap.js"></script>
<script type="text/javascript">var tg = $('.banner-item .item');var num = 0;for (i = 0; i < tg.length; i++) {$('.indicators').append('<span></span>');$('.indicators').find('span').eq(num).addClass('active');}function roll() {tg.eq(num).animate({'opacity': '1','z-index': num}, 1000).siblings().animate({'opacity': '0','z-index': 0}, 1000);$('.indicators').find('span').eq(num).addClass('active').siblings().removeClass('active');if (num >= tg.length - 1) {num = 0;} else {num++;}}$('.indicators').find('span').click(function() {num = $(this).index();roll();});var timer = setInterval(roll, 3000);$('.banner-item').mouseover(function() {clearInterval(timer)});$('.banner-item').mouseout(function() {timer = setInterval(roll, 3000)});
</script>
</body>
</html>
2、數據模型為:
通過http 獲取到數據模型如下:
下圖數據模型的圖片路徑改成可以瀏覽的正確路徑。
{ "_id" : ObjectId("5a791725dd573c3574ee333f"), "_class" : "com.xuecheng.framework.domain.cms.CmsConfig", "name" : "輪播圖", "model" : [{"key" : "banner1", "name" : "輪播圖1地址", "value" : "http://192.168.101.64/group1/M00/00/01/wKhlQFp5wnCAG-kAAATMXxpSaMg864.png"}, {"key" : "banner2", "name" : "輪播圖2地址", "value" : "http://192.168.101.64/group1/M00/00/01/wKhlQVp5wqyALcrGAAGUeHA3nvU867.jpg"}, {"key" : "banner3", "name" : "輪播圖3地址", "value" : "http://192.168.101.64/group1/M00/00/01/wKhlQFp5wtWAWNY2AAIkOHlpWcs395.jpg"}]
}
3、編寫模板
在freemarker測試工程中新建模板index_banner.ftl(復制index_banner.html并修改)。
文件位置:xcEduService01\test-freemarker\src\main\resources\templates\index_banner.ftl
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="http://www.xuecheng.com/plugins/normalize-css/normalize.css" /><link rel="stylesheet" href="http://www.xuecheng.com/plugins/bootstrap/dist/css/bootstrap.css" /><link rel="stylesheet" href="http://www.xuecheng.com/css/page-learing-index.css" /><link rel="stylesheet" href="http://www.xuecheng.com/css/page-header.css" />
</head>
<body>
<div class="banner-roll"><div class="banner-item"><#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerB.jpg);"></div><div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerA.jpg);"></div><div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-banner3.png);"></div><div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerB.jpg);"></div><div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerA.jpg);"></div><div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-banner3.png);"></div>--><#if model??><#list model as item><div class="item" style="background-image: url(${item.value});"></div></#list></#if></div><div class="indicators"></div>
</div>
<script type="text/javascript" src="http://www.xuecheng.com/plugins/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="http://www.xuecheng.com/plugins/bootstrap/dist/js/bootstrap.js"></script>
<script type="text/javascript">var tg = $('.banner-item .item');var num = 0;for (i = 0; i < tg.length; i++) {$('.indicators').append('<span></span>');$('.indicators').find('span').eq(num).addClass('active');}function roll() {tg.eq(num).animate({'opacity': '1','z-index': num}, 1000).siblings().animate({'opacity': '0','z-index': 0}, 1000);$('.indicators').find('span').eq(num).addClass('active').siblings().removeClass('active');if (num >= tg.length - 1) {num = 0;} else {num++;}}$('.indicators').find('span').click(function() {num = $(this).index();roll();});var timer = setInterval(roll, 3000);$('.banner-item').mouseover(function() {clearInterval(timer)});$('.banner-item').mouseout(function() {timer = setInterval(roll, 3000)});
</script>
</body>
</html>
2)模板測試
在freemarker測試工程編寫一個方法測試輪播圖模板,代碼如下:
文件位置:xcEduService01\test-freemarker\src\main\java\com\xuecheng\test\freemarker\controller\FreemarkerController.java
@Autowired
RestTemplate restTemplate;@RequestMapping("/banner")
public String index_banner(Map<String, Object> map){
String dataUrl = "http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f";
ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
Map body = forEntity.getBody();
map.putAll(body);
return "index_banner";
}
請求:http://localhost:8088/freemarker/banner
3.GridFS研究
1)
2)
4.模板存儲
1)
2)
四.靜態化測試
上邊章節完成了數據模型和模板管理的測試,下邊測試整個頁面靜態化的流程,流程如下:
1)填寫頁面DataUrl
在編輯cms頁面信息界面page_edit.vue中添加DataUrl,并將此字段保存到cms_page集合中。
2)靜態化程序獲取頁面的DataUrl
3)靜態化程序遠程請求DataUrl獲取數據模型。
4)靜態化程序獲取頁面的模板信息
5)執行頁面靜態化
1.填寫頁面DataUrl
修改頁面管理模板代碼,實現編輯頁面DataUrl。
注意:此地址由程序員提供給系統管理員,由系統管理員錄入到系統中。
下邊實現頁面修改界面錄入DataUrl:
1)修改頁面管理前端的page_edit.vue
首先來看一下cms_page的模型代碼:
文件位置:\xcEduService01\xc-framework-model\src\main\java\com\xuecheng\framework\domain\cms\CmsPage.java
package com.xuecheng.framework.domain.cms;import lombok.Data;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;import java.util.Date;
import java.util.List;@Data
@ToString
@Document(collection = "cms_page")
public class CmsPage {/*** 頁面名稱、別名、訪問地址、類型(靜態/動態)、頁面模版、狀態*///站點IDprivate String siteId;//頁面ID@Idprivate String pageId;//頁面名稱private String pageName;//別名private String pageAliase;//訪問地址private String pageWebPath;//參數private String pageParameter;//物理路徑private String pagePhysicalPath;//類型(靜態/動態)private String pageType;//頁面模版private String pageTemplate;//頁面靜態化內容private String pageHtml;//狀態private String pageStatus;//創建時間private Date pageCreateTime;//模版idprivate String templateId;//參數列表private List<CmsPageParam> pageParams;//模版文件Idprivate String templateFileId;//靜態文件Idprivate String htmlFileId;//數據Urlprivate String dataUrl;}
接下來,在首頁表單中添加dataUrl輸入框:
文件位置:xc-ui-pc-sysmanage\src\module\cms\page\page_edit.vue
<el‐form‐item label="數據Url" prop="dataUrl">
<el‐input v‐model="pageForm.dataUrl" auto‐complete="off" ></el‐input>
</el‐form‐item>
2)修改頁面管理服務端PageService
在更新cmsPage數據代碼中添加:
//更新dataUrl
one.setDataUrl(cmsPage.getDataUrl()
添加后如下:
文件位置:xcEduService01\xc-service-manage-cms\src\main\java\com\xuecheng\manage_cms\service\PageService.java
public CmsPageResult update(String id,CmsPage cmsPage){//根據id從數據庫查詢頁面信息CmsPage one = this.getById(id);if(one!=null){//準備更新數據//設置要修改的數據//更新模板idone.setTemplateId(cmsPage.getTemplateId());//更新所屬站點one.setSiteId(cmsPage.getSiteId());//更新頁面別名one.setPageAliase(cmsPage.getPageAliase());//更新頁面名稱one.setPageName(cmsPage.getPageName());//更新訪問路徑one.setPageWebPath(cmsPage.getPageWebPath());//更新物理路徑one.setPagePhysicalPath(cmsPage.getPagePhysicalPath());//更新dataUrlone.setDataUrl(cmsPage.getDataUrl());//提交修改cmsPageRepository.save(one);return new CmsPageResult(CommonCode.SUCCESS,one);}//修改失敗return new CmsPageResult(CommonCode.FAIL,null);}
2.靜態化程序
在PageService中定義頁面靜態化方法,如下:
文件位置:xcEduService01\xc-service-manage-cms\src\main\java\com\xuecheng\manage_cms\service\PageService.java
//頁面靜態化方法/*** 靜態化程序獲取頁面的DataUrl** 靜態化程序遠程請求DataUrl獲取數據模型。** 靜態化程序獲取頁面的模板信息** 執行頁面靜態化*/public String getPageHtml(String pageId){//獲取數據模型Map model = getModelByPageId(pageId);if(model == null){//數據模型獲取不到ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);}//獲取頁面的模板信息String template = getTemplateByPageId(pageId);if(StringUtils.isEmpty(template)){ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);}//執行靜態化String html = generateHtml(template, model);return html;}//執行靜態化private String generateHtml(String templateContent,Map model ){//創建配置對象Configuration configuration = new Configuration(Configuration.getVersion());//創建模板加載器StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();stringTemplateLoader.putTemplate("template",templateContent);//向configuration配置模板加載器configuration.setTemplateLoader(stringTemplateLoader);//獲取模板try {Template template = configuration.getTemplate("template");//調用api進行靜態化String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);return content;} catch (Exception e) {e.printStackTrace();}return null;}//獲取頁面的模板信息private String getTemplateByPageId(String pageId){//取出頁面的信息CmsPage cmsPage = this.getById(pageId);if(cmsPage == null){//頁面不存在ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);}//獲取頁面的模板idString templateId = cmsPage.getTemplateId();if(StringUtils.isEmpty(templateId)){ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);}//查詢模板信息Optional<CmsTemplate> optional = cmsTemplateRepository.findById(templateId);if(optional.isPresent()){CmsTemplate cmsTemplate = optional.get();//獲取模板文件idString templateFileId = cmsTemplate.getTemplateFileId();//從GridFS中取模板文件內容//根據文件id查詢文件GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));//打開一個下載流對象GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());//創建GridFsResource對象,獲取流GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);//從流中取數據try {String content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");return content;} catch (IOException e) {e.printStackTrace();}}return null;}//獲取數據模型private Map getModelByPageId(String pageId){//取出頁面的信息CmsPage cmsPage = this.getById(pageId);if(cmsPage == null){//頁面不存在ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);}//取出頁面的dataUrlString dataUrl = cmsPage.getDataUrl();if(StringUtils.isEmpty(dataUrl)){//頁面dataUrl為空ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);}//通過restTemplate請求dataUrl獲取數據ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);Map body = forEntity.getBody();return body;}
單元測試getPageHtml方法,過程略。