278 lines
7.0 KiB
Markdown
278 lines
7.0 KiB
Markdown
# 学习记录问题修复方案
|
||
|
||
## 📊 问题确认
|
||
|
||
### 数据库实际情况(学生ID 452,课程"妙解古诗文"):
|
||
- **上报次数:119次**
|
||
- **总时长:1804秒(约30分钟)**
|
||
- **平均每次:15秒**
|
||
- **视频最后位置:3秒**
|
||
- **学习次数:10次**
|
||
|
||
### 问题根源:
|
||
|
||
1. **前端上报频率过高**
|
||
- 当前:每10秒上报一次
|
||
- 问题:视频很短(几秒)也会多次上报
|
||
|
||
2. **非视频课件重复统计**
|
||
- 每次打开图片/PDF都上报30秒
|
||
- 如果反复切换课件,会重复累加
|
||
|
||
3. **时长统计不准确**
|
||
- 前端计算的duration包含了所有操作时间
|
||
- 没有去重同一课件的重复学习
|
||
|
||
---
|
||
|
||
## 🔧 修复方案
|
||
|
||
### 方案1:优化前端上报逻辑(推荐)
|
||
|
||
#### 1.1 延长上报间隔
|
||
|
||
**文件:** `fronted_uniapp/pages/course/detail.vue`
|
||
|
||
**修改:** 第172行
|
||
```javascript
|
||
// 修改前
|
||
reportInterval: 10000, // 每10秒上报一次
|
||
|
||
// 修改后
|
||
reportInterval: 30000, // 每30秒上报一次(减少上报频率)
|
||
```
|
||
|
||
#### 1.2 优化非视频课件上报
|
||
|
||
**修改:** 第1364-1370行
|
||
```javascript
|
||
// 修改前
|
||
duration: 30, // 非视频课件默认30秒
|
||
|
||
// 修改后
|
||
duration: 10, // 非视频课件默认10秒(更合理)
|
||
```
|
||
|
||
#### 1.3 添加课件去重逻辑
|
||
|
||
**在 `reportNonVideoProgress` 方法中添加去重检查:**
|
||
|
||
```javascript
|
||
// 在 reportNonVideoProgress 方法开始处添加
|
||
async reportNonVideoProgress() {
|
||
console.log('[课程学习] 📤 准备上报非视频课件进度')
|
||
|
||
if (!this.courseware || !this.courseware.id) {
|
||
console.warn('[课程学习] ⚠️ 课件信息不完整,跳过上报')
|
||
return
|
||
}
|
||
|
||
// ✅ 添加:检查是否已经上报过此课件(避免重复统计)
|
||
const storageKey = `courseware_viewed_${this.courseId}_${this.courseware.id}`
|
||
const hasViewed = uni.getStorageSync(storageKey)
|
||
if (hasViewed) {
|
||
console.log('[课程学习] ℹ️ 课件已查看过,跳过重复上报')
|
||
return
|
||
}
|
||
|
||
// 标记为已查看(24小时后过期)
|
||
uni.setStorageSync(storageKey, Date.now())
|
||
setTimeout(() => {
|
||
uni.removeStorageSync(storageKey)
|
||
}, 24 * 60 * 60 * 1000)
|
||
|
||
// ... 原有上报逻辑
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 方案2:后端数据清洗(立即执行)
|
||
|
||
#### 2.1 清理异常数据
|
||
|
||
**问题:** 有些记录duration为0或非常小
|
||
|
||
**SQL:**
|
||
```sql
|
||
-- 查看异常记录
|
||
SELECT
|
||
lr.student_id,
|
||
u.nick_name,
|
||
c.course_name,
|
||
lr.total_duration AS 总时长秒,
|
||
lr.learn_count AS 学习次数,
|
||
COUNT(ld.id) AS 上报次数,
|
||
AVG(ld.duration) AS 平均每次秒
|
||
FROM learning_record lr
|
||
INNER JOIN sys_user u ON lr.student_id = u.user_id
|
||
INNER JOIN course c ON lr.course_id = c.id
|
||
LEFT JOIN learning_detail ld ON lr.id = ld.learning_record_id
|
||
GROUP BY lr.id, lr.student_id, u.nick_name, c.course_name, lr.total_duration, lr.learn_count
|
||
HAVING 总时长秒 > 600 OR 上报次数 > 50
|
||
ORDER BY 总时长秒 DESC;
|
||
```
|
||
|
||
#### 2.2 重新计算总时长(基于课件实际观看)
|
||
|
||
**原理:** 每个课件只统计一次最大观看时长
|
||
|
||
**SQL:**
|
||
```sql
|
||
-- 为每个学习记录重新计算总时长
|
||
UPDATE learning_record lr
|
||
INNER JOIN (
|
||
SELECT
|
||
ld.learning_record_id,
|
||
-- 对每个课件,只取最大的video_end_position
|
||
-- 非视频课件(courseware_id为NULL或video_end_position=0)统计为10秒
|
||
SUM(
|
||
CASE
|
||
WHEN MAX(ld.video_end_position) > 0 THEN MAX(ld.video_end_position)
|
||
ELSE 10
|
||
END
|
||
) AS calculated_duration
|
||
FROM learning_detail ld
|
||
GROUP BY ld.learning_record_id, ld.courseware_id
|
||
) AS calc ON lr.id = calc.learning_record_id
|
||
SET lr.total_duration = calc.calculated_duration;
|
||
```
|
||
|
||
---
|
||
|
||
### 方案3:添加数据验证逻辑(后端)
|
||
|
||
#### 3.1 限制单次上报的最大时长
|
||
|
||
**文件:** `StudyLearningRecordServiceImpl.java`
|
||
|
||
**在 `updateLearningProgress` 方法中添加验证:**
|
||
|
||
```java
|
||
// 第260行后添加
|
||
public int updateLearningProgress(Long studentId, Long courseId, Long coursewareId, Integer duration, Integer videoPosition, Integer videoTotalDuration)
|
||
{
|
||
// ✅ 添加:验证duration合理性
|
||
if (duration != null) {
|
||
// 单次上报时长不应超过60秒(上报间隔30秒 + 缓冲)
|
||
if (duration > 60) {
|
||
logger.warn("学生 {} 上报的时长异常:{} 秒,限制为60秒", studentId, duration);
|
||
duration = 60;
|
||
}
|
||
// 时长不能为负数
|
||
if (duration < 0) {
|
||
logger.warn("学生 {} 上报的时长为负数:{} 秒,忽略", studentId, duration);
|
||
duration = 0;
|
||
}
|
||
}
|
||
|
||
// ... 原有逻辑
|
||
}
|
||
```
|
||
|
||
#### 3.2 优化学习次数统计
|
||
|
||
**当前问题:** 30分钟算一个会话太长
|
||
|
||
**修改:** 第291行
|
||
```java
|
||
// 修改前
|
||
long sessionTimeout = 30 * 60 * 1000L; // 30分钟
|
||
|
||
// 修改后
|
||
long sessionTimeout = 10 * 60 * 1000L; // 10分钟(更合理)
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 立即执行步骤
|
||
|
||
### 步骤1:备份数据
|
||
|
||
```bash
|
||
双击运行:导入前数据备份.bat
|
||
```
|
||
|
||
### 步骤2:查看当前异常数据
|
||
|
||
```sql
|
||
-- 在MySQL中执行
|
||
USE study;
|
||
|
||
SELECT
|
||
lr.student_id,
|
||
u.nick_name AS 学生,
|
||
c.course_name AS 课程,
|
||
lr.total_duration AS 总时长秒,
|
||
ROUND(lr.total_duration / 60, 1) AS 总时长分钟,
|
||
lr.learn_count AS 学习次数,
|
||
lr.progress AS 进度百分比,
|
||
COUNT(ld.id) AS 上报次数
|
||
FROM learning_record lr
|
||
INNER JOIN sys_user u ON lr.student_id = u.user_id
|
||
INNER JOIN course c ON lr.course_id = c.id
|
||
LEFT JOIN learning_detail ld ON lr.id = ld.learning_record_id
|
||
GROUP BY lr.id
|
||
HAVING 总时长秒 > 600 OR 上报次数 > 50
|
||
ORDER BY 总时长秒 DESC
|
||
LIMIT 20;
|
||
```
|
||
|
||
### 步骤3:清理测试数据(可选)
|
||
|
||
**如果需要重置学习记录:**
|
||
|
||
```sql
|
||
-- ⚠️ 谨慎操作!会清空所有学习记录
|
||
-- DELETE FROM learning_detail WHERE student_id = 452;
|
||
-- DELETE FROM learning_record WHERE student_id = 452;
|
||
```
|
||
|
||
### 步骤4:修改前端代码
|
||
|
||
1. 打开 `fronted_uniapp/pages/course/detail.vue`
|
||
2. 修改第172行:`reportInterval: 30000`
|
||
3. 修改第1368行:`duration: 10`
|
||
4. 保存文件
|
||
|
||
### 步骤5:重启服务
|
||
|
||
1. 后端:在IDEA中重启
|
||
2. 前端:在HBuilderX中重新运行
|
||
|
||
### 步骤6:测试验证
|
||
|
||
1. 打开APP
|
||
2. 学习一门课程(播放视频30秒)
|
||
3. 查看数据库:
|
||
```sql
|
||
SELECT * FROM learning_detail
|
||
WHERE student_id = YOUR_ID
|
||
ORDER BY start_time DESC
|
||
LIMIT 5;
|
||
```
|
||
4. 验证:duration应该合理(约30秒左右)
|
||
|
||
---
|
||
|
||
## 🎯 预期效果
|
||
|
||
修复后:
|
||
- ✅ 上报频率降低:从每10秒改为每30秒
|
||
- ✅ 时长更准确:非视频从30秒改为10秒
|
||
- ✅ 避免重复:同一课件不重复统计
|
||
- ✅ 数据验证:单次上报不超过60秒
|
||
- ✅ 学习次数准确:10分钟算一个会话
|
||
|
||
---
|
||
|
||
## 📞 如需帮助
|
||
|
||
如果修复后仍有问题,请提供:
|
||
1. 具体学生ID
|
||
2. 课程名称
|
||
3. 学习时长和实际播放时长
|
||
4. learning_detail表的最近10条记录
|
||
|
||
我会进一步协助分析。
|