当前位置: 首页 > news >正文

天机学堂—学习辅助功能(含场景问答和作业)

我的课表

需求分析

原型图

管理后台

用户端

流程图

数据设计

接口设计

  1. 支付成功报名课程后, 加入到我的课表(MQ)
  2. 分页查询我的课表
  3. 查询我正在学习的课程
  4. 根据id查询指定课程的学习状态
  5. 删除课表中的某课程

代码实现

数据表设计

添加课程到课表(非标准接口)

需求:用户购买/报名课程后,交易服务会通过MQ通知学习服务,学习服务将课程加入用户课表中

接口设计

首先写Listener,定义好RabbitMQ,对DTO数据校验后调用lessonService

@Component
@RequiredArgsConstructor
@Slf4j
public class LessonChangeListener {private final ILearningLessonService lessonService;@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "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 dto) {//1.非空判断if(dto==null || dto.getOrderId()==null || CollUtils.isEmpty(dto.getCourseIds())){log.error("接收到MQ的消息有误,订单数据为空");return;}//2.调用业务lessonService.addLesson(dto);}}
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {private final CourseClient courseClient;@Overridepublic void addLesson(OrderBasicDTO dto) {//1.远程调用tj-course微服务,根据课程id查询出课程信息(课程有效期)List<Long> courseIds = dto.getCourseIds();List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(courseIds);if (CollUtils.isEmpty(courseList)) {throw new BadRequestException("课程不存在");}//2.遍历课程信息,封装LearningLesson(用户id,课程id,过期时间)List<LearningLesson> lessonList = new ArrayList<>();for (CourseSimpleInfoDTO course : courseList) {LearningLesson lesson = new LearningLesson();//2.1 填充userId和courseIdlesson.setUserId(dto.getUserId());lesson.setCourseId(course.getId());//2.2 算出过期时间lesson.setExpireTime(LocalDateTime.now().plusMonths(course.getValidDuration())); lessonList.add(lesson);}//3.批量保存saveBatch(lessonList);}
}

Q1: 过期时间怎么计算?

加入课表的时间(LocalDateTime.now())+课程有效期

Q2: 课程信息是怎么获得的?

远程调用课程微服务tj-course,根据课程id查询课程信息

Q3: 当前添加课表的业务是怎么保证幂等性?

当前业务靠MySQL,在DDL中把(user id ,course id)设置唯一约束

通用做法:在DTO中找个唯一标识,判断Redis里面是否有

分页查询我的课表

需求: 在个人中心-我的课程页面,可以分页查询当前用户的课表及学习状态信息。

接口设计

分页用到的类如下

PageQuery

public class PageQuery {public static final Integer DEFAULT_PAGE_SIZE = 20;public static final Integer DEFAULT_PAGE_NUM = 1;@ApiModelProperty(value = "页码", example = "1")@Min(value = 1, message = "页码不能小于1")private Integer pageNo = DEFAULT_PAGE_NUM;@ApiModelProperty(value = "每页大小", example = "5")@Min(value = 1, message = "每页查询数量不能小于1")private Integer pageSize = DEFAULT_PAGE_SIZE;@ApiModelProperty(value = "是否升序", example = "true")private Boolean isAsc = true;@ApiModelProperty(value = "排序字段", example = "id")private String sortBy;
}

返回的分页结果PageDTO

public class PageDTO<T> {@ApiModelProperty("总条数")protected Long total;@ApiModelProperty("总页码数")protected Long pages;@ApiModelProperty("当前页数据")protected List<T> list;
}

接口

@RestController
@RequestMapping("/lessons")
@Api(tags = "我的课表相关的接口")
@RequiredArgsConstructor
public class LearningLessonController {private final ILearningLessonService lessonService;@GetMapping("/page")@ApiOperation("分页查询我的课表")public PageDTO<LearningLessonVO> queryMyLesson(@Validated PageQuery query){return lessonService.queryMyLesson(query);}}
    /*** 分页查询我的课表** @param query* @return*/@Overridepublic PageDTO<LearningLessonVO> queryMyLesson(PageQuery query) {// 1. 分页查询出当前用户的课表信息Long userId = UserContext.getUser();Page<LearningLesson> pageResult = lambdaQuery().eq(LearningLesson::getUserId, userId).page(query.toMpPageDefaultSortByCreateTimeDesc());List<LearningLesson> lessonList = pageResult.getRecords();if (CollUtils.isEmpty(lessonList)) {return PageDTO.empty(pageResult);}// 2. 根据courseId查询出课程信息List<Long> cIds = lessonList.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(cIds);if (CollUtils.isEmpty(courseList)) {throw new BadRequestException("课程信息不存在");}Map<Long, CourseSimpleInfoDTO> courseMap = courseList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));// 3. 遍历课表List,封装LearningLessonVOList<LearningLessonVO> voList = new ArrayList<>();for (LearningLesson lesson : lessonList) {LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);//封装课程信息CourseSimpleInfoDTO course = courseMap.get(lesson.getCourseId());if (course!=null){vo.setCourseName(course.getName());vo.setCourseCoverUrl(course.getCoverUrl());vo.setSections(course.getSectionNum());}voList.add(vo);}// 4. 封装PageDTOreturn PageDTO.of(pageResult,voList);}

Q1: 为什么把courseList转成了courseMap

Map中Key是课程id,Value是课程对象,从而可以通过课程id拿到课程对象                                                            

查询最近正在学习的课程

需求: 在首页、个人中心-课程表页,需要查询并展示当前用户最近一次学习的课程

接口设计

这次代码是个标准的三层都写案例了

@GetMapping("/now")
@ApiOperation("查询当前用户正在学习的课程")
public LearningLessonVO queryCurrent() {return lessonService.queryCurrent();
}
    /*** 查询当前用户正在学习的课程** @return*/@Overridepublic LearningLessonVO queryCurrent() {//1.获得当前用户IdLong userId = UserContext.getUser();//2.查询课表,当前用户正在学习的课程 SELECT * FROM   learning_lesson WHERE user_id =2 AND status  = 1 ORDER BY latest_learn_time  DESC  limit 0,1;LearningLesson lesson = getBaseMapper().queryCurrent(userId);if(lesson == null){return null;}LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);//3.根据课程id查询出课程信息CourseFullInfoDTO course = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);if(course == null){throw new BadRequestException("课程不存在");}vo.setCourseName(course.getName());vo.setCourseCoverUrl(course.getCoverUrl());vo.setSections(course.getSectionNum());//4.统计课程中的课程Integer courseAmount = lambdaQuery().eq(LearningLesson::getUserId, userId).count();vo.setCourseAmount(courseAmount);//5.根据最近学习的章节id查询章节信息List<CataSimpleInfoDTO> catalogueList = catalogueClient.batchQueryCatalogue(List.of(lesson.getLatestSectionId()));if(!CollUtils.isEmpty(catalogueList)){CataSimpleInfoDTO cata = catalogueList.get(0);vo.setLatestSectionIndex(cata.getCIndex());vo.setLatestSectionName(cata.getName());}return vo;}
public interface LearningLessonMapper extends BaseMapper<LearningLesson> {@Select("SELECT * FROM learning_lesson WHERE user_id = #{userId} AND status=1 ORDER BY latest_learn_time DESC limit 0,1")LearningLesson queryCurrent(@Param("userId") Long userId);
}

根据id查询指定课程的学习状态

需求: 在课程详情页需要查询用户是否购买了指定课程,如果购买了则要返回学习状态信息

接口设计

代码最简单的一集

@GetMapping("/{courseId}")
@ApiOperation("根据课程id查询课程状态")
public LearningLessonVO queryByCourseId(@PathVariable("courseId") Long courseId) {return lessonService.queryByCourseId(courseId);
}
    /*** 根据课程id查询出课程状态** @param courseId* @return*/@Overridepublic LearningLessonVO queryByCourseId(Long courseId) {// 1. 根据用户id和课程id查询出课表LearningLessonLearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, UserContext.getUser()).eq(LearningLesson::getCourseId, courseId).one();if (lesson == null) {return null;}// 2. 根据课程id查询出课程信息CourseFullInfoDTO course = courseClient.getCourseInfoById(courseId, false, false);if(course==null){throw new BadRequestException("课程不存在");}// 3. 封装voLearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);vo.setCourseName(course.getName());vo.setCourseCoverUrl(course.getCoverUrl());vo.setSections(course.getSectionNum());return vo;}

Q1: 为什么不能直接使用courseId查询课表?

还需要userid的约束

检查课程是否有效(作业)

这是一个微服务内部接口,当用户学习课程时,可能需要播放课程视频。此时提供视频播放功能的媒资系统就需要校验用户是否有播放视频的资格。所以,开发媒资服务(tj-media)的同事就请你提供这样一个接口。

    @ApiOperation("校验当前课程是否已经报名")@GetMapping("/{courseId}/valid")public Long isLessonValid(@ApiParam(value = "课程id" ,example = "1") @PathVariable("courseId") Long courseId){return lessonService.isLessonValid(courseId);}
    @Overridepublic Long isLessonValid(Long courseId) {// 1.获取登录用户Long userId = UserContext.getUser();if (userId == null) {return null;}// 2.查询课程LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, UserContext.getUser()).eq(LearningLesson::getCourseId, courseId).one();if (lesson == null) {return null;}return lesson.getId();}

删除课表中课程(作业)

删除课表中的课程有两种场景:

  • 用户直接删除已失效的课程
  • 用户退款后触发课表自动删除
    @DeleteMapping("/{courseId}")@ApiOperation("删除指定课程信息")public void deleteCourseFromLesson(@ApiParam(value = "课程id" ,example = "1") @PathVariable("courseId") Long courseId) {lessonService.deleteCourseFromLesson(null, courseId);}
@Overridepublic void deleteCourseFromLesson(Long userId, Long courseId) {// 1.获取当前登录用户if (userId == null) {userId = UserContext.getUser();}// 2.删除课程remove(buildUserIdAndCourseIdWrapper(userId, courseId));}private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>().lambda().eq(LearningLesson::getUserId, userId).eq(LearningLesson::getCourseId, courseId);return queryWrapper;}

相关文章:

天机学堂—学习辅助功能(含场景问答和作业)

我的课表 需求分析 原型图 管理后台 用户端 流程图 数据设计 接口设计 支付成功报名课程后, 加入到我的课表(MQ)分页查询我的课表查询我正在学习的课程根据id查询指定课程的学习状态删除课表中的某课程 代码实现 数据表设计 添加课程到课表&#xff08;非标准接口&#x…...

Stable Diffusion AI绘画

我们今天来了解一下最近很火的SD模型 ✨在人工智能领域&#xff0c;生成模型一直是研究的热点之一。随着深度学习技术的飞速发展&#xff0c;一种名为Stable Diffusion的新型生成模型引起了广泛关注。Stable Diffusion是一种基于概率的生成模型&#xff0c;它可以学习数据的潜…...

linux性能监控之sar

1.sar命令介绍 sar是一个非常全面的分析工具&#xff0c;可以对文件的读写&#xff0c;系统调用的使用情况&#xff0c;磁盘IO&#xff0c;CPU相关使用情况&#xff0c;内存使用情况&#xff0c;进程活动等都可以进行有效的分析。 sar工具将对系统当前的状态进行取样&am…...

react框架对Excel文件进行上传和导出

1.首先需要安装xlsx第三方的库库 引入插件 npm install xlsx在react引入 import * as XLSX from xlsx;1&#xff0c;首先设置jsx部分的 以下代码包含有导入excel文件和导出excel文件&#xff0c;读着可以根据需要&#xff0c;自己选择想要实现的功能 代码如下&#xff0…...

【前端】-【前端文件操作与文件上传】-【前端接受后端传输文件指南】

目录 前端文件操作与文件上传前端接受后端传输文件指南 前端文件操作与文件上传 一、前端文件上传有两种思路&#xff1a; 二进制blob传输&#xff1a;典型案例是formData传输&#xff0c;相当于用formData搭载二进制的blob传给后端base64传输&#xff1a;转为base64传输&…...

【IC前端虚拟项目】验证环境env与base_teat思路与编写

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 上一篇里解决了最难搞的axi_ram_model,接下来呢就会简单又常规一些了,比如这一篇要说的env和base_test的搭建。在这里我用了gen_uvm_tb脚本: 【前端验证】验证自动化脚本的最后一块拼图补全——gen_t…...

使用Remix部署智能合约到币安链(Remix的操作介绍 币安链合约的部署) 点赞收藏哦

大家好&#xff0c;我是程序员大猩猩呀。 据我所知&#xff0c;很多人进入币圈之后&#xff0c;想要通过炒币一夜暴富&#xff01;另一部分人呢他们希望自己能创建一个项目&#xff0c;然后发行自己的数字货币然后暴富。 不管是什么方式吧&#xff0c;只要不违法&#xff0c;…...

为什么Redis6.0引入了多线程

Redis 6.0引入了多线程&#xff0c;主要原因有以下几点&#xff1a; 提高网络I/O的吞吐量&#xff1a;多线程可以更有效地处理大量的并发连接和请求&#xff0c;特别是在多核心服务器上。通过使用多线程来处理读写网络套接字&#xff0c;Redis能够更充分地利用系统资源&#x…...

速盾:高防ip和高防cdn有什么相同点?

高防IP&#xff08;Dedicated IP&#xff09;和高防CDN&#xff08;Content Delivery Network&#xff09;都是用来保护网站免受各种网络攻击的技术手段&#xff0c;它们在一定程度上具有相同的作用和效果。下面将详细介绍它们的相同点。 首先&#xff0c;高防IP和高防CDN都能…...

设计模式之拦截过滤器模式

想象一下&#xff0c;在你的Java应用里&#xff0c;每个请求就像一场冒险旅程&#xff0c;途中需要经过层层安检和特殊处理。这时候&#xff0c;拦截过滤器模式就化身为你最可靠的特工团队&#xff0c;悄无声息地为每一个请求保驾护航&#xff0c;确保它们安全、高效地到达目的…...

【联通支付注册/登录安全分析报告】

联通支付注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨…...

c++ - 在循环中使用迭代器删除 unordered_set 中的元素

标签 c unordered-set 请考虑以下代码: Class MyClass 为自定义类:class MyClass { public:MyClass(int v) : Val(v) {}int Val; };然后下面的代码将在调用 it T.erase(it); 之后在循环中导致 Debug Assertion Failed: unordered_set<MyClass*> T; unordered_set<…...

深入了解哈希映射(HashMap)

一、哈希映射&#xff08;HashMap&#xff09;简介 在计算机科学中&#xff0c;哈希映射&#xff08;HashMap&#xff09;是一种基于键值对&#xff08;Key-Value pair&#xff09;存储数据的数据结构&#xff0c;它提供了高效的数据查找、插入和删除操作。哈希映射的核心思想…...

Public Key Retrieval is not allowed

Public Key Retrieval is not allowed 最近使用 JDBC 连接 MySQL 频繁出现如下报错&#xff1a; java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowe 这段代码是一个 Java 异常错误信息&#xff0c;其中包含了以下关键信息&#xff1a; 错误类…...

iphone进入恢复模式怎么退出?分享2种退出办法!

iPhone手机莫名其妙的进入到了恢复模式&#xff0c;或者是某些原因需要手机进入恢复模式&#xff0c;但是之后我们不知道如何退出恢复模式怎么办&#xff1f; 通常iPhone进入恢复模式的常见原因主要是软件问题、系统升级失败、误操作问题等导致。那iphone进入恢复模式怎么退出&…...

Leetcode 107:二叉树的层次遍历II

给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09;。 思路&#xff1a;翻转title102的结果即可。 //层次遍历二叉树public static List<List<Integ…...

LNMP一键安装包

LNMP一键安装包是什么? LNMP一键安装包是一个用Linux Shell编写的可以为CentOS/RHEL/Fedora/Debian/Ubuntu/Raspbian/Deepin/Alibaba/Amazon/Mint/Oracle/Rocky/Alma/Kali/UOS/银河麒麟/openEuler/Anolis OS Linux VPS或独立主机安装LNMP(Nginx/MySQL/PHP)、LNMPA(Nginx/MySQ…...

[机器学习-05] Scikit-Learn机器学习工具包进阶指南:协方差估计和交叉分解功能实战【2024最新】

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…...

多线程的情况下 AopContext.currentProxy()切面代理失效问题

多线程的情况下 AopContext.currentProxy()切面代理失效问题 在多线程环境下&#xff0c;AopContext.currentProxy() 可能会遇到问题&#xff0c;特别是在某些情况下&#xff0c;它无法正确地获取到当前线程的代理对象。这通常发生在以下几种情况&#xff1a; 线程不是由Spri…...

https://是怎么实现的?

默认的网站建设好后都是http访问模式&#xff0c;这种模式对于纯内容类型的网站来说&#xff0c;没有什么问题&#xff0c;但如果受到中间网络劫持会让网站轻易的跳转钓鱼网站&#xff0c;为避免这种情况下发生&#xff0c;所以传统的网站改为https协议&#xff0c;这种协议自己…...

PX4 OFFBOARD模式实战:手把手教你用C++代码让无人机自主起飞(附心跳包避坑指南)

PX4 OFFBOARD模式深度实战&#xff1a;从心跳包机制到三维轨迹控制的完整实现 当你的无人机在OFFBOARD模式下突然失控坠落&#xff0c;或者莫名其妙地退出自主控制模式时&#xff0c;是否曾怀疑过自己的代码逻辑&#xff1f;这些问题往往源于对PX4底层通信机制理解不够深入。本…...

嵌入式系统错误处理机制与实现

嵌入式系统中的错误处理机制深度解析1. 错误概念与分类1.1 错误分类体系在嵌入式系统开发中&#xff0c;错误处理是确保系统可靠性的关键环节。从严重性维度分析&#xff0c;程序错误可分为两类&#xff1a;致命性错误&#xff1a;系统无法执行恢复操作&#xff0c;典型处理方式…...

数据可视化避坑指南:当产品经理要你做Echarts版丝带图时,这3个技术难点要注意

Echarts丝带图实战&#xff1a;破解企业级数据可视化的三个高阶难题 当医药企业的销售总监盯着大屏上跳动的数字&#xff0c;突然提出"能不能做成Power BI那种丝带图效果"时&#xff0c;开发团队的沉默往往不是因为技术难度&#xff0c;而是对未知领域的本能警惕。这…...

OpenClaw备份策略详解:百川2-13B模型自动化容灾方案

OpenClaw备份策略详解&#xff1a;百川2-13B模型自动化容灾方案 1. 为什么需要自动化备份策略 去年冬天我经历过一次惨痛的教训——硬盘突然损坏导致三个月积累的模型微调数据全部丢失。那次事件后&#xff0c;我开始系统性地研究如何为本地部署的百川2-13B模型构建自动化备份…...

Paimon数据湖实战:Merge Engines深度解析与应用场景

1. Paimon数据湖中的Merge Engines核心机制 第一次接触Paimon的Merge Engines时&#xff0c;我完全被它强大的数据合并能力震撼到了。这就像是一个智能的数据管家&#xff0c;能够根据不同的业务需求&#xff0c;自动帮你处理各种复杂的数据合并场景。在实际项目中&#xff0c;…...

避坑指南:TDengine开源版taosdump备份恢复,这些性能问题和‘缺口’你得知道

TDengine开源版备份恢复实战&#xff1a;taosdump性能瓶颈与数据缺口深度解析 1. 当开源版遇上生产环境&#xff1a;taosdump的真实表现 去年夏天&#xff0c;我们团队在新能源监控项目中首次尝试用TDengine开源版构建时序数据库集群。当系统运行三个月后&#xff0c;客户突然…...

构建智能游戏AI的理想训练场:腾讯王者荣耀AI开放环境全解析

构建智能游戏AI的理想训练场&#xff1a;腾讯王者荣耀AI开放环境全解析 【免费下载链接】hok_env Honor of Kings AI Open Environment of Tencent 项目地址: https://gitcode.com/gh_mirrors/ho/hok_env 强化学习研究如何突破理论到实践的鸿沟&#xff1f;如何在真实游…...

OptiScaler终极指南:打破DLSS垄断,让所有显卡都能享受AI超分辨率

OptiScaler终极指南&#xff1a;打破DLSS垄断&#xff0c;让所有显卡都能享受AI超分辨率 【免费下载链接】OptiScaler DLSS replacement for AMD/Intel/Nvidia cards with multiple upscalers (XeSS/FSR2/DLSS) 项目地址: https://gitcode.com/GitHub_Trending/op/OptiScaler…...

AI驱动的3D建模革命:PIFuHD开源工具让零基础用户轻松创建高精度数字人

AI驱动的3D建模革命&#xff1a;PIFuHD开源工具让零基础用户轻松创建高精度数字人 【免费下载链接】pifuhd High-Resolution 3D Human Digitization from A Single Image. 项目地址: https://gitcode.com/gh_mirrors/pi/pifuhd 在数字内容创作、游戏开发和AR/VR应用领域…...

Intel Broadwell处理器选型指南:IBRS、noTSX这些后缀到底该怎么选?

Intel Broadwell处理器选型实战&#xff1a;从安全特性到性能优化的深度解析 在2014年问世的Intel Broadwell架构&#xff0c;作为第五代酷睿处理器的重要里程碑&#xff0c;至今仍在特定应用场景中保持着独特的价值。不同于简单的参数对比&#xff0c;本文将带您深入理解不同…...