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

205 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 开发日志 - 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<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` - 增强版课程使用统计
- 支持参数:`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 格式的 `<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 中,提高查询性能