1.互动问答相关
准备阶段—分析业务流程
- 主流程
整体来说,流程是这样的:
- 学员在学习的过程中可以随时提问问题
- 老师、其他学员都可以回答问题
- 老师、学员也都可以对回答多次回复
- 老师、学员也都可以对评论多次回复
- 老师可以在管理端管理问题、回答、评论的状态
准备阶段—字段分析
根据原型图可以得到对应字段
准备阶段—ER图
基本上根据页面原型图得到的字段:
准备阶段—表结构
- 问题表
- 回答/评论表
准备阶段—Mybatis-Plus代码生成
可以生成对应的文件和实体类等信息
准备阶段–类型枚举
准备阶段—接口统计
理论上我们应该先设计所有接口,再继续设计接口对应的表结构。不过由于接口较多,这里我们先对接口做简单统计。然后直接设计数据库,最后边设计接口,边实现接口。
1.用户端
问题页面:
结合原型设计图我们可以看到这里包含4个接口:
- 带条件过滤的分页查询
- 新增提问
- 修改提问
- 删除提问
问题的回答和评论页面:
可以看到页面中包含5个接口:
- 根据id查询问题详情
- 分页查询问题下的所有回答
- 分页查询回答下的评论
- 点赞/取消点赞某个回答或评论
- 回答某个提问、评论他人回答
2.管理端
刚才分析的都是用户端的相关接口,这些接口部分可以与管理端共用,但管理端也有自己的特有需求。
管理端也可以分页查询问题列表,而且过滤条件、查询结果会有很大不同:
比较明显的有两个接口:
- 管理端分页查询问题列表:与用户端分页查询不通用,功能更复杂,查询条件更多
- 隐藏或显示指定问题
除此以外,这里有一个问题状态字段,表示管理员是否查看了该问题以及问题中的回答。默认是未查看状态;当管理员点击查看后,状态会变化为已查看;当学员再次回答或评论,状态会再次变为未查看。
因此,需要注意的是:
- 每当用户点击查看按钮,需要根据根据id查询问题详情,此时应标记问题状态为已查看
- 每当学员回答或评论时,需要将问题标记为未查看
管理端也会有回答列表、评论列表。另外,回答和评论同样有隐藏功能。
问题详情和回答列表:
还有评论列表:
总结一下,回答和评论包含的接口有:
- 管理端根据id查询问题详情
- 分页查询问题下的回答
- 分页查询回答下的评论
- 点赞/取消点赞某个回答或评论
- 隐藏/显示指定回答或评论
- 回答某个提问、评论他人回答、评论(与用户端共用)
==—–用户端–问题相关接口—–==
1.新增互动问题(用户端)
1.原型图
2.设计数据库
3.业务逻辑图
比较简单,通过前端传递给我{课程id,章id,小节id,问题标题,问题具体描述,问题是否匿名}
4.接口分析
通过新增的问题的表单即可分析出接口的请求参数信息了,然后按照Restful的风格设计即可:
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无
6.具体难点和亮点
无
2.修改问题(用户端)
1.原型图
2.设计数据库
3.业务逻辑图
4.接口分析
修改与新增表单基本类似,此处不再分析。我们可以参考新增的接口,然后按照Restful的风格设计为更新即可:
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无
6.具体难点和亮点
要注意校验问题是否是自己的,校验是否有这条问题
3.分页查询问题(用户端)
1.原型图
2.设计数据库
3.业务逻辑图
这就是一个典型的分页查询。主要分析请求参数和返回值就行了。
请求参数就是过滤条件,页面可以看到的条件有:
- 分页条件
- 全部回答/我的回答:也就是要不要基于用户id过滤
- 课程id:隐含条件,因为问题列表是在某课程详情页面查看的,所以一定要以课程id为条件
- 章节id:可选条件,当用户点击小节时传递
返回值格式,从页面可以看到属性有:
- 是否匿名:如果提交问题是选择了匿名,则页面不能展示用户信息
- 用户id:匿名则不显示
- 用户头像:匿名则不显示
- 用户名称:匿名则不显示
- 问题标题
- 提问时间
- 回答数量
- 最近一次回答的信息:
- 回答人名称
- 回答内容
4.接口分析
综上,按照Restful来设计接口,信息如下:
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
[for循环遍历组装数据]
- 4.mapper层
无
6.具体难点和亮点
主要就是根据问题表和问答表查询对应信息:
测试结果:
1 | { |
封装过程:
4.根据id查询问题详情(用户端)
1.原型图
由此可以看出详情页所需要的信息相比分页时,主要多了问题详情,主要字段有:
- 是否匿名
- 用户id:匿名则不显示
- 用户头像:匿名则不显示
- 用户名称:匿名则不显示
- 问题标题
- 提问时间
- 回答数量
- 问题描述详情
2.设计数据库
3.业务逻辑图
1 | 1.根据问题id获取一条问题 |
4.接口分析
而请求参数则更加简单了,就是问题的id
然后,再按照Restful风格设计,接口就出来了:
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无
6.具体难点和亮点
要注意只有不匿名的情况下才能获取用户信息
5.删除问题(用户端)
1.原型图
2.设计数据库
3.业务逻辑图
需要注意的是,当用户删除某个问题时,也需要删除问题下的回答、评论。
整体业务流程如下:
- 查询问题是否存在
- 判断是否是当前用户提问的
- 如果不是则报错
- 如果是则删除问题
- 然后删除问题下的回答及评论 【两个表根据问题表的主键id和评论问答表的question_id对应(1对多)】
4.接口分析
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无
6.具体难点和亮点
需要注意的是,当用户删除某个问题时,也需要删除问题下的回答、评论。
整体业务流程如下:
- 查询问题是否存在
- 判断是否是当前用户提问的
- 如果不是则报错
- 如果是则删除问题
- 然后删除问题下的回答及评论 【两个表根据问题表的主键id和评论问答表的question_id对应(1对多)】
==—–用户端–评论相关接口—–==
1.新增回答/评论(用户端)
1.原型图
针对回答和评论的区别:
2.设计数据库
3.业务逻辑图
4.接口分析
综上,按照Restful的规范设计,接口信息如下:
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无,使用mq即可
6.具体难点和亮点
问题一:回复和评论的区别
回复:回答哪个问题,回复什么内容,要不要被看到
评论:回答哪个问题,回复什么内容,要不要被看到 + 【上一级】是哪个回答下面,评论哪个回答,针对谁
- 问题二:新增回答记得更新question的最近一次回答id
- 问题三:如果评论的用户是学生(前端传递是否是学生提交),标记问题为未查看【管理端根据id查看问题详情会更改为已查看】
2.分页查询回答/评论列表(用户端)
1.原型图
在问题详情页,除了展示问题详情外,最重要的就是回答列表了,原型图如下:
我们先来分析回答列表,需要展示的内容包括:
- 回答id
- 回答内容
- 是否匿名
- 回答人信息(如果是匿名,则无需返回)
- id
- 昵称
- 头像
- 回答时间
- 评论数量
- 点赞数量
请求参数就是问题的id。不过需要注意的是,一个问题下的回答比较多,所以一次只能展示一部分,更多数据会采用滚动懒加载模式。简单来说说就是分页查询,所以也要带上分页参数。
再来看一下回答下的评论列表:
仔细观察后可以发现,需要展示的数据与回答及其相似,都包括:
- 评论id
- 评论内容
- 是否匿名
- 评论人信息(如果是匿名,则无需返回)
- id
- 昵称
- 头像
- 回答时间
- 评论数量(无)
- 点赞数量
- 目标用户昵称(评论特有)
从返回结果来看:相比回答列表,评论无需展示评论下的评论数量,但是需要展示目标用户的昵称,因为评论是针对某个目标的。
从查询参数来看:查询评论需要知道回答的id,这点与查询回答列表不太一样。
2.设计数据库
3.业务逻辑图
4.接口分析
综上,按照Restful的规范设计,接口信息如下:
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无
6.具体难点和亮点
就是拼接数据,没啥难度
==—–管理端-问题相关接口—–==
1.根据条件分页查询问题(管理端)—引入ES
1.原型图
在管理端后台存在问答管理列表页,与用户端类似都是分页查询,但是请求参数和返回值有较大差别:因此需要引入ES处理
从请求参数来看,除了分页参数,还包含3个:
- 问题的查看状态
- 课程名称
- 提问时间
从返回值来看,比用户端多了一些字段:
- 是否匿名: 管理端不关心,全都展示
- 提问者信息:
- 用户id
- 用户头像:匿名则不显示
- 用户 名称:匿名则不显示
- 问题标题
- 提问时间
- 回答数量
- 最近一次回答的信息:
- 回答人名称
- 回答内容
- 问题关联的课程名称
- 问题关联的章、节名称
- 问题关联课程的分类名称
2.设计数据库
3.业务逻辑图
4.接口分析
由于请求入参和返回值与用户端有较大差异,因此我们需要设计一个新的接口:
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
第一部分:
第二部分:
第三部分:
第四部分:
- 4.mapper层
无
6.具体难点和亮点
问题二:查询条件是课程名称,数据是课程id,怎么实现模糊查询?
所有上线的课程数据都会存储到
Elasticsearch
中,方便用户检索课程。并且在tj-search
模块中提供了相关的查询接口问题三:那怎么保证ES和Mysql数据一致性?
1
2
3
4
5方法一:同步双写,课程上架的时候数据写入Mysql,同步也写入ES
方法二:异步双写,课程上架的时候数据写入Mysql,发送消息给MQ,MQ通知ES更新 【项目使用】
方法三:定时同步,对于数据库新增的时候,定时批量/全量同步到ES
方法四:基于Logstash输入输出插件
方法五:基于cancal数据库增量日志解析工具,伪装主从数据库进行同步
策略 | 优点 | 缺点 |
---|---|---|
同步双写 | - 简单易实现 - 实时性高 |
- 代码侵入性强 - 存在不一致的风险 - 可能影响系统性能 |
异步双写(MQ方式) | - 解耦数据写入操作 - 通过消息队列提升性能和扩展性 |
- 系统复杂度增加 - 可能存在消息丢失的风险 - 引入了消息中间件的依赖 |
定期同步 | - 实现简单 - 无需改变现有业务逻辑 |
- 实时性差 - 可能给数据库带来额外压力 |
基于Binlog实时同步 | - 无代码侵入 - 实时性较好 - 业务逻辑与数据同步解耦 |
- 构建Binlog系统复杂 - 可能存在MQ延时风险 |
使用Canal监听Binlog同步数据到ES | - 基于MySQL的Binlog,实现数据的实时同步 - 减少系统耦合 |
- 需要维护额外的Canal服务 |
- 问题四:CaffeineCache本地缓存怎么做的?
2.隐藏/显示问题(管理端)
1.原型图
在管理端的互动问题列表中,管理员可以隐藏某个问题,这样就不会在用户端页面展示了:
2.设计数据库
3.业务逻辑图
由于interaction_question
表中有一个hidden
字段来表示是否隐藏:
因此,本质来说,这个接口是一个修改某字段值的接口,并不复杂。
4.接口分析
我们按照Restful的风格来设定,接口信息如下:
- 接口地址:
/admin/questions/{id}/hidden/{hidden}
- 请求方式:
PUT
- 请求参数: 路径占位符参数
- id:问题id
- hidden:是否隐藏
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无
6.具体难点和亮点
就是简单修改字段
3.根据id查询问题详情(管理端)
1.原型图
在管理端的问题管理页面,点击查看按钮就会进入问题详情页:
问题详情页如下:
2.设计数据库
3.业务逻辑图
可以看到,这里需要查询的数据还是比较多的,包含:
- 问题标题
- 问题描述
- 提问者信息
- id
- 昵称
- 头像
- 课程三级分类
- 课程名称
- 课程负责老师
- 课程所属章节
- 回答数量
- 用户端是否显示
返回值与管理端分页查询基本一致,多了一个课程负责老师信息。所以我们沿用之前的QuestionAdminVO
即可。但是需要添加一个课程负责老师的字段:
4.接口分析
虽然用户端也有根据id查询问题,但是返回值与用户端存在较大差异,所以我们需要另外设计一个接口。
按照Restful风格,接口信息如下:
- 接口地址:
/admin/questions/{id}
- 请求方式:
GET
- 请求参数: 路径占位符格式
- 返回值:与分页查询共享VO,这里不再赘述
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
无
6.具体难点和亮点
问题表中有一个status字段,标记管理员是否已经查看过该问题。因此每当调用根据id查询问题接口,我们可以认为管理员查看了该问题,应该将问题status标记为已查看。
==—–管理端–评论相关接口—–==
1.分页查询问答/评论列表(管理端)
1.原型图
可以看到,返回的数据格式包含:
- 评论id
- 评论内容
- 评论人信息
- id
- 昵称
- 头像
- 类型
- 回答时间
- 评论数量(回答时有)
- 点赞数量
- 目标用户昵称(评论特有)
- 是否被隐藏(管理端特有)
与用户端查询几乎完全一致。
2.设计数据库
3.业务逻辑图
与用户端查询几乎完全一致,为什么不使用同一个接口?
原因有两点:
- 管理端在统计评论数量的时候,被隐藏的评论也要统计(用户端不统计隐藏回答)
- 管理端无视匿名,所有评论都要返回用户信息;用户端匿名评论不返回用户信息。
所以在实现的时候,基本逻辑可以与用户端分页一致,但统计评论数量、处理用户信息时,需要区别对待。
4.接口分析
为了减少代码重复,大家可以对代码做改造抽取,不要重复copy代码
5.具体实现
在用户端的代码添加一个属性判断是否是真:用户端为false,管理端为true【区别:统计数量和用户信息字段】
6.具体难点和亮点
- 问题一:为什么不复用接口?
原因有两点:
①管理端在统计评论数量的时候,被隐藏的评论也要统计(用户端不统计隐藏回答)
②管理端无视匿名,所有评论都要返回用户信息;用户端匿名评论不返回用户信息。
2.隐藏/显示评论(管理端)
1.原型图
与问题类似,管理员也可以显示或隐藏某个评论或评论:
与隐藏问题类似,同样是修改hidden字段。
2.设计数据库
3.业务逻辑图
与隐藏问题类似,同样是修改hidden字段
【注意:如果隐藏的是回答,则回答下的评论也要隐藏】
4.接口分析
- 接口地址:
/admin/replies/{id}/hidden/{hidden}
- 请求方式:
PUT
- 请求参数:路径占位符参数
- id:回答或评论id
- hidden:是否被隐藏
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层