CMS网站页面管理开发汇总


CMS (Content Management System)即内容管理系统,不同的项目对CMS的定位不同。
每个公司对每个项目的CMS定位不同,CMS基本上分为:针对后台数据内容的管理、针对前端页面的管理、针对样式风格的管理等 。
比如:一个给企业做网站的公司,其CMS系统主要是网站页面管理及样式风格的管理。

流程

(1)创建站点
一个网站有很多子站点,比如:学成在线有主门户、学习中心、问答系统等子站点。具体的哪个页面是归属于具体的站点,所以要管理页面,先要管理页面所属的站点。
(2)创建模板
如电商网站的商品详情页面,每个页面的内容布局、板式是相同的,不同的只是内容,这个页面的布局、板式就是页面模板,模板+数据就组成一个完整的页面,最终要创建一个页面文件需要先定义此页面的模板,最终拿到页面的数据再结合模板就拼装成一个完整的页面。
(3)创建页面
指填写页面的基本信息,如:页面的名称、页面的url地址等。
(4)页面预览
页面发布前的一项工作,页面预览使用静态化技术根据页面模板和数据生成页面内容,并通过浏览器预览页面。页面发布前进行页面预览的目是为了保证页面发布后的正确性。
(5)页面发布
将页面发送到页面所在站点的服务器,页面发布成功就可以通过浏览器来访问了。

功能

1)页面管理
管理员在后台添加、修改、删除页面信息
2)页面预览
管理员通过页面预览功能预览页面发布后的效果。
3)页面发布
管理员通过页面发布功能将页面发布到远程门户服务器。
页面发布成功,用户即可在浏览器浏览到最新发布的页面,整个页面添加、发布的过程由于软件自动执行,无需人工登录服务器操作。

技术

数据库:网站管理对事物要求不是很严格,故使用nosql数据库mongodb,提升操作效率。
消息队列:RabbitMQ
框架:SpringBoot

工程结构

基础结构

parent工程:父工程,提供依赖管理,如下所有工程必须依赖该工程
model工程:模型工程,提供统一的模型类管理
utils工程:工具类工程,提供本项目所使用的工具类
api工程:接口工程,统一管理本项目的服务接口swagger,依赖model
face工程:服务接口工程,统一管理本项目的服务接口(可选,拓展dubbo技术),依赖model
common工程:通用工程,提供各层封装,依赖utils
cms工程:业务功能工程,提供网站、页面和模板crud功能实现,依赖api、common
cms_client工程:业务功能工程,提供物理服务器页面文件更新功能,依赖api、common

api工程

swagger2接口编写示例,该接口需要被对应业务工程controller实现

@Api(value="cms页面管理接口",description = "cms页面管理接口,提供页面的增、删、改、查")
public interface CmsPageControllerApi {
    //页面查询
    @ApiOperation("分页查询页面列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name="page",value = "页码",required=true,paramType="path",dataType="int"),
            @ApiImplicitParam(name="size",value = "每页记录数",required=true,paramType="path",dataType="int")
    })
    public QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest);
    //新增页面
    @ApiOperation("新增页面")
    public CmsPageResult add(CmsPage cmsPage);

    //根据页面id查询页面信息
    @ApiOperation("根据页面id查询页面信息")
    public CmsPage findById(String id);
    //修改页面
    @ApiOperation("修改页面")
    public CmsPageResult edit(String id,CmsPage cmsPage);

    //删除页面
    @ApiOperation("删除页面")
    public ResponseResult delete(String id);

    //页面发布
    @ApiOperation("页面发布")
    public ResponseResult post(String pageId);

    @ApiOperation("保存页面")
    public CmsPageResult save(CmsPage cmsPage);

    @ApiOperation("一键发布页面")
    public CmsPostPageResult postPageQuick(CmsPage cmsPage);

}

业务工程目录基本结构

