目录
[Day03-02. 产品原型 - 分析业务流程](#Day03-02. 产品原型 - 分析业务流程)
[Day03-03. 产品原型.设计提交学习记录接口](#Day03-03. 产品原型.设计提交学习记录接口)
[Day03-04. 产品原型 - 设计查询学习记录接口](#Day03-04. 产品原型 - 设计查询学习记录接口)
[Day03-05. 产品原型 - 设计学习计划相关接口](#Day03-05. 产品原型 - 设计学习计划相关接口)
[Day03-06. 产品原型 - 设计数据库表结构](#Day03-06. 产品原型 - 设计数据库表结构)
[Day03-07. 开发接口 - 查询指定课程学习记录](#Day03-07. 开发接口 - 查询指定课程学习记录)
[Day03-08. 开发接口 - 提交学习记录的流程分析](#Day03-08. 开发接口 - 提交学习记录的流程分析)
[Day03-09. 开发接口 - 实现添加学习记录接口](#Day03-09. 开发接口 - 实现添加学习记录接口)
[问题:@Documented 这个注解有什么用 ?](#问题:@Documented 这个注解有什么用 ?)
[Day03-10. 开发接口 - 测试提交学习记录接口](#Day03-10. 开发接口 - 测试提交学习记录接口)
[Day03-11. 开发接口 - 创建学习计划](#Day03-11. 开发接口 - 创建学习计划)
[Day03-12. 开发接口 - 查询学习计划 - 分析代码流程](#Day03-12. 开发接口 - 查询学习计划 - 分析代码流程)
[Day03-13. 开发接口 - 查询我的学习计划 - 本周总的...](#Day03-13. 开发接口 - 查询我的学习计划 - 本周总的...)
[Day03-14 - 开发接口 - 查询我的学习计划 - 分页数据查询](#Day03-14 - 开发接口 - 查询我的学习计划 - 分页数据查询)
[Day03-15. 开发接口 - 测试我的学习计划接口](#Day03-15. 开发接口 - 测试我的学习计划接口)
[Day04-01. 今日课程介绍](#Day04-01. 今日课程介绍)
[Day04-02. 方案分析 - 高并发优化方案分析](#Day04-02. 方案分析 - 高并发优化方案分析)
[Day04-03. 方案分析 - 播放进度统计优化方案](#Day04-03. 方案分析 - 播放进度统计优化方案)
[Day04-04. 方案分析 - 播放进度统计数据持久化方案](#Day04-04. 方案分析 - 播放进度统计数据持久化方案)
[Day04-05. 方案分析 - 延迟任务](#Day04-05. 方案分析 - 延迟任务)
问题:解释Durationh个System.nanoTime()?
[Day04-06. 代码改造 - 添加播放记录到缓存并添加延迟任务](#Day04-06. 代码改造 - 添加播放记录到缓存并添加延迟任务)
[Day04-07. 代码改造 - 播放记录缓存的读取和清除方法](#Day04-07. 代码改造 - 播放记录缓存的读取和清除方法)
[Day04-08. 代码改造 - 异步执行延迟任务](#Day04-08. 代码改造 - 异步执行延迟任务)
[Day04-09. 代码改造 - 改造提交学习记录接口](#Day04-09. 代码改造 - 改造提交学习记录接口)
[Day04-10. 代码改造 - 测试提交学习记录接口](#Day04-10. 代码改造 - 测试提交学习记录接口)
[Day04-11. 课后思考题](#Day04-11. 课后思考题)
[问题:DelayQueue >存在的问题以及怎么优化?](#问题:DelayQueue >存在的问题以及怎么优化?)
[问题:线程池 7 大核心参数?](#问题:线程池 7 大核心参数?)

Day03-02. 产品原型 - 分析业务流程

Day03-03. 产品原型.设计提交学习记录接口

Day03-04. 产品原型 - 设计查询学习记录接口

Day03-05. 产品原型 - 设计学习计划相关接口



Day03-06. 产品原型 - 设计数据库表结构

Day03-07. 开发接口 - 查询指定课程学习记录

Day03-08. 开发接口 - 提交学习记录的流程分析


Day03-09. 开发接口 - 实现添加学习记录接口
问题:@Documented 这个注解有什么用 ?
让这个自定义注解,在生成 JavaDoc 文档时被显示出来。
具体说
它是 Java 原生的元注解(标记注解的注解)
不加
@Documented别人用 IDEA 查看你的@EnumValid时,鼠标悬停、JavaDoc 里看不到这个注解加上
@Documented生成文档、鼠标悬浮提示时,@EnumValid会完整显示在文档里
举个例子
你写了:
java
运行
@Documented public @interface EnumValid { }别人在使用时:
java
运行
@EnumValid private Integer status;在 IDEA 鼠标悬浮、或生成 JavaDoc 时,就能看到
@EnumValid这个注解,而不是被忽略。
问题:下图定义的自定义枚举是怎么实现功能的?
java/** * 用于状态的枚举校验 **/ @Documented // 生成JavaDoc时显示该注解 @Retention(RetentionPolicy.RUNTIME) // 运行时生效 @Target({ ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD }) // 可标注在:参数、字段、方法上 @Constraint(validatedBy = {EnumValidator.class, EnumValueValidator.class}) // 真正的校验逻辑由这两个类执行: // EnumValidator 校验枚举 // EnumValueValidator 校验数字是否在指定列表里 public @interface EnumValid { // 定义注解 // 校验失败时的提示信息 String message() default "不满足业务条件"; // 允许的数字列表,例如:enumeration = {0,1,2,3} int[] enumeration() default {}; // 校验分组(固定格式) Class<?>[] groups() default { }; // 负载(固定格式) Class<? extends Payload>[] payload() { }; }
@Constraint(validatedBy = ...)
绑定真正做校验的类
这里绑定了两个:
EnumValidator:校验枚举
EnumValueValidator:校验数字是否在指定列表
Day03-10. 开发接口 - 测试提交学习记录接口
Day03-11. 开发接口 - 创建学习计划

问题:下图注解作用?
@Range表示范围
@Valid的作用非常核心,简单一句话就是:让 Spring 自动校验你的 DTO 参数(LearningPlanDTO)是否合法,不合法就直接返回错误,不需要你写 if 判断。
Day03-12. 开发接口 - 查询学习计划 - 分析代码流程

Day03-13. 开发接口 - 查询我的学习计划 - 本周总的...
问题:getBaseMapper()是什么?
getBaseMapper()= 获取当前 Service 对应的 Mapper 对象 就是用来调用 Mapper 里面的自定义方法的。
问题:变成map的好处?
Map<Long, Integer> countMap = IdAndNumDTO.toMap(list);
List转Map的好处(背这个就够)一句话:为了后面循环时,不用反复查库、不用反复遍历 List,直接 O (1) 快速取值!
详细解释(结合你这段代码)
你现在的代码:
java
运行
// 1. 查询出来是 List 结构 List<IdAndNumDTO> list = recordMapper.countLearnedSections(userId, begin, end); // 2. 转成 Map<id, 数量> Map<Long, Integer> countMap = IdAndNumDTO.toMap(list);后面你要循环每一个课程 ,拿对应已学习小节数:
java
运行
for (LearningLesson r : records) { // 从map里 1 步拿到值! vo.setWeekLearnedSections(countMap.getOrDefault(r.getId(), 0));
问题:SQL语句中sum和count区别?
SUM():对列的数值做加法运算,结果是「数值总和」COUNT():统计行的数量,结果是「行数 / 非空值个数」
函数 作用 计算对象 空值处理 SUM(列名)求和 :把指定列的数值累加起来 列中的数值 遇到 NULL会自动忽略,不参与计算COUNT(列名/*)计数 :统计符合条件的行数 / 非空值数量 行 / 列的存在性 COUNT(列名)忽略NULL,COUNT(*)统计所有行(含 NULL)
Day03-14 - 开发接口 - 查询我的学习计划 - 分页数据查询
问题:需要sql查询返回是一个集合该怎么办?
设置一个实体类接受它即可
Day03-15. 开发接口 - 测试我的学习计划接口
Day04-01. 今日课程介绍

Day04-02. 方案分析 - 高并发优化方案分析




Day04-03. 方案分析 - 播放进度统计优化方案
问题:播放进度优化核心是哪里?
要关注高频,那低频的就是第一次,因为它只有一次




Day04-04. 方案分析 - 播放进度统计数据持久化方案

Day04-05. 方案分析 - 延迟任务
问题:解释Durationh个System.nanoTime()?
javapublic DelayTask(D data, Duration delayTime) { this.data = data; this.deadlineNanos = System.nanoTime() + delayTime.toNanos(); }
Duration是什么?一句话:Duration 是 Java 用来表示「一段时间」的工具类(时、分、秒、毫秒、纳秒)。
System.nanoTime()是什么?一句话:获取系统当前的「纳秒级时间戳」,专门用来做精确计时、延迟、超时判断。
问题:idea生成单元测试的快捷键?
快速生成测试类
右键菜单 :在要生成测试类的类上右键,选择 Go To -> Test。
快捷键 :使用 Ctrl + Shift + T 直接打开创建测试类的弹框。
Day04-06. 代码改造 - 添加播放记录到缓存并添加延迟任务

Day04-07. 代码改造 - 播放记录缓存的读取和清除方法
Day04-08. 代码改造 - 异步执行延迟任务
问题:下图为什么要设置为null?
- 先搞懂:MyBatis-Plus
updateById的默认行为
updateById会只更新对象中「非空字段」 ,而忽略null值的字段。
问题:Spring的生命周期?
Spring Bean 生命周期
Bean 从创建到销毁的整个过程,一共分这几步,按顺序背就行:
实例化通过构造方法创建 Bean 实例(相当于 new 了一个对象)。
属性填充 进行依赖注入,给 Bean 里的
@Autowired、@Value等赋值。执行 Aware 接口让 Bean 感知容器,比如:
BeanNameAware
BeanFactoryAware
ApplicationContextAware**前置处理(BeanPostProcessor#postProcessBeforeInitialization)**初始化之前的增强处理。
执行初始化方法
@PostConstruct标注的方法
InitializingBean#afterPropertiesSet()XML 或注解指定的
initMethod**后置处理(BeanPostProcessor#postProcessAfterInitialization)**初始化之后的增强,AOP 代理就是在这里生成的。
Bean 正常使用项目运行中被注入、被调用。
销毁容器关闭时执行:
@PreDestroy
DisposableBean#destroy()指定的
destroyMethod
一句话精简版(面试口述)
Spring Bean 生命周期:实例化 → 属性注入 → Aware 感知 → 初始化前后置处理 → 初始化方法 → 正常使用 → 销毁。
Day04-09. 代码改造 - 改造提交学习记录接口
Day04-10. 代码改造 - 测试提交学习记录接口
Day04-11. 课后思考题
问题:DelayQueue<DelayTask<RecordTaskData>>存在的问题以及怎么优化?
目前我们的延迟任务执行还是单线程模式,大家将其改造为线程池模式,核心线程数与CPU核数一致即可。
问题:线程池怎么定义及使用?
① 先定义一个规范的线程池(带名字、带拒绝策略)
javaprivate ThreadPoolExecutor poolExecutor; // 静态内部类或直接定义线程工厂 private ThreadFactory threadFactory = new ThreadFactoryBuilder() .setNameFormat("delay-task-pool-%d") .build(); @PostConstruct public void init() { // 1. 构造规范线程池 poolExecutor = new ThreadPoolExecutor( 12, 12, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), // 队列放大一点 threadFactory, // 命名线程工厂 new ThreadPoolExecutor.CallerRunsPolicy() // 更友好的拒绝策略 ); // 2. 用自己的线程池执行,而不是默认ForkJoinPool poolExecutor.execute(this::handleDelayTask); }② 加上优雅关闭
java@PreDestroy public void destroy() { if (poolExecutor != null) { poolExecutor.shutdown(); try { if (!poolExecutor.awaitTermination(60, TimeUnit.SECONDS)) { poolExecutor.shutdownNow(); } } catch (InterruptedException e) { poolExecutor.shutdownNow(); } } }
问题:线程池 7 大核心参数?
javaThreadPoolExecutor( int corePoolSize, // 核心线程数(一直存活) int maximumPoolSize, // 最大线程数(核心+非核心) long keepAliveTime, // 非核心线程空闲多久被回收 TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂(给线程起名字) RejectedExecutionHandler handler // 拒绝策略 )
本文摘要:课程内容主要围绕学习系统开发展开,重点讲解了产品原型设计、接口开发和性能优化。Day03课程详细介绍了学习记录和计划的接口设计、数据库表结构及实现过程,包括查询/提交学习记录接口开发,并讨论了自定义枚举校验的实现原理。Day04课程聚焦高并发优化方案,特别是播放进度统计的优化策略,涉及延迟任务处理、缓存机制和MyBatis-Plus的更新行为。文章还穿插讲解了Spring生命周期、SQL函数区别等关键技术点,为构建高效学习系统提供了完整的技术方案。






