《可视化搭建系统》“星空” nodejs server 端(三)
前两篇说到了星空的大体架构以及 webcomponents 在星空中的作用,本篇说一下星空 server(eggjs) 端做得一些事情。
表结构设计
之前在创业公司那套搭建系统,一张表维护了所有的信息,也行,但后期维护与扩展困难。所以我们需要根据需求及后期扩展来设计一下。
回顾一下搭建主页面 👇🏻
从上图可以看出,主要包含两大部分,页面跟物料。
简单分析下:
首先是页面,页面除了包含一些基本信息,比如页面发布状态、名称等以外,还涉及到页面跟物料的配置关系。涉及到发布,就有回滚,把回滚考虑进去,意味着页面配置存在多版本管理。
其次是物料,物料由前端开发,运营用来页面搭建。考虑物料更新(比如你这个物料有 bug,需要开发更新一版),那么物料也存在多版本管理。
具体如何来设计呢?
把 “page” “pageConfig” 分成两张表,“page”表管理基本数据,“pageConfig”管理页面配置数据及历史版本回滚。
把 “module” “module_version” 也分成两张表,“module” 管理基本数据,“module_version” 管理版本。
下面看下 4 张表的结构
page 表
CREATE TABLE IF NOT EXISTS `page` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '页面ID',
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '页面名称',
`owner` varchar(255) NOT NULL DEFAULT '' COMMENT '页面创建者',
`is_delete` tinyint(1) DEFAULT '0' COMMENT '是否删除状态',
`published_status` tinyint(1) DEFAULT '0' COMMENT '发布状态 0: 未发布; 1: 已发布线上; 2: 已下线;',
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
一些页面级别的属性,放在 page 表中进行管理。
page_config 表
CREATE TABLE IF NOT EXISTS `page_config` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '配置ID',
`page_id` bigint(20) unsigned NOT NULL COMMENT '关联的页面ID',
`owner` varchar(255) NOT NULL DEFAULT '' COMMENT '操作者',
`config` mediumtext COMMENT '页面配置的具体内容,JSON格式',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
其中 page_config 表通过 page_id 字段,跟 page 表进行关联。同一个 page_id 可以存在多条记录,也就可以用来管理页面历史记录、回滚等相关操作。
需要注意的是 config 字段, 在 page_config 中最为重要。
- materialList 存储页面物料搭建顺序
- props 存储物料的静态数据
- schema 存储 formRender 的渲染数据结构
- materialPageConfig 存储页面全局配置数据
{
"materialList": [
{
"id": 1,
"module_id": 1,
"title": "物料",
"version": 1,
"props": {
"desc": "物料静态配置数据"
},
"schema": {
"type": "object",
"properties": {
"desc": {
"title": "描述",
"type": "string",
"placeholder": "请输入",
"required": true
}
}
},
}
],
"materialPageConfig": {
"title": "测试标题",
"bgColor": "255,255,255,1"
}
}
module 表
CREATE TABLE IF NOT EXISTS `module` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '模块ID',
`owner` varchar(255) NOT NULL DEFAULT '' COMMENT '模块作者',
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '模块名称',
`desc` varchar(255) NOT NULL DEFAULT '' COMMENT '模块描述',
`cover` varchar(255) DEFAULT NULL COMMENT '模块封面',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
物料相关属性,放到 module 表进行管理。
modele_version 表
CREATE TABLE `module_version` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '版本号 ID',
`module_id` bigint(20) unsigned NOT NULL COMMENT '关联的模块ID',
`config` mediumtext COMMENT '模块配置信息,JSON格式',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
其中 module_version 表通过 module_id 字段,跟 module 表进行关联。同一个 module_id 可以存在多条记录,也就可以用来管理物料的历史记录、回滚等相关操作。
需要注意的是 config 字段, 在 module_version 中最为重要。
- props 存储物料的默认配置数据
- schema 存储 formRender 的渲染数据结构
{
"props": {
"desc": "我是一个物料"
},
"schema": {
"type": "object",
"properties": {
"desc": {
"title": "描述",
"type": "string",
"placeholder": "请输入",
"required": true
}
}
}
}
通过上述4张表,基本上就可以对物料,以及页面进行管理。
且可以根据自己的业务场景,进行一定的扩展。比如物料可以按业务类型进行区分,直播、内容、促销等,那么就可以在 module 表中,加一个 type 字段,用来管理类型。
项目结构
项目基于 eggjs 开发,个人觉得 eggjs 的目录结构还是蛮清晰的。
整体逻辑比较常规,围绕上述的4张表,进行增删改查。
基于 eggjs 的插件生态,一些基础功能,通过配置就可以快速完成。
- egg-cors 处理跨域
- egg-mysql 处理数据库
- egg-jwt 实现登录管理
- egg-validate 统一处理请求入参校验
基于 midlle 机制,自定义中间件处理业务逻辑。比如 response_handle 中间件,统一处理异常及接口请求数据结构。再比如 jwt 中间件,统一校验请求的登录态等。
预览、发布
server 端整体代码都不难,相对复杂一点的是预览跟发布接口,这里单独拿出来聊聊。
我们先来看下,预览跟发布的操作流程
点进测试地址, 观察下网络请求
观察上图,可以对请求分为 3 大类
- 页面 document html 请求,返回测试页面
- react/vue 物料运行时
- 物料资源
- 物料 js 资源
- 物料 css 样式
点击线上地址,其实请求跟测试地址是类似的,只是 document html 页面来源不同。测试页面是 eggjs 动态返回的,线上页面是 nginx 代理返回的。
细说预览流程
直接上代码,再来分析
可以看到,核心是 transformTemplate 方法,主要做了两件事
- 将数据库中获取到的页面配置数据,写入 starry_template.html 中
- 返回写入后的页面
如何将获取到的数据写入 starry_template.html 中呢?我们可以在模板中设置一个标志位,并将数据 JSON.stringify 一下后,替换掉标志位即可。也就是 transformTemplate 方法做的事情。
返回写入后的页面很简单,直接在 middle 中的 response_handle 中间件中支持一下 html 请求,改写一下 content-type 为 text/html 即可。
starry_template.html
最后,返回的 html,客户端是如何渲染成页面的呢,我们来分析一下。
第一步,取数据
上面说到数据注入到了页面中,那我们先把数据取出来。
- materialPageConfig 存储一些页面级别的配置,比如页面标题、页面背景色等,通过 document 相关方法对页面整体进行设置即可
- materialList 存储页面模块地址,以及模块的配置数据等,后面马上会用到
第二步,注册并渲染 webcomponents
- http://localhost:4000/static 是一个 nginx 的静态服务,用于存储开发好的 js 模块,这部分后续的系列会细讲,开发开发好的模块,打包后,通过 cli 会 upload 到对应的 nginx 服务
- magic(webcomponentName, module, enhanceOptions) 这部分是在注册 webcomponents,这部分可以参考《可视化搭建系统》“星空” webcomponents 从0到1(二)
- Promise.all 等待所有的 webcomponents 注册好之后,再根据 materialList 中的模块顺序,使用 document.createElement 创建 dom,append 至 body 下即可
发布流程
发布流程跟预览流程,基本一致。
只是说预览的 html 是根据预览数据实时生成,返回到客户端预览。而发布是将数据写入 html 后,将此 html 写入线上 nginx 服务,最后根据统一的线上域名/page_id.html 就可以访问到发布后的页面了。
小结
本文主要介绍了星空的 server 端的一些简单设计思想,主要是页面、模块管理以及模版 html 的应用。个人觉得,作为 nodejs 的简单入门实战项目,还是不错的。
“星空”已完成的历史系列
“星空”后续系列
- 物料模板设计,脚手架实现(四)
- “星空”搭建后台实现 (五)
- docker、nginx 环境详解(六)
- “星空”代码开源,本地启动流程详解(七)
转载自:https://juejin.cn/post/7253813970092359741