天机学堂day03学习 - 指南
一、分析产品原型
1.1.分析业务流程


1.2.业务接口统计
1.2.1.提交学习记录

1.2.2.根据id查询指定课程的学习记录

1.2.3.创建学习计划

1.2.4.查询最近正在学习课程

1.2.5.抽取业务实体

1.2.6.提交学习记录

二、实现接口
2.1.查询学习记录实现
2.1.1.查询学习记录Controller

2.1.2.查询学习记录Service

2.1.3.查询学习记录ServiceImp


2.2.提交学习记录
2.2.1.提交学习记录Controller

2.2.2.提交学习记录Service

2.2.3.提交学习记录ServiceImpl
2.2.3.1.addLearningRecord

2.2.3.2.handleExamRecord

2.2.3.3.handleVideoRecord

2.2.3.4.handleLearningLessonChanges

2.2.3.5.代码实现
//1.查询学习记录Controller
@ApiOperation("查询当前用户指定课程的学习记录")
@GetMapping("/course/{courseId}")
LearningLessonDTO queryLearningRecordByCourse(
@ApiParam(value = "课程id", required = true) @PathVariable("courseId") Long courseId) {
return recordService.queryLearningRecordByCourse(courseId);
}
//2.提交学习记录Service
void addLearningRecord(LearningRecordFormDTO formDTO);
//3.提交学习记录ServiceImpl
@Override
@Transactional
public void addLearningRecord(LearningRecordFormDTO recordDTO) {
//1.获取登录用户
Long userId = UserContext.getUser();
//2.处理学习记录
boolean finished = false;
if (recordDTO.getSectionType() == SectionType.VIDEO) {
//2.1处理视频
finished = handleVideoRecord(userId, recordDTO);
} else {
//2.2处理考试
finished = handleExamRecord(userId, recordDTO);
}
//3.处理课表数据
handleLearningLessonChanges(recordDTO, finished);
}
private void handleLearningLessonChanges(LearningRecordFormDTO recordDTO, boolean finished) {
//1.查询课表
LearningLesson lesson = lessonService.getById(recordDTO.getLessonId());
if (lesson == null) {
throw new BizIllegalException("课程不存在,无法更新数据!");
}
//2.判断是否有新的完成小结
boolean allLearned = false;
if (finished) {
//3.如果有新完成的小结,需要查询课程数据
CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
if (cInfo == null) {
throw new BizIllegalException("课程不存在,无法更新数据!");
}
//4.比较课程是否全部学完:已学习小结 >= 课程总小结
allLearned = lesson.getLearnedSections() + 1 >= cInfo.getSectionNum();
}
//5.如果全部学完,则更新课表状态为已学完
lessonService.lambdaUpdate()
.set(lesson.getLearnedSections() == 0, LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
.set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED.getValue())
.set(!finished, LearningLesson::getLatestSectionId, recordDTO.getSectionId())
.set(!finished, LearningLesson::getLatestLearnTime, recordDTO.getCommitTime())
.setSql(finished, "learned_sections = learned_sections + 1")
.eq(LearningLesson::getId, lesson.getId())
.update();
}
private boolean handleVideoRecord(Long userId, LearningRecordFormDTO recordDTO) {
//1.查询旧的学习记录
LearningRecord oldRecord = lambdaQuery()
.eq(LearningRecord::getLessonId, recordDTO.getLessonId())
.eq(LearningRecord::getSectionId, recordDTO.getSectionId())
.one();
//2.判断是否存在
if (oldRecord == null) {
//3.不存在新增
//3.1转换DTO为PO
LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
//3.2.填充数据
record.setUserId(userId);
//3.写入数据库
boolean success = save(record);
if (!success) {
throw new DbException("新增学习记录失败");
}
return false;
}
//4.存在更新
//4.1.判断是否是第一次完成
boolean finished = !oldRecord.getFinished() && recordDTO.getMoment() * 2 >= recordDTO.getDuration();
//4.2.更新学习记录
boolean success = lambdaUpdate()
.set(LearningRecord::getMoment, recordDTO.getMoment())
.set(finished, LearningRecord::getFinished, true)
.set(finished, LearningRecord::getFinishTime, recordDTO.getCommitTime())
.eq(LearningRecord::getId, oldRecord.getId())
.update();
if (!success) {
throw new DbException("更新学习记录失败");
}
return finished;
}
private boolean handleExamRecord(Long userId, LearningRecordFormDTO recordDTO) {
//1.转换DTO为PO
LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
//2.填充数据
record.setUserId(userId);
record.setFinished(true);
record.setFinishTime(recordDTO.getCommitTime());
//3.写入数据库
boolean success = save(record);
if (!success) {
throw new DbException("新增考试记录失败!");
}
return true;
}
2.3.创建学习计划
2.3.1创建学习计划Controller

