學成在線筆記四:頁面靜態化

頁面靜態化流程

頁面靜態化流程如下圖:

  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詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章