扶贫网站建设方案,学做家常菜去那个网站,现在网络推广有哪些平台,网上商店建设前准备开发接口
添加课程到课表
需求分析: 用户购买课程后#xff0c;交易服务会通过MQ通知学习服务#xff0c;学习服务将课程加入用户课表中
接下来#xff0c;我们来分析一下添加课表逻辑的业务流程。首先来对比一下请求参数和数据库字段#xff1a; 一个userId和一个cours…开发接口添加课程到课表需求分析: 用户购买课程后交易服务会通过MQ通知学习服务学习服务将课程加入用户课表中接下来我们来分析一下添加课表逻辑的业务流程。首先来对比一下请求参数和数据库字段一个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的数据形成集合。最终批量新增到数据库即可。流程如图那么问题来了我们该如何根据课程id查询课程信息呢课程course的信息是由课程服务course-service来维护的目前已经开发完成并部署到了虚拟机的开发环境中。我们现在需要查询课程信息自然需要调用课程服务暴露的Feign接口。如果没有这样的接口则需要联系维护该服务的同事协商开发相关接口。在咱们的项目中课程微服务已经暴露了一些接口。我们有三种方式可以查看已经开放的接口与开发的同事交流沟通通过网关中的Swagger文档来查看直接查看课程服务的源码首先我们来看一下swagger文档不过这种方式查看到的接口数量非常多有很多是给前端用的。不一定有对应的Feign接口。要查看Feign接口需要到tj-api中查看检索其中的API可以发现一个这样的接口根据id批量查询课程的基本信息而在课程基本信息CourseSimpleInfoDTO中就有有效期信息功能实现我们在tj-learning服务中定义一个MQ的监听器package com.tianji.learning.mq; Slf4j Component RequiredArgsConstructor public class LessonChangeListener { private final ILearningLessonService lessonService; /** * 监听订单支付或课程报名的消息 * param order 订单信息 */ RabbitListener(bindings QueueBinding( value Queue(value learning.lesson.pay.queue, durable true), exchange Exchange(name MqConstants.Exchange.ORDER_EXCHANGE, type ExchangeTypes.TOPIC), key MqConstants.Key.ORDER_PAY_KEY )) public void listenLessonPay(OrderBasicDTO order){ // 1.健壮性处理 if(order null || order.getUserId() null || CollUtils.isEmpty(order.getCourseIds())){ // 数据有误无需处理 log.error(接收到MQ消息有误订单数据为空); return; } // 2.添加课程 log.debug(监听到用户{}的订单{}需要添加课程{}到课表中, order.getUserId(), order.getOrderId(), order.getCourseIds()); lessonService.addUserLessons(order.getUserId(), order.getCourseIds()); } }订单中与课表有关的字段就是userId、courseId因此这里要传递的就是这两个参数。注意这里添加课程的核心逻辑是在ILearningLessonService中实现的首先是接口声明package com.tianji.learning.service; /** * p * 学生课程表 服务类 * /p */ public interface ILearningLessonService extends IServiceLearningLesson { void addUserLessons(Long userId, ListLong courseIds); }然后是对应的实现类Service public class LearningLessonServiceImpl extends ServiceImplLearningLessonMapper, LearningLesson implements ILearningLessonService { Override public void addUserLessons(Long userId, ListLong courseIds) { // TODO 添加课程信息到用户课程表 } }现在我们正式实现LearningLessonServiceImpl中的addUserLessons方法package com.tianji.learning.service.impl; // 略 SuppressWarnings(ALL) Service RequiredArgsConstructor Slf4j public class LearningLessonServiceImpl extends ServiceImplLearningLessonMapper, LearningLesson implements ILearningLessonService { private final CourseClient courseClient; Override Transactional public void addUserLessons(Long userId, ListLong courseIds) { // 1.查询课程有效期 ListCourseSimpleInfoDTO cInfoList courseClient.getSimpleInfoList(courseIds); if (CollUtils.isEmpty(cInfoList)) { // 课程不存在无法添加 log.error(课程信息不存在无法添加到课表); return; } // 2.循环遍历处理LearningLesson数据 ListLearningLesson list new ArrayList(cInfoList.size()); for (CourseSimpleInfoDTO cInfo : cInfoList) { LearningLesson lesson new LearningLesson(); // 2.1.获取过期时间 Integer validDuration cInfo.getValidDuration(); if (validDuration ! null validDuration 0) { LocalDateTime now LocalDateTime.now(); lesson.setCreateTime(now); lesson.setExpireTime(now.plusMonths(validDuration)); } // 2.2.填充userId和courseId lesson.setUserId(userId); lesson.setCourseId(cInfo.getId()); list.add(lesson); } // 3.批量新增 saveBatch(list); } }功能测试检查队列检查交换机修改该课程的结束时间, 不然报名会失败, course表, java泛型课程查看消息: 表示消费成功查看日志查看数据库登录用户传递流程天机学堂是基于JWT实现登录的登录信息就保存在请求头的token中。因此要获取当前登录用户只要获取请求头解析其中的token即可。但是每个微服务都可能需要登录用户信息在每个微服务都做token解析就属于重复编码了。因此我们的把token解析的行为放到了网关中然后由网关把用户信息放入请求头传递给下游微服务。每个微服务要从请求头拿出用户信息在业务中使用也比较麻烦所以我们定义了一个HandlerInterceptor拦截进入微服务的请求并获取用户信息存入UserContext底层基于ThreadLocal。这样后续的业务处理时就能直接从UserContext中获取用户了以上就是天机学堂中获取用户信息的基本实现思路。查看代码: 以后需要用户ID时, 使用UserContext.getUser();工具类获取分页查询我的课表需求: 在个人中心-我的课程页面可以分页查询用户的课表及学习状态信息接口信息课表VO属性分析万事具备接下来根据我们分析的接口来定义和实现接口。package com.tianji.learning.controller; /** * p * 学生课程表 前端控制器 * /p * * author author * since 2025-11-26 */ RestController RequestMapping(/lessons) Api(tags 我的课程相关接口) RequiredArgsConstructor public class LearningLessonController { private final ILearningLessonService lessonService; GetMapping(/page) ApiOperation(分页查询我的课程列表) public PageDTOLearningLessonVO pageMyLessons(PageQuery query) { return lessonService.queryMyLessons(query); } }package com.tianji.learning.service; /** * p * 学生课程表 服务类 * /p * * author author * since 2025-11-26 */ public interface ILearningLessonService extends IServiceLearningLesson { PageDTOLearningLessonVO queryMyLessons(PageQuery query); }package com.tianji.learning.service.impl; /** * p * 学生课程表 服务实现类 * /p * * author author * since 2025-11-26 */ Service RequiredArgsConstructor Slf4j public class LearningLessonServiceImpl extends ServiceImplLearningLessonMapper, LearningLesson implements ILearningLessonService { private final CourseClient courseClient; Override public PageDTOLearningLessonVO queryMyLessons(PageQuery query) { // 获取当前登录用户 Long userId UserContext.getUser(); // 分页查询 // select * from learning_lesson where user_id ? order by latest_learn_time desc limit ?,? PageLearningLesson page lambdaQuery() .eq(LearningLesson::getUserId, userId) .page(query.toMpPage(latest_learn_time, false)); ListLearningLesson records page.getRecords(); if(CollUtils.isEmpty(records)) { return PageDTO.empty(page); } // 查询课程信息 SetLong cIds records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet()); ListCourseSimpleInfoDTO cInfoList courseClient.getSimpleInfoList(cIds); if (CollUtils.isEmpty(cInfoList)) { throw new BadRequestException(课程信息不存在); } MapLong, CourseSimpleInfoDTO cMap cInfoList.stream() .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c - c)); // 封装VO返回 ArrayListLearningLessonVO list new ArrayList(records.size()); for (LearningLesson r : records) { LearningLessonVO vo BeanUtils.copyBean(r, LearningLessonVO.class); CourseSimpleInfoDTO cInfo cMap.get(r.getCourseId()); vo.setCourseName(cInfo.getName()); vo.setCourseCoverUrl(cInfo.getCoverUrl()); vo.setSections(cInfo.getSectionNum()); list.add(vo); } return PageDTO.of(page, list); } }接口测试由于我使用的JDK17, 访问接口后b报错:Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.lang.Class java.lang.invoke.SerializedLambda.capturingClass accessible: module java.base does not opens java.lang.invoke to unnamed module这表明Java不允许访问SerializedLambda类的私有字段。问题出现在MyBatis-Plus的SerializedLambdaMeta类初始化过程中它尝试通过反射设置字段可访问。在启动应用时添加以下JVM参数--add-opens java.base/java.lang.invokeALL-UNNAMED如果你使用的是IDE如IntelliJ IDEA可以在运行配置的VM options中添加这个参数。如果是Maven项目可以在pom.xml中配置plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration jvmArguments--add-opens java.base/java.lang.invokeALL-UNNAMED/jvmArguments /configuration /plugin继续测试前端页面查询我正在学习的课程package com.tianji.learning.controller; /** * p * 学生课程表 前端控制器 * /p * * author author * since 2025-11-26 */ RestController RequestMapping(/lessons) Api(tags 我的课程相关接口) RequiredArgsConstructor public class LearningLessonController { private final ILearningLessonService lessonService; GetMapping(/now) ApiOperation(查询我正在学习的课程) public LearningLessonVO queryMyCurrentLesson() { return lessonService.queryMyCurrentLesson(); } }package com.tianji.learning.service; /** * p * 学生课程表 服务类 * /p * * author author * since 2025-11-26 */ public interface ILearningLessonService extends IServiceLearningLesson { LearningLessonVO queryMyCurrentLesson(); }package com.tianji.learning.service.impl; /** * p * 学生课程表 服务实现类 * /p * * author author * since 2025-11-26 */ Service RequiredArgsConstructor Slf4j public class LearningLessonServiceImpl extends ServiceImplLearningLessonMapper, LearningLesson implements ILearningLessonService { 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.查询小节信息 ListCataSimpleInfoDTO 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; } }代码提交提交代码到远程仓库把开发分支合并到dev分支先切换到dev分支, 再把开发分支合并到dev提交刚合并过来的代码删除开发分支查看tjxt-dev-build是否构建构建完成启动tj-learning服务, 简单测试