2.3.2创建学习计划Service

2.3.3创建学习计划ServiceImpl

2.3.4代码实现
//1.创建学习计划Controller
@ApiOperation("创建学习计划")
@PostMapping("/plans")
public void createLearningPlans(@Valid @RequestBody LearningPlanDTO planDTO){
lessonService.createLearningPlan(planDTO.getCourseId(), planDTO.getFreq());
}
//2.创建学习计划Service
void createLearningPlan(@NotNull @Min(1) Long courseId, @NotNull @Range(min = 1, max = 50) Integer freq);
//3.创建学习计划ServiceImpl
@Override
public void createLearningPlan(Long courseId, Integer freq) {
// 1.获取当前登录的用户
Long userId = UserContext.getUser();
// 2.查询课表中的指定课程有关的数据
LearningLesson lesson = queryByUserIdAndCourseId(userId, courseId);
AssertUtils.isNotNull(lesson, "课程信息不存在!");
// 3.修改数据
LearningLesson l = new LearningLesson();
l.setId(lesson.getId());
l.setWeekFreq(freq);
if(lesson.getPlanStatus() == PlanStatus.NO_PLAN) {
l.setPlanStatus(PlanStatus.PLAN_RUNNING);
}
updateById(l);
}
2.4.查询学习计划进度
2.4.1查询学习计划进度Controller

2.4.2查询学习计划进度Service

