練手項目2筆記之day04

1. 頁面靜態化需求

1. 爲什麼對頁面進行管理

本項目對cms的定位是對頁面進行管理,對那些由於經營需要(首頁的廣告、門戶頁面樣式)而需要快速上線的功能頁面進行管理

2. 如何進行頁面管理

通過人工開發來編寫HTML頁面可以實現,如果採用cms系統就可以實現由cms自動化對頁面進行更新,自動採用靜態化的技術生成HTML頁面,快速上線。

3. 如何對頁面進行靜態化

採用頁面模板+數據=輸出HTML頁面的技術實現靜態化

4. 靜態化的HTML頁面存放哪裏

生成的靜態化的頁面,由cms程序自動發佈到服務器(門戶服務器、其他)中,實現頁面的快速上線。

dya0401

2. 頁面靜態化

1. 頁面靜態化流程

模板+數據模型=輸出。如何獲取頁面的數據模型?

CMS管理了各種頁面,CMS對頁面進行靜態化時需要數據模型,但是CMS並不知道每個頁面的數據模型的具體內
容,它只管執行靜態化程序便可對頁面進行靜態化,所以CMS靜態化程序需要通過一種通用的方法來獲取數據模

在編輯頁面信息時指定一個DataUrl,此DataUrl便是獲取數據模型的Url,它基於Http方式,CMS對頁面進行靜態
化時會從頁面信息中讀取DataUrl,通過Http遠程調用的方法請求DataUrl獲取數據模型

管理員怎麼知道DataUrl的內容呢?

輪播圖頁面,它的DataUrl由開發輪播圖管理的程序員提供 ;精品課程推薦頁面,它的DataUrl由精品課程推薦的程序員提供;課程詳情頁面,它的DataUrl由課程管理的程序員提供。

頁面靜態化流程如下圖:

dya0402

  1. 靜態化程序首先讀取頁面獲取DataUrl。
  2. 靜態化程序遠程請求DataUrl得到數據模型。
  3. 獲取頁面模板。
  4. 執行頁面靜態化。

2. 數據模型

1. 輪播圖DataUrl接口

1. 需求分析

CMS中有輪播圖管理、精品課程推薦的功能,以輪播圖管理爲例說明:輪播圖管理是通過可視化的操作界面由管理
員指定輪播圖圖片地址,最後將輪播圖圖片地址保存在cms_config集合中

針對首頁的輪播圖信息、精品推薦等信息的獲取統一提供一個Url供靜態化程序調用,這樣我們就知道了輪播圖頁
面、精品課程推薦頁面的DataUrl,管理在頁面配置中將此Url配置在頁面信息中

本小節開發一個查詢輪播圖、精品推薦信息的接口,此接口供靜態化程序調用獲取數據模型。

2. 接口定義

輪播圖信息、精品推薦等信息存儲在MongoDB的cms_config集合中

cms_config有固定的數據結構,如下:

@Data
@ToString
@Document(collection = "cms_config")
public class CmsConfig {
  @Id
  private String id;//主鍵
  private String name;//數據模型的名稱
  private List<CmsConfigModel> model;//數據模型項目
}

數據模型項目內容如下:

@Data
@ToString
public class CmsConfigModel {
  private String key;//主鍵
  private String name;//項目名稱
  private String url;//項目url
  private Map mapValue;//項目複雜值
  private String value;//項目簡單值
}

上邊的模型結構可以對照cms_config中的數據進行分析。

其中,在mapValue 中可以存儲一些複雜的數據模型內容

根據配置信息Id查詢配置信息,定義接口如下:

@Api(value="cms配置管理接口",description = "cms配置管理接口,提供數據模型的管理、查詢接口")
public interface CmsConfigControllerApi {
  @ApiOperation("根據id查詢CMS配置信息")
  public CmsConfig getmodel(String id);
}
3. dao

定義CmsConfig的dao接口

public interface CmsConfigRepository extends MongoRepository<CmsConfig,String> {
}
4. service

定義CmsConfigService實現根據id查詢CmsConfig信息

