2026-03-21 12:45:56 +08:00
|
|
|
|
# 开发日志 2026-03-21
|
|
|
|
|
|
|
|
|
|
|
|
## 学校端 - 课程使用统计功能实现
|
|
|
|
|
|
|
|
|
|
|
|
### 需求背景
|
|
|
|
|
|
|
|
|
|
|
|
学校端 Dashboard 页面已有"课程使用统计"卡片组件,但后端返回空数据。需要实现该功能,让学校管理员能够查看本校各课程包的使用情况。
|
|
|
|
|
|
|
|
|
|
|
|
### 实现内容
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. 后端修改
|
|
|
|
|
|
|
|
|
|
|
|
**Controller 层** (`SchoolStatsController.java`)
|
|
|
|
|
|
|
|
|
|
|
|
- 添加日期范围参数支持,允许前端传入 `startDate` 和 `endDate`
|
|
|
|
|
|
- 不传参数时默认统计本月数据
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@GetMapping("/courses")
|
|
|
|
|
|
@Operation(summary = "获取课程使用统计")
|
|
|
|
|
|
public Result<List<Map<String, Object>>> getCourseUsageStats(
|
|
|
|
|
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
|
|
|
|
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
|
|
|
|
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
|
|
|
|
|
return Result.success(schoolStatsService.getCourseUsageStats(tenantId, startDate, endDate));
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Service 接口** (`SchoolStatsService.java`)
|
|
|
|
|
|
|
|
|
|
|
|
- 更新方法签名,添加日期参数
|
|
|
|
|
|
- 添加 `LocalDate` 导入
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
List<Map<String, Object>> getCourseUsageStats(Long tenantId, LocalDate startDate, LocalDate endDate);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Service 实现** (`SchoolStatsServiceImpl.java`)
|
|
|
|
|
|
|
|
|
|
|
|
- 使用 `LessonMapper.getCourseUsageStats()` 查询实际数据
|
|
|
|
|
|
- 支持日期范围筛选,默认统计本月
|
|
|
|
|
|
- 将 `CourseUsageStatsVO` 转换为 `Map` 格式返回
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public List<Map<String, Object>> getCourseUsageStats(Long tenantId, LocalDate startDate, LocalDate endDate) {
|
|
|
|
|
|
// 计算时间范围
|
|
|
|
|
|
LocalDateTime startTime;
|
|
|
|
|
|
LocalDateTime endTime;
|
|
|
|
|
|
|
|
|
|
|
|
if (startDate != null) {
|
|
|
|
|
|
startTime = startDate.atStartOfDay();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
startTime = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (endDate != null) {
|
|
|
|
|
|
endTime = endDate.atTime(23, 59, 59);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
endTime = LocalDateTime.now();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调用 Mapper 查询
|
|
|
|
|
|
List<CourseUsageStatsVO> stats = lessonMapper.getCourseUsageStats(
|
|
|
|
|
|
tenantId, null, startTime, endTime
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为 Map 格式返回
|
|
|
|
|
|
return stats.stream().map(vo -> {
|
|
|
|
|
|
Map<String, Object> map = new HashMap<>();
|
|
|
|
|
|
map.put("courseId", vo.getCoursePackageId());
|
|
|
|
|
|
map.put("courseName", vo.getCoursePackageName());
|
|
|
|
|
|
map.put("usageCount", vo.getUsageCount());
|
|
|
|
|
|
map.put("studentCount", vo.getStudentCount() != null ? vo.getStudentCount() : 0);
|
|
|
|
|
|
map.put("avgDuration", vo.getAvgDuration() != null ? vo.getAvgDuration() : 0);
|
|
|
|
|
|
map.put("lastUsedAt", vo.getLastUsedAt());
|
|
|
|
|
|
return map;
|
|
|
|
|
|
}).collect(Collectors.toList());
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. 前端修改
|
|
|
|
|
|
|
|
|
|
|
|
**API 层** (`src/api/school.ts`)
|
|
|
|
|
|
|
|
|
|
|
|
- 更新 `getCourseUsageStats` 支持日期参数
|
|
|
|
|
|
- 增强返回类型定义
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
export const getCourseUsageStats = (startDate?: string, endDate?: string) => {
|
|
|
|
|
|
const params: Record<string, string> = {};
|
|
|
|
|
|
if (startDate) params.startDate = startDate;
|
|
|
|
|
|
if (endDate) params.endDate = endDate;
|
|
|
|
|
|
return http.get<Array<{
|
|
|
|
|
|
courseId: number;
|
|
|
|
|
|
courseName: string;
|
|
|
|
|
|
usageCount: number;
|
|
|
|
|
|
studentCount?: number;
|
|
|
|
|
|
avgDuration?: number;
|
|
|
|
|
|
lastUsedAt?: string;
|
|
|
|
|
|
}>>('/v1/school/stats/courses', { params });
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**组件层** (`src/views/school/DashboardView.vue`)
|
|
|
|
|
|
|
|
|
|
|
|
- `loadCourseStats` 函数传递日期范围参数
|
|
|
|
|
|
- `onMounted` 中设置默认日期范围为当月
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
const loadCourseStats = async () => {
|
|
|
|
|
|
courseStatsLoading.value = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const startDate = dateRange.value?.[0]?.format('YYYY-MM-DD');
|
|
|
|
|
|
const endDate = dateRange.value?.[1]?.format('YYYY-MM-DD');
|
|
|
|
|
|
const data = await getCourseUsageStats(startDate, endDate);
|
|
|
|
|
|
courseStats.value = data.slice(0, 10);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to load course stats:', error);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
courseStatsLoading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
// ... 其他初始化
|
|
|
|
|
|
// 设置默认日期范围为当月
|
|
|
|
|
|
const now = dayjs();
|
|
|
|
|
|
const monthStart = now.startOf('month');
|
|
|
|
|
|
const monthEnd = now.endOf('month');
|
|
|
|
|
|
dateRange.value = [monthStart, monthEnd];
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 数据来源
|
|
|
|
|
|
|
|
|
|
|
|
课程使用统计基于 `lesson` 表的实际授课记录统计:
|
|
|
|
|
|
|
|
|
|
|
|
- **usageCount**: 统计周期内 COMPLETED 状态的 lesson 数量
|
|
|
|
|
|
- **studentCount**: 参与学生数(去重统计 student_record 中的 student_id)
|
|
|
|
|
|
- **avgDuration**: 平均时长(lesson 的 start_datetime 到 end_datetime 的分钟差平均值)
|
|
|
|
|
|
- **lastUsedAt**: 最后使用时间(最近一次授课完成时间)
|
|
|
|
|
|
|
|
|
|
|
|
### 文件变更列表
|
|
|
|
|
|
|
|
|
|
|
|
| 文件 | 变更说明 |
|
|
|
|
|
|
|------|---------|
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolStatsController.java` | 添加日期范围参数 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/service/SchoolStatsService.java` | 更新方法签名 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolStatsServiceImpl.java` | 实现实际查询逻辑 |
|
|
|
|
|
|
| `reading-platform-frontend/src/api/school.ts` | 更新 API 函数和类型 |
|
|
|
|
|
|
| `reading-platform-frontend/src/views/school/DashboardView.vue` | 传递日期参数,设置默认日期范围 |
|
|
|
|
|
|
|
|
|
|
|
|
### 测试验证
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] 后端编译通过
|
|
|
|
|
|
- [ ] 启动后端服务(端口 8480)
|
|
|
|
|
|
- [ ] 启动前端服务(端口 5173)
|
|
|
|
|
|
- [ ] 登录学校管理员账号
|
|
|
|
|
|
- [ ] 访问 Dashboard 页面,查看"课程使用统计"卡片
|
|
|
|
|
|
- [ ] 验证日期范围筛选功能
|
|
|
|
|
|
- [ ] 验证数据显示正确性
|
|
|
|
|
|
|
|
|
|
|
|
### 后续优化建议
|
|
|
|
|
|
|
|
|
|
|
|
1. **增加更多统计维度**:按班级、按教师统计
|
|
|
|
|
|
2. **可视化增强**:趋势图、热力图
|
|
|
|
|
|
3. **导出功能**:Excel 导出课程使用明细
|
|
|
|
|
|
4. **性能优化**:大数据量时考虑缓存
|
2026-03-21 18:43:47 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 学校端 - 数据导出功能实现
|
|
|
|
|
|
|
|
|
|
|
|
### 需求背景
|
|
|
|
|
|
学校端数据概览页面(DashboardView.vue)已有数据导出的 UI 界面和前端调用逻辑,但后端 `SchoolExportController.java` 中的 4 个接口只返回占位数据,没有实际的 Excel 导出功能。需要实现学校端数据概览的导出功能,包括:
|
|
|
|
|
|
1. **授课记录导出** - 导出指定时间范围内的授课记录
|
|
|
|
|
|
2. **教师绩效导出** - 导出教师绩效统计数据
|
|
|
|
|
|
3. **学生统计导出** - 导出学生统计数据
|
|
|
|
|
|
|
|
|
|
|
|
### 实现内容
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. 添加依赖
|
|
|
|
|
|
在 `pom.xml` 中添加 EasyExcel 3.3.4:
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<dependency>
|
|
|
|
|
|
<groupId>com.alibaba</groupId>
|
|
|
|
|
|
<artifactId>easyexcel</artifactId>
|
|
|
|
|
|
<version>3.3.4</version>
|
|
|
|
|
|
</dependency>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. 新建 DTO 类
|
|
|
|
|
|
- `dto/response/LessonExportVO.java` - 授课记录导出 VO(9 个字段)
|
|
|
|
|
|
- `dto/response/TeacherPerformanceExportVO.java` - 教师绩效导出 VO(7 个字段)
|
|
|
|
|
|
- `dto/response/StudentStatExportVO.java` - 学生统计导出 VO(6 个字段)
|
|
|
|
|
|
|
|
|
|
|
|
#### 3. 新建 Service
|
|
|
|
|
|
- `service/SchoolExportService.java` - 导出服务接口
|
|
|
|
|
|
- `service/impl/SchoolExportServiceImpl.java` - 导出服务实现
|
|
|
|
|
|
|
|
|
|
|
|
#### 4. 扩展 Mapper
|
|
|
|
|
|
在 `LessonMapper.java` 中添加 3 个导出查询方法:
|
|
|
|
|
|
- `selectExportData()` - 授课记录查询
|
|
|
|
|
|
- `selectTeacherExportData()` - 教师绩效查询
|
|
|
|
|
|
- `selectStudentExportData()` - 学生统计查询
|
|
|
|
|
|
|
|
|
|
|
|
#### 5. 修改 Controller
|
|
|
|
|
|
`controller/school/SchoolExportController.java` - 实现 3 个导出接口:
|
|
|
|
|
|
- `GET /api/v1/school/export/lessons` - 授课记录导出
|
|
|
|
|
|
- `GET /api/v1/school/export/teacher-stats` - 教师绩效导出
|
|
|
|
|
|
- `GET /api/v1/school/export/student-stats` - 学生统计导出
|
|
|
|
|
|
|
|
|
|
|
|
#### 6. 修改前端 API
|
|
|
|
|
|
`src/api/school.ts` - 增强导出函数,处理空数据时返回的 JSON 响应
|
|
|
|
|
|
|
|
|
|
|
|
### 技术要点
|
|
|
|
|
|
|
|
|
|
|
|
1. **EasyExcel 导出**:使用 `@ExcelProperty` 注解定义 Excel 列名,支持自动列宽
|
|
|
|
|
|
2. **空数据处理**:当没有数据时返回 JSON 响应 `Result.error(404, "暂无数据")`
|
|
|
|
|
|
3. **响应类型判断**:前端通过 `content-type` 头区分 Excel 和 JSON 响应
|
|
|
|
|
|
4. **中文文件名**:使用 `URLEncoder` 编码确保中文文件名正确下载
|
|
|
|
|
|
5. **权限控制**:通过 `@RequireRole(UserRole.SCHOOL)` 确保只能导出当前租户数据
|
|
|
|
|
|
|
|
|
|
|
|
### 字段设计
|
|
|
|
|
|
|
|
|
|
|
|
#### 授课记录导出
|
|
|
|
|
|
| Excel 列名 | 数据来源 |
|
|
|
|
|
|
|-----------|----------|
|
|
|
|
|
|
| 授课日期 | lesson.lesson_date |
|
|
|
|
|
|
| 授课时间 | lesson.start_time ~ end_time |
|
|
|
|
|
|
| 班级名称 | clazz.name |
|
|
|
|
|
|
| 教师姓名 | teacher.name |
|
|
|
|
|
|
| 课程名称 | course_package.name |
|
|
|
|
|
|
| 学生人数 | COUNT(DISTINCT sr.student_id) |
|
|
|
|
|
|
| 平均参与度 | AVG(sr.participation) |
|
|
|
|
|
|
| 备注 | lesson.notes |
|
|
|
|
|
|
|
|
|
|
|
|
#### 教师绩效导出
|
|
|
|
|
|
| Excel 列名 | 数据来源 |
|
|
|
|
|
|
|-----------|----------|
|
|
|
|
|
|
| 教师姓名 | teacher.name |
|
|
|
|
|
|
| 所属班级 | GROUP_CONCAT(clazz.name) |
|
|
|
|
|
|
| 授课次数 | COUNT(lesson.id) |
|
|
|
|
|
|
| 课程数量 | COUNT(DISTINCT course_id) |
|
|
|
|
|
|
| 活跃等级 | CASE WHEN (HIGH/MEDIUM/LOW/INACTIVE) |
|
|
|
|
|
|
| 最后活跃时间 | MAX(end_datetime) |
|
|
|
|
|
|
| 平均参与度 | AVG(sr.participation) |
|
|
|
|
|
|
|
|
|
|
|
|
#### 学生统计导出
|
|
|
|
|
|
| Excel 列名 | 数据来源 |
|
|
|
|
|
|
|-----------|----------|
|
|
|
|
|
|
| 学生姓名 | student.name |
|
|
|
|
|
|
| 性别 | student.gender |
|
|
|
|
|
|
| 年级 | student.grade |
|
|
|
|
|
|
| 授课次数 | COUNT(DISTINCT lesson.id) |
|
|
|
|
|
|
| 平均参与度 | AVG(sr.participation) |
|
|
|
|
|
|
| 平均专注度 | AVG(sr.focus) |
|
|
|
|
|
|
|
|
|
|
|
|
### 问题修复
|
|
|
|
|
|
|
|
|
|
|
|
1. **lesson_type 字段不存在**
|
|
|
|
|
|
- 问题:`lesson` 表没有 `lesson_type` 字段
|
|
|
|
|
|
- 解决:改用 `l.status` 和 `l.notes`
|
|
|
|
|
|
|
|
|
|
|
|
2. **student 表 class_id 字段不存在**
|
|
|
|
|
|
- 问题:`student` 表没有 `class_id` 字段
|
|
|
|
|
|
- 解决:改用 `s.grade` 作为 `className` 显示
|
|
|
|
|
|
|
|
|
|
|
|
### 测试结果
|
|
|
|
|
|
|
|
|
|
|
|
| 测试项 | 结果 |
|
|
|
|
|
|
|--------|------|
|
|
|
|
|
|
| 编译通过 | ✅ |
|
|
|
|
|
|
| 授课记录导出 | ✅ HTTP 200 |
|
|
|
|
|
|
| 教师绩效导出 | ✅ HTTP 200 |
|
|
|
|
|
|
| 学生统计导出 | ✅ HTTP 200 |
|
|
|
|
|
|
| 空数据处理 | ✅ 返回 JSON |
|
|
|
|
|
|
|
|
|
|
|
|
### 文件变更列表
|
|
|
|
|
|
|
|
|
|
|
|
| 文件 | 变更说明 |
|
|
|
|
|
|
|------|---------|
|
|
|
|
|
|
| `reading-platform-java/pom.xml` | 添加 EasyExcel 依赖 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/dto/response/LessonExportVO.java` | 新建 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/dto/response/TeacherPerformanceExportVO.java` | 新建 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentStatExportVO.java` | 新建 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/service/SchoolExportService.java` | 新建 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolExportServiceImpl.java` | 新建 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/mapper/LessonMapper.java` | 添加 3 个导出查询方法 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolExportController.java` | 实现导出接口 |
|
|
|
|
|
|
| `reading-platform-frontend/src/api/school.ts` | 增强导出函数 |
|
|
|
|
|
|
|
|
|
|
|
|
### 测试验证
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 后端编译通过
|
|
|
|
|
|
- [x] 启动后端服务(端口 8481)
|
|
|
|
|
|
- [x] 授课记录导出接口测试通过
|
|
|
|
|
|
- [x] 教师绩效导出接口测试通过
|
|
|
|
|
|
- [x] 学生统计导出接口测试通过
|
|
|
|
|
|
- [x] 空数据场景测试通过
|
|
|
|
|
|
- [x] 前端服务运行正常(端口 5174)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**今日完成**: 学校端数据导出功能(3 个导出接口)
|
2026-03-23 09:46:08 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 学校端 - 数据报告功能实现
|
|
|
|
|
|
|
|
|
|
|
|
### 需求背景
|
|
|
|
|
|
|
|
|
|
|
|
学校端"数据中心"菜单下的"数据报告"子菜单功能之前未实现。前端 `ReportView.vue` 页面已存在,但调用的 API 返回空数据。需要实现完整的数据报告功能,让学校管理员能够查看:
|
|
|
|
|
|
1. **整体概览** - 教师总数、学生总数、班级总数、本月授课次数
|
|
|
|
|
|
2. **教师报告** - 教师教学数据统计(授课次数、任务数、评分)
|
|
|
|
|
|
3. **课程报告** - 课程使用排行(授课次数、学生数、完成率)
|
|
|
|
|
|
4. **学生报告** - 学生学习数据(完成任务、成长记录、出勤率)
|
|
|
|
|
|
|
|
|
|
|
|
### 实现内容
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. 新建后端 Mapper
|
|
|
|
|
|
|
|
|
|
|
|
**ReportMapper.java** - 数据报告 Mapper
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Mapper
|
|
|
|
|
|
public interface ReportMapper extends BaseMapper<Lesson> {
|
|
|
|
|
|
// 4 个统计查询方法
|
|
|
|
|
|
Map<String, Object> getOverviewStats(...) // 概览统计
|
|
|
|
|
|
List<Map<String, Object>> getTeacherReports(...) // 教师报告
|
|
|
|
|
|
List<Map<String, Object>> getCourseReports(...) // 课程报告
|
|
|
|
|
|
List<Map<String, Object>> getStudentReports(...) // 学生报告
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**SQL 查询逻辑**:
|
|
|
|
|
|
- **概览统计**: 统计教师数、学生数、班级数、本月授课次数、本月任务完成数
|
|
|
|
|
|
- **教师报告**: 按教师分组,统计授课次数、任务完成数、平均评分、最后活跃时间
|
|
|
|
|
|
- **课程报告**: 按课程分组,统计授课次数、学生数、平均评分、完成率
|
|
|
|
|
|
- **学生报告**: 按学生分组,统计任务完成数、成长记录数、出勤率
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. 新建后端 Service
|
|
|
|
|
|
|
|
|
|
|
|
**SchoolReportService.java** - 服务接口
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
public interface SchoolReportService {
|
|
|
|
|
|
ReportOverviewResponse getOverview(Long tenantId, LocalDate startDate, LocalDate endDate);
|
|
|
|
|
|
List<TeacherReportResponse> getTeacherReports(Long tenantId, LocalDate startDate, LocalDate endDate, int limit);
|
|
|
|
|
|
List<CourseReportResponse> getCourseReports(Long tenantId, LocalDate startDate, LocalDate endDate, int limit);
|
|
|
|
|
|
List<StudentReportResponse> getStudentReports(Long tenantId, LocalDate startDate, LocalDate endDate, int limit);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**SchoolReportServiceImpl.java** - 服务实现
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
@Service
|
|
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
|
|
public class SchoolReportServiceImpl implements SchoolReportService {
|
|
|
|
|
|
private final ReportMapper reportMapper;
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public ReportOverviewResponse getOverview(Long tenantId, LocalDate startDate, LocalDate endDate) {
|
|
|
|
|
|
// 计算时间范围(默认本月)
|
|
|
|
|
|
// 调用 Mapper 查询
|
|
|
|
|
|
// 转换为 ReportOverviewResponse 返回
|
|
|
|
|
|
}
|
|
|
|
|
|
// ... 其他方法
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3. 修改 Controller
|
|
|
|
|
|
|
|
|
|
|
|
**SchoolReportController.java** - 添加日期范围参数
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@GetMapping("/overview")
|
|
|
|
|
|
public Result<ReportOverviewResponse> getOverview(
|
|
|
|
|
|
@RequestParam(required = false) LocalDate startDate,
|
|
|
|
|
|
@RequestParam(required = false) LocalDate endDate) {
|
|
|
|
|
|
return Result.success(schoolReportService.getOverview(tenantId, startDate, endDate));
|
|
|
|
|
|
}
|
|
|
|
|
|
// ... 其他接口
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 4. 前端 API 对齐
|
|
|
|
|
|
|
|
|
|
|
|
**src/api/school.ts** - 更新类型定义和 API 函数
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// 更新接口定义
|
|
|
|
|
|
export interface ReportOverview {
|
|
|
|
|
|
reportDate: string;
|
|
|
|
|
|
totalTeachers: number;
|
|
|
|
|
|
totalStudents: number;
|
|
|
|
|
|
totalClasses: number;
|
|
|
|
|
|
monthlyLessons: number;
|
|
|
|
|
|
monthlyTasksCompleted: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface TeacherReport {
|
|
|
|
|
|
teacherId: number;
|
|
|
|
|
|
teacherName: string;
|
|
|
|
|
|
lessonCount: number;
|
|
|
|
|
|
taskCount: number;
|
|
|
|
|
|
averageRating: number;
|
|
|
|
|
|
lastLessonTime?: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
// ... CourseReport, StudentReport
|
|
|
|
|
|
|
|
|
|
|
|
// 更新 API 函数支持日期参数
|
|
|
|
|
|
export const getReportOverview = (startDate?: string, endDate?: string) => {
|
|
|
|
|
|
const params: Record<string, string> = {};
|
|
|
|
|
|
if (startDate) params.startDate = startDate;
|
|
|
|
|
|
if (endDate) params.endDate = endDate;
|
|
|
|
|
|
return http.get<ReportOverview>('/v1/school/reports/overview', { params });
|
|
|
|
|
|
};
|
|
|
|
|
|
// ... 其他 API
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 5. 前端页面完善
|
|
|
|
|
|
|
|
|
|
|
|
**ReportView.vue** - 更新数据绑定和显示
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// 概览数据
|
|
|
|
|
|
const overviewData = ref<ReportOverview>({
|
|
|
|
|
|
reportDate: '',
|
|
|
|
|
|
totalTeachers: 0,
|
|
|
|
|
|
totalStudents: 0,
|
|
|
|
|
|
totalClasses: 0,
|
|
|
|
|
|
monthlyLessons: 0,
|
|
|
|
|
|
monthlyTasksCompleted: 0,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 加载数据时传递日期参数
|
|
|
|
|
|
const loadData = async () => {
|
|
|
|
|
|
const startDate = dateRange.value?.[0]?.format('YYYY-MM-DD');
|
|
|
|
|
|
const endDate = dateRange.value?.[1]?.format('YYYY-MM-DD');
|
|
|
|
|
|
|
|
|
|
|
|
const [overview, teachers, courses, students] = await Promise.all([
|
|
|
|
|
|
getReportOverview(startDate, endDate),
|
|
|
|
|
|
getTeacherReports(startDate, endDate),
|
|
|
|
|
|
getCourseReports(startDate, endDate),
|
|
|
|
|
|
getStudentReports(startDate, endDate),
|
|
|
|
|
|
]);
|
|
|
|
|
|
// ...
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 设置默认日期范围为当月
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
setDefaultDateRange();
|
|
|
|
|
|
loadData();
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 字段映射
|
|
|
|
|
|
|
|
|
|
|
|
#### 概览卡片
|
|
|
|
|
|
| 显示项 | 数据字段 |
|
|
|
|
|
|
|--------|---------|
|
|
|
|
|
|
| 教师总数 | totalTeachers |
|
|
|
|
|
|
| 学生总数 | totalStudents |
|
|
|
|
|
|
| 班级总数 | totalClasses |
|
|
|
|
|
|
| 本月授课 | monthlyLessons |
|
|
|
|
|
|
|
|
|
|
|
|
#### 教师报告
|
|
|
|
|
|
| 显示项 | 数据字段 |
|
|
|
|
|
|
|--------|---------|
|
|
|
|
|
|
| 教师姓名 | teacherName |
|
|
|
|
|
|
| 授课次数 | lessonCount |
|
|
|
|
|
|
| 任务完成数 | taskCount |
|
|
|
|
|
|
| 平均评分 | averageRating |
|
|
|
|
|
|
| 最后活跃 | lastLessonTime |
|
|
|
|
|
|
|
|
|
|
|
|
#### 课程报告
|
|
|
|
|
|
| 显示项 | 数据字段 |
|
|
|
|
|
|
|--------|---------|
|
|
|
|
|
|
| 课程名称 | courseName |
|
|
|
|
|
|
| 授课次数 | lessonCount |
|
|
|
|
|
|
| 参与学生 | studentCount |
|
|
|
|
|
|
| 完成率 | completionRate |
|
|
|
|
|
|
| 平均评分 | averageRating |
|
|
|
|
|
|
|
|
|
|
|
|
#### 学生报告
|
|
|
|
|
|
| 显示项 | 数据字段 |
|
|
|
|
|
|
|--------|---------|
|
|
|
|
|
|
| 学生姓名 | studentName |
|
|
|
|
|
|
| 班级 | className |
|
|
|
|
|
|
| 完成任务 | taskCount |
|
|
|
|
|
|
| 成长记录 | growthRecordCount |
|
|
|
|
|
|
| 出勤率 | attendanceRate |
|
|
|
|
|
|
|
|
|
|
|
|
### 功能特性
|
|
|
|
|
|
|
|
|
|
|
|
1. **日期范围筛选**: 支持选择日期范围,默认统计当月数据
|
|
|
|
|
|
2. **实时刷新**: 选择日期范围后自动刷新数据
|
|
|
|
|
|
3. **详情弹窗**: 点击教师/课程可查看详细信息
|
|
|
|
|
|
4. **响应式设计**: 支持不同屏幕尺寸
|
|
|
|
|
|
|
|
|
|
|
|
### 文件变更列表
|
|
|
|
|
|
|
|
|
|
|
|
| 文件 | 变更说明 |
|
|
|
|
|
|
|------|---------|
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/mapper/ReportMapper.java` | 新建,4 个统计查询方法 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/service/SchoolReportService.java` | 新建,服务接口 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolReportServiceImpl.java` | 新建,服务实现 |
|
|
|
|
|
|
| `reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolReportController.java` | 修改,调用 Service |
|
|
|
|
|
|
| `reading-platform-frontend/src/api/school.ts` | 更新,类型定义和 API 函数 |
|
|
|
|
|
|
| `reading-platform-frontend/src/views/school/ReportView.vue` | 更新,数据绑定和显示 |
|
|
|
|
|
|
|
|
|
|
|
|
### 测试验证
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 后端编译通过
|
|
|
|
|
|
- [x] ReportView.vue 类型检查通过
|
|
|
|
|
|
- [ ] 启动后端服务(端口 8480)
|
|
|
|
|
|
- [ ] 启动前端服务(端口 5173)
|
|
|
|
|
|
- [ ] 登录学校管理员账号
|
|
|
|
|
|
- [ ] 访问数据报告页面
|
|
|
|
|
|
- [ ] 验证日期范围筛选
|
|
|
|
|
|
- [ ] 验证各 Tab 数据显示
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**今日完成**: 学校端数据报告功能(4 个统计接口 + 前端页面)
|