一、代码文件
(1)controller
(2)service
(3)dao
(4)config
二、配置文件
(1)application.yml
(2)logback-spring.xml
示例:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<property resource="application.properties" />
    <!--定义日志文件的存储地址,使用绝对路径-->
    <property name="LOG_HOME" value="${log.logDirectory}"/>

    <!-- Console 输出设置 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <fileNamePattern>${LOG_HOME}/vander.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>512</queueSize>
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref="FILE"/>
    </appender>


    <logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>
    <logger name="org.springframework.boot" level="DEBUG"/>
    <root level="info">
        <!--<appender-ref ref="ASYNC"/>-->
        <appender-ref ref="FILE"/>
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

业务模型

页面信息

1、定义一个页面需要指定页面所属站点
一个站点包括多个页面,比如:学成在线的门户站点(网站)包括了多个页面。
2、定义一个页面需要指定页面使用的模板
多个页面可以使用相同的模板,比如:商品信息模板,每个商品就是一个页面,所有商品使用同一个商品信息模板

唯一索引:页面名称pageName、站点siteId、页面pageWebPath

拼接页面Url:cmsSite.siteDomain+cmsSite.siteWebPath+ cmsPage.pageWebPath + cmsPage.pageName

@Data
@ToString
@Document(collection = "cms_page")
public class CmsPage {
    //站点ID
    private String siteId;
    //页面ID
    @Id
    private String pageId;
    //页面名称,preview_4028e58161bd3b380161bd3bcd2f0000.html
    private String pageName;
    //别名,课程预览页面
    private String pageAliase;
    //访问地址,/coursepre/
    private String pageWebPath;
    //参数
    private String pageParameter;
    //物理路径,F:\\\\develop\\\\xc_portal_static\\\\course\\\\preview\\\\
    private String pagePhysicalPath;
    //类型(静态/动态),1
    private String pageType;
    //页面模版
    private String pageTemplate;
    //页面静态化内容
    private String pageHtml;
    //状态
    private String pageStatus;
    //创建时间
    private Date pageCreateTime;
    //模版id
    private String templateId;
    //参数列表
    private List<CmsPageParam> pageParams;
    //模版文件Id
//    private String templateFileId;
    //静态文件Id
    private String htmlFileId;
    //数据Url,http://localhost:40200/portalview/course/getpre/4028e58161bd3b380161bd3bcd2f0000
    private String dataUrl;
}

@Data
@ToString
public class CmsPageParam {
   //参数名称
    private String pageParamName;
    //参数值
    private String pageParamValue;
}

站点信息

@Data
@ToString
@Document(collection = "cms_site")
public class CmsSite { 
    //站点ID
    @Id
    private String siteId;
    //站点名称,门户主站
    private String siteName;
    //站点域名,http://localhost
    private String siteDomain;
    //站点端口,80
    private String sitePort;
    //站点访问地址,/
    private String siteWebPath;
    //创建时间
    private Date siteCreateTime;
    //站点物理路径
    private String sitePhysicalPath;

}
@Data
@ToString
@Document(collection = "cms_site_server")
public class CmsSiteServer {
    //站点id
    private String siteId;
    //服务器ID
    @Id
    private String serverId;
    //服务器IP,127.0.0.1
    private String ip;
    //端口,80
    private String port;
    //访问地址,/
    private String webPath;
    //服务器名称(代理、静态、动态、CDN),门户服务器
    private String serverName;
    //资源发布地址(完整的HTTP接口),/upload
    private String uploadPath;
    //使用类型(测试、生产)
    private String useType;
}

模板信息

@Data
@ToString
@Document(collection = "cms_template")
public class CmsTemplate {
    //站点ID
    private String siteId;
    //模版ID
    @Id
    private String templateId;
    //模版名称,轮播图
    private String templateName;
    //模版参数
    private String templateParameter;
    //模版文件Id,存在到GridFs唯一ID
    private String templateFileId;
}

功能实现

需明确先后顺序,如下:
(1)模板crud实现(上传模板到GridFs获取templateFileId)
(2)站点crud实现
(3)页面crud实现

页面发布

流程

1、前端请求cms执行页面发布。
2、cms执行静态化程序生成html文件。
3、cms将html文件存储到GridFS中。
4、cms向MQ发送页面发布消息
5、MQ将页面发布消息通知给Cms Client
6、Cms Client从GridFS中下载html文件
7、Cms Client将html保存到所在服务器指定目录