@Service
public class CmsConfigService {
  @Autowired
  CmsConfigRepository cmsConfigRepository;
  //根據id查詢配置管理信息
  public CmsConfig getConfigById(String id){
    Optional<CmsConfig> optional = cmsConfigRepository.findById(id);
    if(optional.isPresent()){
      CmsConfig cmsConfig = optional.get();
      return cmsConfig;
    } r
      eturn null;
  }
}
5. Controller
@RestController
@RequestMapping("/cms/config")
public class CmsConfigController implements CmsConfigControllerApi {
  @Autowired
  CmsConfigService cmsConfigService;
  @Override
  @GetMapping("/getmodel/{id}")
  public CmsConfig getmodel(@PathVariable("id") String id) {
    return cmsConfigService.getConfigById(id);
  }
}
6. 測試

3. 遠程請求接口

SpringMVC提供 RestTemplate請求http接口,RestTemplate的底層可以使用第三方的http客戶端工具實現http 的
請求,常用的http客戶端工具有Apache HttpClient、OkHttpClient等,本項目使用OkHttpClient完成http請求

  1. 添加依賴
  2. 配置RestTemplate 在SpringBoot啓動類中配置RestTemplate
@SpringBootApplication
@EntityScan("com.xuecheng.framework.domain.cms")//掃描實體類
@ComponentScan(basePackages = {"com.xuecheng.api"})//掃描接口
@ComponentScan(basePackages = {"com.xuecheng.manage_cms"})//掃描自己的
@ComponentScan(basePackages = {"com.xuecheng.framework"})//掃描common工程的類
public class ManageCmsApplication {
  public static void main(String[] args) {
    SpringApplication.run(ManageCmsApplication.class,args);
  }

  @Bean
  public RestTemplate restTemplate(){
    return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
  }
}
  1. 測試RestTemplate

根據url獲取數據,並轉換爲map格式

@SpringBootTest
@RunWith(SpringRunner.class)
public class RestTemplateTest {
  @Autowired
  private RestTemplate restTemplate;

  @Test
  public void testRestTemplate(){
    ResponseEntity<Map> forEntity = restTemplate.getForEntity("http://localhost:31001/getmodel/5a791725dd573c3574ee333f", Map.class);
    Map body = forEntity.getBody();
    System.out.println(body);
  }
}

3. 模板管理

1. 模板管理業務流程

cms提供模板管理功能,業務流程如下

dya0403

  1. 要增加新模板首先需要製作模板,模板的內容就是Freemarker ftl模板內容。
  2. 通過模板管理模塊功能新增模板、修改模板、刪除模板
  3. 模板信息存儲在MongoDB數據庫,其中模板信息存儲在cms_template集合中,模板文件存儲在GridFS文件系
    統中

下邊是一個模板的例子

{
  "_id" : ObjectId("5a962b52b00ffc514038faf7"),
  "_class" : "com.xuecheng.framework.domain.cms.CmsTemplate",
  "siteId" : "5a751fab6abb5044e0d19ea1",
  "templateName" : "首頁",
  "templateParameter" : "",
  "templateFileId" : "5a962b52b00ffc514038faf5"
}

其中templateFileId是模板文件的ID,此ID對應GridFS文件系統中文件ID

2. 模板製作

1. 編寫模板文件
  1. 輪播圖頁面原型

在門戶的靜態工程目錄有輪播圖的靜態頁面,路徑是:/include/index_banner.html

  1. 數據模型通過http獲取數據,將圖片路徑改爲可以瀏覽的正確路徑
  2. 編寫模板 在freemarker測試工程中新建模板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 class="indicators"></div>
  <div>圖片文件損壞</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. 模板測試

編寫方法測試輪播圖模板

@Autowired
private RestTemplate restTemplate;

@RequestMapping("/banner")
public String index_banner(Map<String,Object> map){
  // 使用restTemplate請求輪播圖的模型數據
  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";
}

3. GridFS研究

1. 介紹

