页面静态化
页面静态化流程
页面静态化流程如下图:
、
- 静态化程序首先读取页面获取DataUrl。
- 静态化程序远程请求DataUrl得到数据模型。
- 获取页面模板。
- 执行页面静态化。
CMS模板文件上传
CMS页面模板文件上传功能实现,该功能在新增或编辑模板的时候可进行模板文件的上传
最终页面效果如下:
后端
CmsTemplateControllerApi
新增接口定义
1 2 3 4 5
| @ApiOperation("上传模板文件") String uploadTemplate(MultipartFile file);
@ApiOperation("移除模板文件") void removeTemplateFile(String templateFileId);
|
CmsTemplateController
接口实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Override @PostMapping("upload") public String uploadTemplate(@RequestParam("file") MultipartFile file) { String templateFileId = cmsTemplateService.uploadTemplateFile(file); if (StringUtils.isBlank(templateFileId)) { ExceptionCast.cast(CmsCode.CMS_TEMPLATE_FILE_UPLOAD_ERROR); } return templateFileId; }
@Override @DeleteMapping("file/remove/{templateFileId}") public void removeTemplateFile(@PathVariable String templateFileId) { cmsTemplateService.removeTemplateFile(templateFileId); }
|
CmsTemplateService
完成模板文件上传与删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Autowired private GridFsTemplate gridFsTemplate;
public String uploadTemplateFile(MultipartFile file) { try { return gridFsTemplate.store(file.getInputStream(), "template").toString(); } catch (Exception e) { return ""; } }
public void removeTemplateFile(String templateFileId) { Query query = new Query(Criteria.where("_id").is(templateFileId)); gridFsTemplate.delete(query); }
|
修改删除方法的逻辑,在删除模板之前先删除模板文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public void deleteById(String templateId) { Optional<CmsTemplate> templateOptional = cmsTemplateRepository.findById(templateId); if (templateOptional.isPresent()) { Query query = new Query(Criteria.where("_id").is(templateOptional.get().getTemplateFileId())); gridFsTemplate.delete(query); cmsTemplateRepository.deleteById(templateId); } }
|
前端
API定义
修改src/module/cms/api/cms.js
,新增API定义
1 2 3 4 5 6
|
export const removeTemplateFileById = (templateFileId) => { return http.requestDelete(apiUrl + '/cms/template/file/remove/'+ templateFileId) }
|
页面内容新增
在template_add.vue
以及template_edit.vue
中新增文件上传框
1 2 3 4 5 6 7 8 9 10 11 12 13
| <el-form-item label="模板文件ID"> <el-upload class="upload-demo" drag action="http://localhost:11000/api/cms/template/upload" :multiple="multiple" :limit="limit" :on-success="uploadOnSuccess" :on-remove="onRemove"> <i class="el-icon-upload"></i> <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> </el-upload> </el-form-item>
|
新增方法
在template_add.vue
以及template_edit.vue
中新增需要调用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| uploadOnSuccess:function(response, file, fileList) { if (response) { this.cmsTemplate.templateFileId = response this.$message({ showClose: true, message: '模板文件上传成功', type: 'success' }) } },
onRemove:function(file, fileList) { cmsApi.removeTemplateFileById(this.cmsTemplate.templateFileId).then(res => { this.$message({ showClose: true, message: '删除成功', type: 'success' }) }) }
|
数据模型接口实现
实体类
CmsConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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 {
@Id private String id; private String name; private List<CmsConfigModel> model;
}
|
CmsConfigModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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;
}
|
CmsConfigResult
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.xuecheng.framework.domain.cms.response;
import com.xuecheng.framework.domain.cms.CmsConfig; import com.xuecheng.framework.model.response.ResponseResult; import com.xuecheng.framework.model.response.ResultCode; import lombok.Data;
@Data public class CmsConfigResult extends ResponseResult { CmsConfig cmsConfig; public CmsConfigResult(ResultCode resultCode, CmsConfig cmsConfig) { super(resultCode); this.cmsConfig = cmsConfig; } }
|
CmsConfigRepository
1 2 3 4 5 6 7
| 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> { }
|
CmsConfigService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.xuecheng.manage_cms.service;
import com.xuecheng.framework.domain.cms.CmsConfig; import com.xuecheng.manage_cms.dao.CmsConfigRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
@Slf4j @Service public class CmsConfigService {
@Autowired private CmsConfigRepository cmsConfigRepository;
public CmsConfig findById(String id) { return cmsConfigRepository.findById(id).orElse(null); }
}
|
CmsConfigController & CmsConfigControllerApi
CmsConfigController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.xuecheng.manage_cms.controller;
import com.xuecheng.api.cms.CmsConfigControllerApi; import com.xuecheng.framework.domain.cms.CmsConfig; import com.xuecheng.framework.domain.cms.response.CmsCode; import com.xuecheng.framework.exception.ExceptionCast; import com.xuecheng.manage_cms.service.CmsConfigService; 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 {
@Autowired private CmsConfigService cmsConfigService;
@Override @GetMapping("{id}") public CmsConfig getModel(@PathVariable String id) { CmsConfig cmsConfig = cmsConfigService.findById(id); if (cmsConfig == null) { ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL); } return cmsConfig; } }
|
CmsConfigControllerApi
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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配置信息") CmsConfig getModel(String id);
}
|
静态化实现
CmsPageService
编写静态页面生成方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
public String genHtml(String pageId) { String html = null;
Map model = getModel(pageId);
String templateContent = getTemplate(pageId);
try { Configuration configuration = new Configuration(Configuration.getVersion());
StringTemplateLoader templateLoader = new StringTemplateLoader(); templateLoader.putTemplate("template", templateContent);
configuration.setTemplateLoader(templateLoader);
Template template = configuration.getTemplate("template");
html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
} catch (IOException e) { ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL); } catch (TemplateException e) { ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_SAVEHTMLERROR); } return html; }
private String getTemplate(String pageId) { CmsPage cmsPage = this.findByPageId(pageId); isNullOrEmpty(cmsPage, CmsCode.CMS_EDITPAGE_NOTEXISTS); isNullOrEmpty(cmsPage.getTemplateId(), CmsCode.CMS_EDITPAGE_NOTEXISTS);
CmsTemplate cmsTemplate = cmsTemplateService.findByTemplateId(cmsPage.getTemplateId()); isNullOrEmpty(cmsTemplate, CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL); isNullOrEmpty(cmsTemplate.getTemplateFileId(), CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL); String fileContent = downloadFileFromMongoDB(cmsTemplate.getTemplateFileId()); isNullOrEmpty(fileContent, CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
return fileContent; }
private String downloadFileFromMongoDB(String fileId) { GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId))); if (gridFSFile == null) { ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL); } GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId()); GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream); String content = null; try { content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8"); } catch (IOException ignored) { } return content; }
private Map getModel(String pageId) { CmsPage cmsPage = this.findByPageId(pageId); if (cmsPage == null) { ExceptionCast.cast(CmsCode.CMS_EDITPAGE_NOTEXISTS); } if (StringUtils.isBlank(cmsPage.getDataUrl())) { ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL); } ResponseEntity<Map> forEntity = restTemplate.getForEntity(cmsPage.getDataUrl(), Map.class); if (forEntity.getBody() == null) { ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL); } return forEntity.getBody(); }
|
效果测试
编写模板文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <!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"> <#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>
|
上传模板文件到文件系统中,我在最开始已经实现了模板文件的上传,所以我这里只需要新增一个模板。
新建页面并使用该模板
编写测试方法
1 2 3 4 5 6 7 8
| @Test public void testGenHtml() { String pageId = "5d7b85025f315734a084d61e"; String s = cmsPageService.genHtml(pageId); System.out.println(s); }
|
运行
只截取了部分,大致效果差不多,我就不贴从页面访问的效果了,因为这个图片链接是fastDFS
中的链接~ 😅😅😅😅。
页面预览
需求分析
页面在发布前增加页面预览的步骤,方便用户检查页面内容是否正确。页面预览的流程如下:
- 用户进入cms前端,点击“页面预览”在浏览器请求cms页面预览链接。
- cms根据页面id查询DataUrl并远程请求DataUrl获取数据模型。
- cms根据页面id查询页面模板内容。
- cms执行页面静态化。
- cms将静态化内容响应给浏览器。
- 在浏览器展示页面内容,实现页面预览的功能。
后端
引入依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
|
配置Freemarker
1 2 3 4 5 6 7
| spring: freemarker: cache: false settings: template_update_delay: 0
|
CmsPagePreviewController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.xuecheng.manage_cms.controller;
import com.xuecheng.framework.domain.cms.response.CmsCode; import com.xuecheng.framework.web.BaseController; import com.xuecheng.manage_cms.service.CmsPageService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets;
@Slf4j @Controller public class CmsPagePreviewController extends BaseController {
@Autowired private CmsPageService cmsPageService;
@RequestMapping("cms/preview/{pageId}") public void preview(@PathVariable String pageId) { String htmlContent = cmsPageService.genHtml(pageId); isNullOrEmpty(htmlContent, CmsCode.CMS_GENERATEHTML_HTMLISNULL); try { ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(htmlContent.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { log.error("[CMS页面预览] 预览页面失败,异常信息:{}", e); } }
}
|
前端
添加页面预览按钮
修改page_list.vue
,在操作栏新增页面预览
按钮
1 2 3 4 5
| <el-button size="small" type="text" @click="preview(scope.$index, scope.row)">页面预览 </el-button>
|
方法区新增页面预览方法
1 2 3 4
| preview:function(index, data) { window.open("http://localhost:11000/cms/preview/" + data.pageId) }
|
注意
我这里没有按照教程上面的设置nginx
,因为我做到这里的时候我连前端门户工程都没搭建(lazy,emmmm~),所以这里就没有使用nginx
理了。
第五天的内容全部都是RabbitMQ的教学,所有我就没有整理成笔记了。
但是!但是!但是!我有另外两篇文章专门对RabbitMQ做了笔记(献丑了)
RabbitMQ详解
代码获取
代码获取