kindergarten_java/docs/dev-logs/2026-03-20-teacher-course-usage-stats.md
En 6f64723428 feat: 教师端数据看板与学校端课程统计功能
教师端数据看板:
- 新增 TeacherDashboardResponse/TeacherLessonVO/TeacherLessonTrendVO
- 新增 TeacherWeeklyStatsResponse 周统计响应
- 新增 TeacherActivityLevel 枚举和 TeacherActivityRankResponse 活跃度排行
- 实现教师端课程统计、任务完成详情、任务反馈接口

学校端课程统计:
- 新增 CourseUsageVO/CourseUsageStatsVO/CoursePackageVO
- 新增 SchoolCourseResponse 和学校端课程使用查询接口
- 实现学校端统计数据和课程趋势接口

用户资料功能:
- 新增 UpdateProfileRequest/UpdateProfileResponse
- 实现用户资料更新接口

前后端对齐:
- 更新 OpenAPI 规范和前端 API 类型生成
- 优化 DashboardView 组件和 API 调用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 12:45:56 +08:00

5.7 KiB
Raw Permalink Blame History

开发日志 - 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

新增接口方法:

List<CourseUsageStatsVO> 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 - 增强版课程使用统计
  • 支持参数:periodTypestartDateendDate
  • 保留旧接口 /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 统计逻辑

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

测试验证

  • 后端编译通过
  • 启动后端服务测试 API
  • API 返回数据正确
  • 前端展示周期选择器
  • 切换周期数据正确刷新
  • tooltip 显示完整信息

API 测试响应示例

请求: GET /api/v1/teacher/course-usage-stats?periodType=MONTH

响应:

{
  "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 格式的 <if> 动态标签

解决方案: 使用 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 中,提高查询性能