在昨天的学习中,我带领大家解决了天机学堂的简单的BUG。并且演示了整个项目的核心业务流程。现在,你对项目有了基本的了解,是时候动手开发一些业务功能了。
接下来接收到一个正式的开发任务:开发天机学堂项目的学习辅助相关功能。
不要小看这部分功能,作为一个在线教育项目,学习是核心。而怎样让学员有一个好的学习体验,持续有动力的学习,就显得非常关键。我们要实现的学习辅助功能,就是要起到激励、促进学员、帮助学员学习的功能,非常重要。
那么从哪里入手呢?
我们来回顾一下,在演示项目业务流程时,我们发现搜索课程、报名课程等流程都已经完成开发了。并且在《个人中心-我的订单》页面可以看到我们下单报名的课程:
通过今天的学习,我们要达成的目标如下:
-
完成我的课程表相关功能
-
学会阅读产品原型,分析需求
-
能根据需求设计接口
-
能根据需求设计数据库表
-
学会跨微服务的业务开发
1.接口设计
那么接下来,我们就一起来分析、设计、实现这些接口吧。
科程下栽の,参考资料 https://sourl.cn/sx6zLt
接口分析和设计的方法
企业开发中往往会通过一些工具来设计API接口,比如比较常见的一款API接口工具:YAPI
主要描述查询参数的基本信息,包括:
-
参数名称
-
参数是否必须
-
参数示例
-
参数描述
由于这里是查询用户集合,请求方式是GET,因此查询参数就是普通的QUERY参数,也就是路径后的?
拼接参数。如果是POST或者PUT请求,这里还可以传递更复杂的参数格式,比如FORM表单、JSON等
知道了这些,前端就知道发送请求时,要携带哪些参数了。
...................
2.数据结构
基于之前的分析,我们已经知道了业务基本流程、用户的交互行为。而用户的这些行为必然产生数据,需要保存到数据库中。这些数据在保存时必须有设定好的结构,这样才能支撑我们完成各种接口功能。
接下来,我们就分析一下课表相关的业务对应的数据结构。
ER图
我们可以结合原型图中包含的信息来画一个ER图,分析我的课表包含的信息:
表结构
基于ER图,课表对应的数据库结构应该是这样的:
CREATE TABLE learning_lesson ( id bigint NOT NULL COMMENT '主键', user_id bigint NOT NULL COMMENT '学员id', course_id bigint NOT NULL COMMENT '课程id', status tinyint DEFAULT '0' COMMENT '课程状态,0-未学习,1-学习中,2-已学完,3-已失效', week_freq tinyint DEFAULT NULL COMMENT '每周学习频率,每周3天,每天2节,则频率为6', plan_status tinyint NOT NULL DEFAULT '0' COMMENT '学习计划状态,0-没有计划,1-计划进行中', learned_sections int NOT NULL DEFAULT '0' COMMENT '已学习小节数量', latest_section_id bigint DEFAULT NULL COMMENT '最近一次学习的小节id', latest_learn_time datetime DEFAULT NULL COMMENT '最近一次学习的时间', create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', expire_time datetime NOT NULL COMMENT '过期时间', update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY idx_user_id (user_id,course_id) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生课表';
我们要创建一个名为tj_learning
的database,并且执行上面的SQL语句,创建learning_lesson
3 代码生成
在天机学堂项目中,我们使用的是Mybatis作为持久层框架,并且引入了MybatisPlus来简化开发。因此,在创建据库以后,就需要创建对应的实体类、mapper、service等。
这些代码格式固定,编写起来又比较费时。好在IDEA中提供了一个MP插件,可以生成这些重复代码
private final CatalogueClient catalogueClient;
@Override
public LearningLessonVO queryMyCurrentLesson() {
// 1.获取当前登录的用户
Long userId = UserContext.getUser();
// 2.查询正在学习的课程 select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
LearningLesson lesson = lambdaQuery()
.eq(LearningLesson::getUserId, userId)
.eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
.orderByDesc(LearningLesson::getLatestLearnTime)
.last("limit 1")
.one();
if (lesson == null) {
return null;
}
// 3.拷贝PO基础属性到VO
LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
// 4.查询课程信息
CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
if (cInfo == null) {
throw new BadRequestException("课程不存在");
}
vo.setCourseName(cInfo.getName());
vo.setCourseCoverUrl(cInfo.getCoverUrl());
vo.setSections(cInfo.getSectionNum());
// 5.统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
Integer courseAmount = lambdaQuery()
.eq(LearningLesson::getUserId, userId)
.count();
vo.setCourseAmount(courseAmount);
// 6.查询小节信息
List<CataSimpleInfoDTO> cataInfos =
catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
if (!CollUtils.isEmpty(cataInfos)) {
CataSimpleInfoDTO cataInfo = cataInfos.get(0);
vo.setLatestSectionName(cataInfo.getName());
vo.setLatestSectionIndex(cataInfo.getCIndex());
}
return vo;
}