发布一个页面,需发布到该页面所属的每个站点服务器,其它站点服务器不发布
比如:发布一个门户的页面,需要发布到每个门户服务器上,而用户中心服务器则不需要发布
所以本项目采用routing模式,用站点id作为routingKey,这样就可以匹配页面只发布到所属的站点服务器上
页面发布流程:
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保存到所在服务器指定目录
创建Cms Client工程作为页面发布消费方,将Cms Client部署在多个服务器上,它负责接收到页面发布的消息后从GridFS中下载文件在本地保存
页面发布消费方:
需求:
1、将cms Client部署在服务器,配置队列名称和站点ID
2、cms Client连接RabbitMQ并监听各自的“页面发布队列”
3、cms Client接收页面发布队列的消息
4、根据消息中的页面id从mongodb数据库下载页面到本地
调用dao查询页面信息,获取到页面的物理路径,调用dao查询站点信息,得到站点的物理路径
页面物理路径=站点物理路径+页面物理路径+页面名称
从GridFS查询静态文件内容,将静态文件内容保存到页面物理路径下
创建Cms Client工程:
1、pom中添加amqp、mongodb、io、fastjson依赖
2、yml文件中配置MongoDB、rabbitmq服务
3、编写RabbitmqConfig配置类
创建交换机
每个队列与交换机绑定
配置队列名称和routingKey,将站点ID作为routingKey
最重要还是这个配置类
定义消息格式:消息内容采用json格式存储数据
PageService:
保存html页面到服务器物理路径:
根据pageId查询cmsPage表获取:FileId、SiteId、PageName、PagePhysicalPath;
拼接页面的物理路径;
将页面的物理路径保存到服务器物理路径上;
ConsumerPostPage:
作为发布页面的消费客户端,监听 页面发布队列的消息,收到消息后从mongodb下载文件,保存在本地。
页面发布生产方:
CMS系统作为页面发布的生产方,管理员通过 cms系统发布“页面发布”的消费。
1、yml中配置Rabbitmq的连接参数
2、pom中配置amqp
3、RabbitMQConfig配置
由于cms作为页面发布方要面对很多不同站点的服务器,面对很多页面发布队列,所以这里不再配置队列,只需要 配置交换机即可。
4、在PageService类中定义页面发布方法postPage、发送页面发布消息sendPostPage、保存静态页面内容saveHtml。
代码执行流程:
在page_list.vue
页面
<el-button size="small" type="primary" plain @click="postPage(page.row.pageId)">发布</el-button>
点击发布调用postPage方法:
postPage: function (pageId) {
this.$confirm('确认发布该页面吗?', '提示', {}).then(() => {
cmsApi.page_postPage(pageId).then((res) => {
if (res.success) {
console.log('发布页面id=' + pageId);
this.$message.success('发布成功,请稍后查看结果');
} else {
this.$message.error('发布失败');
}
});
}).catch(() => {
});
}
调用page_postPage请求后端接口:
/*发布页面*/
export const page_postPage= id => {
return http.requestPost(apiUrl+'/cms/page/postPage/'+id)
}
执行CmsPageController中方法:
该方法执行完成调用rabbitTemplate.convertAndSend
方法发送消息:
Client监听到mq消息时会调用PageService中的savePageToServerPath方法进行消费
思考:
1、如果发布到服务器的页面内容不正确怎么办?
2、一个页面需要发布很多服务器,点击“发布”后如何知道详细的发布结果?
3、一个页面发布到多个服务器,其中有一个服务器发布失败时怎么办?
用户的操作流程:
1、进入我的课程
2、点击“添加课程”,进入添加课程界面
3、输入课程基本信息,点击提交
4、课程基本信息提交成功,自动进入“管理课程”界面,点击“管理课程”也可以进入“管理课程”界面
5、编辑图片 上传课程图片
6、编辑课程营销信息 营销信息主要是设置课程的收费方式及价格
7、编辑课程计划 添加课程计划
课程计划:定义了课程的章节内容, 课程计划包括两级,第一级是课程的大章节、第二级是大章节下属的小章节,每个小章节通常是一段视频,学生点击小章节在线学习
代码实现:
前端使用element-ui 的tree组件来完成,在course_plan.vue文件中添加tree组件的代码
<el-tree
:data="teachplanList"
:props="defaultProps"
node-key="id"
default-expand-all
:expand-on-click-node="false"
:render-content="renderContent">
</el-tree>
数据对象:
data() {
return {
mediaFormVisible:false,
teachplayFormVisible:false,//控制添加窗口是否显示
teachplanList : [{
id: 1,
pname: '一级 1',
children: [{
id: 4,
pname: '二级 1-1',
children: [{
id: 9,
pname: '三级 1-1-1'
}, {
id: 10,
pname: '三级 1-1-2'
}]
}]
}],
defaultProps:{
children: 'children',
label: 'pname'
},
teachplanRules: {
pname: [
{required: true, message: '请输入课程计划名称', trigger: 'blur'}
],
status: [
{required: true, message: '请选择状态', trigger: 'blur'}
]
},
teachplanActive:{},
teachplanId:''
}
}
后端采用表的自连接方式进行查询,mapper.xml文件的SQL语句如下:
<select id="selectList" parameterType="java.lang.String"
resultMap="teachplanMap">
SELECT
a.id one_id,
a.pname one_pname,
b.id two_id,
b.pname two_pname,
c.id three_id,
c.pname three_pname
FROM
teachplan a
LEFT JOIN teachplan b
ON b.parentid = a.id
LEFT JOIN teachplan c
ON c.parentid = b.id
WHERE a.parentid = '0'
<if test="_parameter !=null and _parameter!=''">
AND a.courseid = #{courseId}
</if>
ORDER BY a.orderby,
b.orderby,
c.orderby
</select>
说明:针对输入参数为简单类型#{}中可以是任意类型,判断参数是否为空要用 _parameter(它属于mybatis的内 置参数)
添加课程计划:
上级结点说明:
不选择上级结点表示当前课程计划为该课程的一级结点。
当添加该课程在课程计划中还没有节点时要自动添加课程的根结点
添加课程计划采用弹出窗口组件Dialog
1、在course_plan.vue页面添加添加课程计划的弹出窗口代码:
<el-dialog title="添加课程计划" :visible.sync="teachplayFormVisible">
<el-form ref="teachplanForm" :model="teachplanActive" label-width="140px" style="width:600px;"
:rules="teachplanRules">
<el-form-item label="上级结点">
<el-select v-model="teachplanActive.parentid" placeholder="不填表示根结点">
<el-option
v-for="item in teachplanList"
:key="item.id"
:label="item.pname"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="章节/课时名称" prop="pname">
<el-input v-model="teachplanActive.pname" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="课程类型">
<el-radio-group v-model="teachplanActive.ptype">
<el-radio class="radio" label='1'>视频</el-radio>
<el-radio class="radio" label='2'>文档</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="学习时长(分钟) 请输入数字">
<el-input type="number" v-model="teachplanActive.timelength" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="排序字段">
<el-input v-model="teachplanActive.orderby" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="章节/课时介绍" prop="description">
<el-input type="textarea" v-model="teachplanActive.description"></el-input>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="teachplanActive.status">
<el-radio class="radio" label="0">未发布</el-radio>
<el-radio class="radio" label='1'>已发布</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="addTeachplan">提交</el-button>
<el-button type="primary" v-on:click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</el-dialog>
2、在数据模型中添加如下变量:
teachplayFormVisible:false,//控制添加窗口是否显示
3、后台CourseController中编写addTeachplan方法
测试流程:
1、新建一个课程
2、向新建课程中添加课程计划
添加一级结点
添加二级结点