2.4.3查询学习计划进度ServiceImpl
2.4.4代码实现
//查询学习计划进度Controller
@ApiOperation("查询我的学习计划")
@GetMapping("/plans")
public LearningPlanPageVO queryMyPlans(PageQuery query){
return lessonService.queryMyPlans(query);
}
//查询学习计划进度Service
LearningPlanPageVO queryMyPlans(PageQuery query);
//查询学习计划进度ServiceImpl
@Override
public LearningPlanPageVO queryMyPlans(PageQuery query) {
LearningPlanPageVO result = new LearningPlanPageVO();
// 1.获取当前登录用户
Long userId = UserContext.getUser();
// 2.获取本周起始时间
LocalDate now = LocalDate.now();
LocalDateTime begin = DateUtils.getWeekBeginTime(now);
LocalDateTime end = DateUtils.getWeekEndTime(now);
// 3.查询总的统计数据
// 3.1.本周总的已学习小节数量
Integer weekFinished = recordMapper.selectCount(new LambdaQueryWrapper()
.eq(LearningRecord::getUserId, userId)
.eq(LearningRecord::getFinished, true)
.gt(LearningRecord::getFinishTime, begin)
.lt(LearningRecord::getFinishTime, end)
);
result.setWeekFinished(weekFinished);
// 3.2.本周总的计划学习小节数量
Integer weekTotalPlan = getBaseMapper().queryTotalPlan(userId);
result.setWeekTotalPlan(weekTotalPlan);
// TODO 3.3.本周学习积分
// 4.查询分页数据
// 4.1.分页查询课表信息以及学习计划信息
Page p = lambdaQuery()
.eq(LearningLesson::getUserId, userId)
.eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
.in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
.page(query.toMpPage("latest_learn_time", false));
List records = p.getRecords();
if (CollUtils.isEmpty(records)) {
return result.emptyPage(p);
}
// 4.2.查询课表对应的课程信息
Map cMap = queryCourseSimpleInfoList(records);
// 4.3.统计每一个课程本周已学习小节数量
List list = recordMapper.countLearnedSections(userId, begin, end);
Map countMap = IdAndNumDTO.toMap(list);
// 4.4.组装数据VO
List voList = new ArrayList<>(records.size());
for (LearningLesson r : records) {
// 4.4.1.拷贝基础属性到vo
LearningPlanVO vo = BeanUtils.copyBean(r, LearningPlanVO.class);
// 4.4.2.填充课程详细信息
CourseSimpleInfoDTO cInfo = cMap.get(r.getCourseId());
if (cInfo != null) {
vo.setCourseName(cInfo.getName());
vo.setSections(cInfo.getSectionNum());
}
// 4.4.3.每个课程的本周已学习小节数量
vo.setWeekLearnedSections(countMap.getOrDefault(r.getId(), 0));
voList.add(vo);
}
return result.pageInfo(p.getTotal(), p.getPages(), voList);
}
private Map queryCourseSimpleInfoList(List records) {
// 3.1.获取课程id
Set cIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
// 3.2.查询课程信息
List cInfoList = courseClient.getSimpleInfoList(cIds);
if (CollUtils.isEmpty(cInfoList)) {
// 课程不存在,无法添加
throw new BadRequestException("课程信息不存在!");
}
// 3.3.把课程集合处理成Map,key是courseId,值是course本身
Map cMap = cInfoList.stream()
.collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
return cMap;
}
三、课后练习题
3.1.课程过期
编写一个SpringTask定时任务,定期检查learning_lesson表中的课程是否过期,如果过期则将课程状态修改为已过期。
参考链接
天机学堂day03课后练习答案及成功测试结果(仅供参考)-CSDN博客
3.1.1开启定时任务

3.1.2创建定时器类 LessonStatusCheckTask

3.1.3代码实现
package com.tianji.learning.LessonStatusCheckTask;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.service.ILearningLessonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
@Component
@Slf4j
@RequiredArgsConstructor
public class LessonStatusCheckTask {
private final ILearningLessonService lessonService;
/**
* 检查课表中的课程状态
*/
@Scheduled(cron = "0 * * * * ?") //秒 分 时 日 月 周 (年) 每分钟执行一次
public void checkLessonStatus() {
//1.日志输出,方便观察
LocalDateTime now = LocalDateTime.now();
log.info("定时检查课表中的课程是否过期:{}", now);
//2.查询课表中所有状态为未过期的课程(不需要区分用户)
List notExpiredCourses = lessonService.list(Wrappers.lambdaQuery()
.ne(LearningLesson::getStatus, LessonStatus.EXPIRED)); //ne表示不等于(not equal)
//3.遍历所有未过期的课程,判断是否过期(当前时间在过期时间之后)
for (LearningLesson notExpiredCourse : notExpiredCourses) {
if (now.isAfter(notExpiredCourse.getExpireTime())) {
notExpiredCourse.setStatus(LessonStatus.EXPIRED);
}
}
//4.批量更新课程状态
lessonService.updateBatchById(notExpiredCourses);
}
}
3.2.方案思考
思考题:思考一下目前提交学习记录功能可能存在哪些问题?有哪些可以改进的方向?
四、遇到的问题
4.1.提交学习记录
4.1.1.无法接受到前端心跳请求
不要用java泛型那个课去测试,java泛型是试看类型课程,前端对试看内容不进行心跳记录提交,测试的时候要用课程2这个课测试
4.2.都看到这了,给文涛点个赞支持一下呗
你的‘赞’,是给与文涛最大的动力鸭
有问题,可以评论区大家一起讨论
后续会在此更新,相关问题及解决方案

浙公网安备 33010602011771号