学成在线笔记四:页面静态化

页面静态化流程

页面静态化流程如下图:

  1. 静态化程序首先读取页面获取DataUrl。
  2. 静态化程序远程请求DataUrl得到数据模型。
  3. 获取页面模板。
  4. 执行页面静态化。

CMS模板文件上传

CMS页面模板文件上传功能实现,该功能在新增或编辑模板的时候可进行模板文件的上传

最终页面效果如下:

后端

CmsTemplateControllerApi

新增接口定义

    @ApiOperation("上传模板文件")
    String uploadTemplate(MultipartFile file);

    @ApiOperation("移除模板文件")
    void removeTemplateFile(String templateFileId);

CmsTemplateController

接口实现

    @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

完成模板文件上传与删除

    @Autowired
    private GridFsTemplate gridFsTemplate;

    /**
     * 上传文件
     *
     * @param file 文件
     */
    public String uploadTemplateFile(MultipartFile file) {
        try {
            return gridFsTemplate.store(file.getInputStream(), "template").toString();
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 移除文件
     *
     * @param templateFileId 模板文件ID
     */
    public void removeTemplateFile(String templateFileId) {
        Query query = new Query(Criteria.where("_id").is(templateFileId));
        gridFsTemplate.delete(query);
    }

修改删除方法的逻辑,在删除模板之前先删除模板文件

    /**
     * 删除指定ID的模板
     *
     * @param templateId 模板ID
     */
    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定义

/**
 * 按ID删除模板
 */
export const removeTemplateFileById = (templateFileId) => { 
    return http.requestDelete(apiUrl + '/cms/template/file/remove/'+ templateFileId)
}

页面内容新增

template_add.vue以及template_edit.vue中新增文件上传框

            <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中新增需要调用的方法

// 模板文件上传成功
uploadOnSuccess:function(response, file, fileList) {
    if (response) {
        this.cmsTemplate.templateFileId = response
        this.$message({
            showClose: true,
            message: '模板文件上传成功',
            type: 'success'
        })
    }
},
// 移除模板文件
onRemove:function(file, fileList) {
    // 调用API 删除文件
    cmsApi.removeTemplateFileById(this.cmsTemplate.templateFileId).then(res => {
        this.$message({
            showClose: true,
            message: '删除成功',
            type: 'success'
        })
    })
}

数据模型接口实现

实体类

  • CmsConfig

    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;
    
    /**
     * Created by admin on 2018/2/6.
     */
    @Data
    @ToString
    @Document(collection = "cms_config")
    public class CmsConfig {
    
        @Id
        private String id;
        private String name;
        private List<CmsConfigModel> model;
    
    }
    
  • CmsConfigModel

    package com.xuecheng.framework.domain.cms;
    
    import lombok.Data;
    import lombok.ToString;
    
    import java.util.Map;
    
    /**
     * Created by admin on 2018/2/6.
     */
    @Data
    @ToString
    public class CmsConfigModel {
        private String key;
        private String name;
        private String url;
        private Map mapValue;
        private String value;
    
    }
    
  • CmsConfigResult

    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

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

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;

    /**
     * 按ID查询CMS配置信息
     *
     * @param id id
     */
    public CmsConfig findById(String id) {
        return cmsConfigRepository.findById(id).orElse(null);
    }

}

CmsConfigController & CmsConfigControllerApi

  • CmsConfigController

    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

    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

编写静态页面生成方法

    /**
     * 根据页面ID生成html
     * 流程:
     * 1、静态化程序获取页面的DataUrl
     * 2、静态化程序远程请求DataUrl获取数据模型。
     * 3、静态化程序获取页面的模板信息
     * 4、执行页面静态化
     *
     * @param pageId 页面ID
     */
    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;
    }

    /**
     * 获取模板内容
     *
     * @param pageId 页面ID
     * @return 模板内容
     */
    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;
    }

    /**
     * 下载文件
     *
     * @param fileId 文件ID
     * @return 文件内容
     */
    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 gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
        //获取流中的数据
        String content = null;
        try {
            content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
        } catch (IOException ignored) { }
        return content;
    }

    /**
     * 根据pageId获取模型数据
     *
     * @param pageId 页面ID
     * @return 模型数据
     */
    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. 编写模板文件

    <!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>
    
  2. 上传模板文件到文件系统中,我在最开始已经实现了模板文件的上传,所以我这里只需要新增一个模板。

  3. 新建页面并使用该模板

  4. 编写测试方法

        @Test
        public void testGenHtml() {
            // 此ID需要到数据库中查看
            String pageId = "5d7b85025f315734a084d61e";
            // 生成html
            String s = cmsPageService.genHtml(pageId);
            System.out.println(s);
        }
    
  5. 运行

    只截取了部分,大致效果差不多,我就不贴从页面访问的效果了,因为这个图片链接是fastDFS中的链接~ 😅😅😅😅。

页面预览

需求分析

页面在发布前增加页面预览的步骤,方便用户检查页面内容是否正确。页面预览的流程如下:

  1. 用户进入cms前端,点击“页面预览”在浏览器请求cms页面预览链接。
  2. cms根据页面id查询DataUrl并远程请求DataUrl获取数据模型。
  3. cms根据页面id查询页面模板内容。
  4. cms执行页面静态化。
  5. cms将静态化内容响应给浏览器。
  6. 在浏览器展示页面内容,实现页面预览的功能。

后端

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

配置Freemarker

spring:
  freemarker:
    # 关闭缓存
    cache: false
    settings:
      # 模板更新时间,正式环境可以设置较大
      template_update_delay: 0

CmsPagePreviewController

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;

    /**
     * CMS页面预览
     *
     * @param pageId 预览的页面ID
     */
    @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,在操作栏新增页面预览按钮

<el-button
    size="small"
    type="text"
    @click="preview(scope.$index, scope.row)">页面预览
</el-button>

方法区新增页面预览方法

// 页面预览
preview:function(index, data) {
    window.open("http://localhost:11000/cms/preview/" + data.pageId)
}

注意

我这里没有按照教程上面的设置nginx,因为我做到这里的时候我连前端门户工程都没搭建(lazy,emmmm~),所以这里就没有使用nginx理了。

第五天的内容全部都是RabbitMQ的教学,所有我就没有整理成笔记了。

但是!但是!但是!我有另外两篇文章专门对RabbitMQ做了笔记(献丑了)

RabbitMQ详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值