天机学堂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.都看到这了,给文涛点个赞支持一下呗

        你的‘赞’,是给与文涛最大的动力鸭

        有问题,可以评论区大家一起讨论

        后续会在此更新,相关问题及解决方案

posted @ 2025-11-25 12:43  yangykaifa  阅读(36)  评论(0)    收藏  举报