GridFS是MongoDB提供的用於持久化存儲文件的模塊,CMS使用MongoDB存儲數據,使用GridFS可以快速集成
開發

它的工作原理是: 在GridFS存儲文件是將文件分塊存儲,文件會按照256KB的大小分割成多個塊進行存儲,GridFS使用兩個集合(collection)存儲文件,一個集合是chunks, 用於存儲文件的二進制數據;一個集合是files,用於存儲文件的元數據信息(文件名稱、塊大小、上傳時間等信息)。

從GridFS中讀取文件要對文件的各各塊進行組裝、合併。

2. GridFS存取文件測試
@SpringBootTest
@RunWith(SpringRunner.class)
public class GridFSTest {

  @Autowired
  private GridFsTemplate gridFsTemplate;

  @Autowired
  private GridFSBucket gridFSBucket;

  // 存文件5cfcd43b5ed156408c2a03c0
  @Test
  public void testGridFSTemplate() throws FileNotFoundException {
    // 定義file
    File file = new File("e:/index_banner.ftl");
    // 定義fileInputStream
    FileInputStream fileInputStream = new FileInputStream(file);
    ObjectId objectId = gridFsTemplate.store(fileInputStream, "index_banner.ftl");
    System.out.println(objectId);
  }

  // 取文件
  @Test
  public void queryFile() throws IOException {
    // 根據文件id查詢文件
    GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is("5cfcd43b5ed156408c2a03c0")));
    // 打開下載流對象
    GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
    // 創建GridFSResource對象,獲取流
    GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
    // 從流中取數據
    String content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
    System.out.println(content);
  }
}
  1. 存文件

文件存儲成功得到一個文件id 此文件id是fs.files集合中的主鍵 可以通過文件id查詢fs.chunks表中的記錄,得到文件的內容。

  1. 讀取文件

在config包中定義Mongodb的配置類 GridFSBucket用於打開下載流對象

@Configuration
public class MongoConfig {
  @Value("${spring.data.mongodb.database}")
  String db;
  @Bean
  public GridFSBucket getGridFSBucket(MongoClient mongoClient){
    MongoDatabase database = mongoClient.getDatabase(db);
    GridFSBucket bucket = GridFSBuckets.create(database);
    return bucket;
  }
}

4. 模板存儲

根據模板管理的流程,最終將模板信息存儲到MongoDB的cms_template中,將模板文件存儲到GridFS中

手動向cms_template及GridFS中存儲模板,增刪改都是直接在Studio 3T 進行

4. 靜態化測試

1、填寫頁面DataUrl
在編輯cms頁面信息界面填寫DataUrl,將此字段保存到cms_page集合中。
2、靜態化程序獲取頁面的DataUrl
3、靜態化程序遠程請求DataUrl獲取數據模型。
4、靜態化程序獲取頁面的模板信息
5、執行頁面靜態化

1. 填寫頁面dataUrl

修改頁面管理模板代碼,實現編輯頁面DataUrl。 此地址由程序員提供給系統管理員

2. 靜態化程序

在pageService中定義頁面靜態化方法