步骤

(1)通过页面pageId获取:数据模型Map

//获取数据模型
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_DATAURLISNULL);
    }
    //通过restTemplate请求dataUrl获取数据
    ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
    Map body = forEntity.getBody();
    return body;
}

(2)通过页面pageId获取:模板文件字符串

//获取页面的模板信息
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(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;
}

(3)执行静态化获取:最终页面文件字符串

//执行静态化
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;
}

(4)保存最终页面文件到GridFs,并更新数据库页面信息

//保存html到GridFS
private CmsPage saveHtml(String pageId,String htmlContent){
    //先得到页面信息
    CmsPage cmsPage = this.getById(pageId);
    if(cmsPage == null){
        ExceptionCast.cast(CommonCode.INVALID_PARAM);
    }
    ObjectId objectId = null;
    try {
        //将htmlContent内容转成输入流
        InputStream inputStream = IOUtils.toInputStream(htmlContent, "utf-8");
        //将html文件内容保存到GridFS
        objectId = gridFsTemplate.store(inputStream, cmsPage.getPageName());
    } catch (IOException e) {
        e.printStackTrace();
    }
    //将html文件id更新到cmsPage中
    cmsPage.setHtmlFileId(objectId.toHexString());
    cmsPageRepository.save(cmsPage);
    return cmsPage;
}

(5)发送消息到客户端

//向mq 发送消息
private void sendPostPage(String pageId){
    //得到页面信息
    CmsPage cmsPage = this.getById(pageId);
    if(cmsPage == null){
        ExceptionCast.cast(CommonCode.INVALID_PARAM);
    }
    //创建消息对象
    Map<String,String> msg = new HashMap<>();
    msg.put("pageId",pageId);
    //转成json串
    String jsonString = JSON.toJSONString(msg);
    //发送给mq
    //站点id
    String siteId = cmsPage.getSiteId();
    rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE,siteId,jsonString);
}

(6)客户端监听到消息

@RabbitListener(queues = {"${xuecheng.mq.queue}"})
public void postPage(String msg){
    //解析消息
    Map map = JSON.parseObject(msg, Map.class);
    //得到消息中的页面id
    String pageId = (String) map.get("pageId");
    //校验页面是否合法
    CmsPage cmsPage = pageService.findCmsPageById(pageId);
    if(cmsPage == null){
        LOGGER.error("receive postpage msg,cmsPage is null,pageId:{}",pageId);
        return ;
    }
    //调用service方法将页面从GridFs中下载到服务器
    pageService.savePageToServerPath(pageId);
}

(7)客户端将最新页面文件下载到物理服务器

//保存html页面到服务器物理路径
public void savePageToServerPath(String pageId){
    //根据pageId查询cmsPage
    CmsPage cmsPage = this.findCmsPageById(pageId);
    //得到html的文件id,从cmsPage中获取htmlFileId内容
    String htmlFileId = cmsPage.getHtmlFileId();

    //从gridFS中查询html文件
    InputStream inputStream = this.getFileById(htmlFileId);
    if(inputStream == null){
        LOGGER.error("getFileById InputStream is null ,htmlFileId:{}",htmlFileId);
        return ;
    }
    //得到站点id
    String siteId = cmsPage.getSiteId();
    //得到站点的信息
    CmsSite cmsSite = this.findCmsSiteById(siteId);
    //得到站点的物理路径
    String sitePhysicalPath = cmsSite.getSitePhysicalPath();
    //得到页面的物理路径
    String pagePath = sitePhysicalPath + cmsPage.getPagePhysicalPath() + cmsPage.getPageName();
    //将html文件保存到服务器物理路径上
    FileOutputStream fileOutputStream = null;
    try {
        fileOutputStream = new FileOutputStream(new File(pagePath));
        IOUtils.copy(inputStream,fileOutputStream);
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

页面预览

通过页面发布(1)(2)(3)步骤可以获取到最终页面文本

//页面预览
@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();
    response.setHeader("Content-type","text/html;charset=utf-8");
    outputStream.write(pageHtml.getBytes("utf-8"));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值