205 lines
5.7 KiB
Markdown
205 lines
5.7 KiB
Markdown
|
|
# 开发日志 - 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 中,提高查询性能
|