# 开发日志 - 2026-03-20 ## 教师端课程使用统计功能实现 ### 实现内容 实现了教师端课程使用统计功能(增强版),支持按时间周期筛选和更多维度的统计。 ### 后端改动 #### 1. 新增 DTO 类 **CourseUsageQuery.java** - 请求参数 DTO - `periodType`: 统计周期类型(TODAY/WEEK/MONTH/CUSTOM) - `startDate`: 自定义周期开始日期 - `endDate`: 自定义周期结束日期 **CourseUsageStatsVO.java** - 响应对象 DTO(增强版) - `coursePackageId`: 课程包 ID - `coursePackageName`: 课程包名称 - `usageCount`: 使用次数(基于实际授课记录统计) - `studentCount`: 参与学生数(去重) - `avgDuration`: 平均时长(分钟) - `lastUsedAt`: 最后使用时间 #### 2. LessonMapper.java 新增 `getCourseUsageStats()` 方法: - 使用自定义 SQL 统计课程包使用情况 - 基于 `lesson` 表实际授课记录 - 支持时间范围筛选 - 支持教师 ID 筛选 - 只统计 `COMPLETED` 状态的课程 - 返回 TOP 10 课程包 #### 3. TeacherStatsService.java 新增接口方法: ```java List getCourseUsageStats(Long tenantId, Long teacherId, CourseUsageQuery query); ``` #### 4. TeacherStatsServiceImpl.java 实现 `getCourseUsageStats()` 方法: - 调用 `calculateTimeRange()` 计算时间范围 - 支持四种周期类型:TODAY、WEEK、MONTH、CUSTOM - 调用 Mapper 获取统计数据 实现 `calculateTimeRange()` 辅助方法: - TODAY: 当天 00:00:00 至今 - WEEK: 本周一至今 - MONTH: 本月 1 号至今 - CUSTOM: 自定义日期范围 #### 5. TeacherStatsController.java 新增接口: - `GET /api/v1/teacher/course-usage-stats` - 增强版课程使用统计 - 支持参数:`periodType`、`startDate`、`endDate` - 保留旧接口 `/api/v1/teacher/course-usage`(标记为 @Deprecated) ### 前端改动 #### 1. src/api/teacher.ts 新增类型定义: - `CourseUsageQueryParams` - 查询参数类型 - `CourseUsageStatsItem` - 响应数据类型 新增 API 方法: - `getTeacherCourseUsageStats()` - 获取增强版课程使用统计 保留旧 API(向后兼容): - `getTeacherCourseUsage()` #### 2. src/views/teacher/DashboardView.vue **新增响应式数据**: - `usagePeriodType`: 当前选择的周期类型 - `courseUsageStatsData`: 增强版课程使用统计数据 - `periodOptions`: 周期选项(今日/本周/本月) **UI 改动**: - 在课程使用卡片添加周期选择器(a-segmented) - 用户可通过点击切换统计周期 **图表增强**: - `initUsageChart()` 支持新旧两种数据类型 - tooltip 显示更详细信息: - 使用次数 - 参与学生数 - 平均时长 - 最后使用时间 **数据加载**: - `loadUsageData()` 调用新 API `getTeacherCourseUsageStats()` - 周期切换时自动重新加载数据 ### 统计逻辑 1. **使用次数**:统计周期内 COMPLETED 状态的 lesson 数量 2. **参与学生数**:去重统计 student_record 中的 student_id 3. **平均时长**:计算 lesson 的 start_datetime 到 end_datetime 的分钟差平均值 4. **最后使用时间**:最近一次授课完成时间 ### 数据流向 ``` 用户选择周期 → 前端调用 API → Controller 接收参数 → Service 计算时间范围 → Mapper 执行 SQL 统计 → 返回 CourseUsageStatsVO 列表 → 前端图表展示 ``` ### SQL 统计逻辑 ```sql SELECT cp.id AS coursePackageId, cp.name AS coursePackageName, COUNT(l.id) AS usageCount, COUNT(DISTINCT sr.student_id) AS studentCount, AVG(TIMESTAMPDIFF(MINUTE, l.start_datetime, l.end_datetime)) AS avgDuration, MAX(l.end_datetime) AS lastUsedAt FROM lesson l INNER JOIN course_package cp ON l.course_id = cp.id LEFT JOIN student_record sr ON l.id = sr.lesson_id WHERE l.tenant_id = #{tenantId} AND l.end_datetime >= #{startTime} AND l.end_datetime <= #{endTime} AND l.status = 'COMPLETED' AND l.teacher_id = #{teacherId} GROUP BY cp.id, cp.name ORDER BY usageCount DESC LIMIT 10 ``` ### 测试验证 - [x] 后端编译通过 ✅ - [x] 启动后端服务测试 API ✅ - [x] API 返回数据正确 ✅ - [ ] 前端展示周期选择器 - [ ] 切换周期数据正确刷新 - [ ] tooltip 显示完整信息 ### API 测试响应示例 **请求**: `GET /api/v1/teacher/course-usage-stats?periodType=MONTH` **响应**: ```json { "code": 200, "message": "操作成功", "data": [ { "coursePackageId": "17", "coursePackageName": "课程简介课程简介课程简介课程简介课程简介课程简介课程简介", "usageCount": 15, "studentCount": 5, "avgDuration": 0, "lastUsedAt": "2026-03-16T00:00:00" }, { "coursePackageId": "16", "coursePackageName": "T 色他", "usageCount": 1, "studentCount": 0, "avgDuration": 0, "lastUsedAt": "2026-03-16T00:00:00" } ] } ``` ### 技术难点解决 **问题**: MyBatis `@SelectProvider` 不支持 XML 格式的 `` 动态标签 **解决方案**: 使用 SQL 条件 `AND (#{teacherId} IS NULL OR l.teacher_id = #{teacherId})` 替代动态 SQL,实现可选参数过滤。 ### 文件清单 **后端新增**: - `dto/request/CourseUsageQuery.java` - `dto/response/CourseUsageStatsVO.java` **后端修改**: - `mapper/LessonMapper.java` - `service/TeacherStatsService.java` - `service/impl/TeacherStatsServiceImpl.java` - `controller/teacher/TeacherStatsController.java` **前端修改**: - `src/api/teacher.ts` - `src/views/teacher/DashboardView.vue` ### 后续优化建议 1. **前端展示优化**:可以考虑将饼图改为条形图,更适合展示 TOP 排行 2. **导出功能**:支持导出统计数据为 Excel 3. **更多维度**:增加课程类型分布、班级使用对比等 4. **缓存优化**:统计数据可以缓存在 Redis 中,提高查询性能