/**
     *  頁面靜態化方法:
     *      靜態化程序獲取頁面的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 Map getModelByPageId(String pageId){
  // 取出頁面的信息
  CmsPage cmsPage = this.getById(pageId);
  if(cmsPage==null){
    // 頁面不存在
    ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
  }
  // 取出頁面的dataUrl
  String dataUrl = cmsPage.getDataUrl();
  if(StringUtils.isEmpty(dataUrl)){
    // 頁面dataUrl爲空
    ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
  }
  // 通過restTemplate請求dataUrl獲取數據
  ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
  Map body = forEntity.getBody();
  return body;
}

@Autowired
private CmsTemplateRepository cmsTemplateRepository;

@Autowired
private GridFsTemplate gridFsTemplate;

@Autowired
private GridFSBucket gridFSBucket;

// 獲取頁面模板信息
private String getTemplateByPageId(String pageId){
  // 取出頁面的信息
  CmsPage cmsPage = this.getById(pageId);
  if(cmsPage==null){
    // 頁面不存在
    ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
  }
  // 獲取頁面的模板id
  String 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();
    // 獲取模板文件id
    String templateFileId = cmsTemplate.getTemplateFileId();
    // 從GridFS中取模板文件內容
    // 根據文件id查詢文件
    GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is("5cfcd43b5ed156408c2a03c0")));
    // 打開下載流對象
    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 String generateHtml(String templateContent,Map model){
  // 創建配置對象
  Configuration configuration = new Configuration(Configuration.getVersion());
  // 使用模板加載器變爲模板
  StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
  stringTemplateLoader.putTemplate("template",templateContent);
  // 在配置中設置模板加載器
  configuration.setTemplateLoader(stringTemplateLoader);
  // 獲取模板內容
  try {
    Template template = configuration.getTemplate("template");
    // 調用api進行靜態化
    String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
    return content;
  } catch (IOException e) {
    e.printStackTrace();
  } catch (TemplateException e) {
    e.printStackTrace();
  }
  return null;
}

單元測試

4. 頁面預覽

1. 頁面預覽開發

1. 需求分析

頁面在發佈前增加頁面預覽的步驟,方便用戶檢查頁面內容是否正確。

dya0404

1、用戶進入cms前端,點擊“頁面預覽”在瀏覽器請求cms頁面預覽鏈接。
2、cms根據頁面id查詢DataUrl並遠程請求DataUrl獲取數據模型。
3、cms根據頁面id查詢頁面模板內容
4、cms執行頁面靜態化。

5、cms將靜態化內容響應給瀏覽器。
6、在瀏覽器展示頁面內容,實現頁面預覽的功能。

2. 搭建環境

在cms服務需要集成freemarker 在application.yml配置freemarker

server:
  port: 31001
spring:
  application:
    name: xc-service-manage-cms
  data:
    mongodb:
      uri: mongodb://root:123@localhost:27017
      database: xc_cms
  freemarker:
    cache: false # 關閉緩存,方便測試
    settings:
      template_update_delay: 0

3. service

靜態化方法已經實現

4. controller

調用service的靜態化方法,將靜態化內容通過response輸出到瀏覽器顯示

創建CmsPagePreviewController類,用於頁面預覽

請求頁面id,查詢得到頁面的模板信息、數據模型url,根據模板和數據生成靜態化內容,並輸出到瀏覽器

@Controller
public class CmsPagePreviewController extends BaseController {

  @Autowired
  private PageService pageService;

  // 頁面預覽
  @RequestMapping(value = "/cms/preview/{pageId}",method = RequestMethod.GET)
  public void preview(@PathVariable("pageId") String pageId) throws IOException {
    // 執行靜態化
    String pageHtml = pageService.getPageHtml(pageId);
    // 通過response對象將內容輸出
    ServletOutputStream outputStream = response.getOutputStream();
    outputStream.write(pageHtml.getBytes("utf-8"));
  }

}

2. 預覽測試

1. 配置nginx代理

虛擬主機配置

#頁面預覽
location /cms/preview/ {
	proxy_pass http://cms_server_pool/cms/preview/;
}

配置cms_server_pool將請求轉發到cms:

#cms頁面預覽
upstream cms_server_pool{
	server 127.0.0.1:31001 weight=10;
}

重新加載nginx 配置文件

從cms_page找一個頁面進行測試。注意:頁面配置一定要正確,需設置正確的模板id和dataUrl。

2. 添加“頁面預覽”鏈接

在頁面列表添加“頁面預覽”鏈接,修改page_list.vue:

<template slot‐scope="page">
	<el‐button @click="edit(page.row.pageId)" type="text" size="small">修改</el‐button>
	<el‐button @click="del(page.row.pageId)" type="text" size="small">刪除</el‐button>
	<el‐button @click="preview(page.row.pageId)" type="text" size="small">頁面預覽</el‐button>

添加preview方法:

//頁面預覽
preview(pageId){
	window.open("http://www.xuecheng.com/cms/preview/"+pageId)
},
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章