1.我的课表
准备阶段—业务流程
准备阶段—字段分析
【主要涉及主键id,学员id和课程id也要记录[要考虑是谁学了什么课程]】
课表要记录的是用户的学习状态,所谓学习状态就是记录谁在学习哪个课程,学习的进度如何。
其中,谁在学习哪个课程,就是一种关系。也就是说课表就是用户和课程的中间关系表。因此一定要包含三个字段:
- userId:用户id,也就是谁
- courseId:课程id,也就是学的课程
- id:唯一主键
而学习进度,则是一些附加的功能字段,页面需要哪些功能就添加哪些字段即可:
status:课程学习状态。0-未学习,1-学习中,2-已学完,3-已过期
planStatus:学习计划状态,0-没有计划,1-计划进行中
weekFreq:计划的学习频率
learnedSections:已学习小节数量,【注意:课程总小节数、课程名称、封面等可由课程id查询得出,无需重复记录】
latestSectionId:最近一次学习的小节id,方便根据id查询最近学习的课程正在学第几节
latestLearnTime:最近一次学习时间,用于分页查询的排序:
createTime和expireTime,也就是课程加入时间和过期时间
准备阶段—ER图
准备阶段—表结构
准备阶段—Mybatis-plus代码生成
参考我的Mybatis-plus笔记-代码生成步骤:
准备阶段—状态枚举
准备阶段—所有接口
==具体实现==
1.支付/报名课程后添加课表
1.原型图
2.设计数据库
1 | create table learning_lesson |
3.业务逻辑图
接下来,我们来分析一下添加课表逻辑的业务流程。首先来对比一下请求参数和数据库字段:
参数:
- Long userId
- List
courseIds
数据表:
一个userId和一个courseId是learning_lesson表中的一条数据。而订单中一个用户可能购买多个课程。因此请求参数中的courseId集合就需要逐个处理,将来会有多条课表数据。
另外,可以发现参数中只有userId和courseId,表中的其它字段都需要我们想办法来组织:
- status:课程状态,可以默认为0,代表未学习
- week_freq:学习计划频率,可以为空,代表没有设置学习计划
- plan_status:学习计划状态,默认为0,代表没有设置学习计划
- learned_sections:已学习小节数,默认0,代表没有学习
- latest_section_id:最近学习小节id,可以为空,代表最近没有学习任何小节
- latest_learn_time:最近学习时间,可以为空,代表最近没有学习
- create_time:创建时间,也就是当前时间
- expire_time:过期时间,这个要结合课程来计算。每个课程都有自己的有效期(valid_duration),因此过期时间就是create_time加上课程的有效期
- update_time:更新时间,默认当前时间,有数据库实时更新,不用管
可见在整张表中,需要我们在新增时处理的字段就剩下过期时间expire_time
了。而要知道这个就必须根据courseId查询课程的信息,找到其中的课程有效期(valid_duration
)。课程表结构如图:
因此,我们要做的事情就是根据courseId集合查询课程信息,然后分别计算每个课程的有效期,组织多个LearingLesson的数据,形成集合。最终批量新增到数据库即可。
流程如图:
其中消息发送者信息:
4.接口分析
5.具体实现
- 1.数据库设计:
- 2.准备PO和枚举类:
1 | 使用枚举类的优点: |
- 3.使用MybatisPlus生成其余基础代码:
- 4.设计MQ消费者信息
【我和课程下单统一OrderBasicDTO,主要传递orderId和courseID和userID以及完成时间】
- 5.具体逻辑
6.具体难点和亮点
问题一:课程过期时间怎么算?
课程过期时间=课程加入课程时间(当前)+课程有效期(通过传入的courseId课程id远程调用课程微服务获取media_duration有效时间)
问题二:如果这个人网络不好,重复下单怎么保证幂等性?
1.我给(courseId,userId)创建唯一索引,保证幂等性
2.我使用redis:进来的时候判断OrderId订单id是否有,有的话就重复,没有的话就存在redis[设置60s]
问题三:Id如何设计?
分为分库和不分库情况:我考虑并发就分库,然后使用雪花算法【还有其他方法,在tk实习时候考虑的那个笔记里面】
参考本文:https://mp.weixin.qq.com/s/zQNfcpCbPoo4yQFJR7FpqQ
2.分页查询我的课表
1.原型图
2.设计数据库
3.业务逻辑图
肉眼可见的字段就包含:
- 课程名称
- 课程加入课表时间
- 课程有效期结束时间
- 课程状态
- 课程已学习小节数
- 课程总小节数
- 课程是否创建了学习计划
还有一些字段是页面中没有的,但是可以从功能需要中推测出来,例如:
- 课程id:因为我们点击卡片,需要跳转到对应课程页面,必须知道课程id
- 课程封面:页面渲染时为了美观,一定会展示一个课程的封面图片
- 学习计划频率:当用户点击修改学习计划时,需要回显目前的学习计划频率
- 课表id,当前课程在课表中的对应id,当用户点击继续学习,或创建集合,需要根据课表来操作
4.接口分析
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceImp层
6.具体难点和亮点
- 问题一:如何查询避免封装时候两次for循环
使用courseList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c))转换为map,在后续直接取出来就行
3. 查看最近学习的课程(一门)
1.原型图
2.设计数据库
learning_lesson
course
course_catalogue
3.业务逻辑图
主要分为四个部分数据:
4.接口分析
5.具体实现
- 1.controller层
- 2.service层
3.serviceimpl层
4.mapper层
6.具体难点和亮点
问题一:什么是最近学习的一门课程【基本上围绕learning-lesson和course以及course-catalogue三个数据库表获取数据】
可以在学习中心位置查看最近学习的一门课程,主要是通过userId用户id查询一条课程表信息;通过课程表信息的courseId课程id查询课程的具体信息;通过课程表信息的latest_section_id最近一次学习的小节名称远程调用课程学习微服务获取(通过latest_section_id查询course-catalogue表数据);通过userId用户id来count(*)获得数据
4.根据id查询某个课程学习状态
1.原型图
在课程详情页,课程展示有两种不同形式:
- 对于未购买的课程:展示为立刻购买或加入购物车
- 对于已经购买的课程:展示为马上学习,并且显示学习的进度、有效期
2.设计数据库
3.业务逻辑图
4.接口分析
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
6.具体难点和亮点
- 问题一:查询课表的课程还是课程表的信息?
①根据courseId和UserId(两者是唯一索引,能保证只有得到一条数据)查询课表得到公共数据,针对课程具体信息要传入courseId课程id远程调用查询
5.删除课表中的课程
1.原型图
2.设计数据库
3.业务逻辑图
删除课表中的课程有两种场景:
- ①用户直接删除已失效的课程【比较简单】
- ②用户退款后触发课表自动删除【涉及发送MQ消息给learning-service服务】
现在那边退款成功之后增加步骤4[发送消息,我需要负责接受消息]
4.接口分析
这里我们可以按照Restful的规范来定义这个删除接口:
- 请求方式:删除业务的请求方式都是DELETE
- 请求路径:一般是资源名 + 标示,这里删除的是课表中的课程,因此:
/ls/lessons/{courseId}
- 请求参数:自然是路径中传递的课程id
- 返回值:无
5.具体实现
- 1.controller层
2.service层
3.serviceimpl层
- 4.mapper层
- 5.learning-service微服务接收MQ消息
6.具体难点和亮点
问题一:删除的有哪几种情况?
①根据用户下单,然后取消报名的时候发送MQ消息给learning-service微服务告知删除
②已经学习了很久,课程失效了就直接根据情况删除
6.检查课程是否有效
1.原型图
2.设计数据库
3.业务逻辑图
这是一个微服务内部接口,当用户学习课程时,可能需要播放课程视频。此时提供视频播放功能的媒资系统就需要校验用户是否有播放视频的资格
。所以,开发媒资服务(tj-media
)的同事就请你提供这样一个接口。
用户要想有播放视频的资格,那就必须满足两个条件:
- 用户课表中是否有该课程
- 课程状态是否是有效的状态(未过期)
4.接口分析
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
6.具体难点和亮点
问题一:如何判断课程是否有效
就是①判断课表是否有这个课程,②这个课程的expire过期时间是否失效了,没办法学了
7.统计课程的学习人数
1.原型图
课程微服务中需要统计每个课程的报名人数,同样是一个内部调用接口,在tj-api模块中已经定义好了:
1 | /** |
2.设计数据库
3.业务逻辑图
4.接口分析
这里我们可以按照Restful的规范来定义这个统计接口:
- 请求方式:删除业务的请求方式都是GET
- 请求路径:一般是资源名 + 标示,这里删除的是课表中的课程,因此:
/lessons/{courseId}
- 请求参数:自然是路径中传递的课程id
- 返回值:Integer学习人数
5.具体实现
- 1.controller层
- 2.service层
- 3.serviceimpl层
- 4.mapper层
6.具体难点和亮点
问题一:sql怎么写?怎么统计
1
2
3select count(user_id)
from learning_lesson
where course_id=xx 【根据课程id分类】