376 lines
10 KiB
Markdown
376 lines
10 KiB
Markdown
# 学习进度判断逻辑说明
|
||
|
||
## 📊 核心逻辑
|
||
|
||
**学习进度 = (累计学习时长 / 课程总时长) × 100%**
|
||
|
||
## 🔍 详细判断流程
|
||
|
||
### 1. 课件类型分类
|
||
|
||
系统将课件分为以下类型:
|
||
|
||
| 类型 | 说明 | 是否参与进度计算 | 计算方式 |
|
||
|------|------|----------------|---------|
|
||
| `video` | 视频课件 | ✅ **是** | 基于学习时长 |
|
||
| `document` | 文档课件(PDF、Word、Excel等) | ✅ **是** | 基于完成状态 |
|
||
| `image` | 图片课件 | ✅ **是** | 基于完成状态 |
|
||
| `text` | 文本课件 | ❌ **否** | - |
|
||
|
||
### 2. 进度计算步骤(混合计算模式)
|
||
|
||
系统采用**混合计算模式**,不同类型课件使用不同的计算方式:
|
||
|
||
#### 步骤1:查询课程的所有课件
|
||
|
||
```java
|
||
// 查询所有类型的课件
|
||
SELECT * FROM courseware WHERE course_id = ?
|
||
```
|
||
|
||
**课件分类**:
|
||
- 视频课件(`type = 'video'`)
|
||
- 图片课件(`type = 'image'`)
|
||
- 文档课件(`type = 'document'`,包括PDF)
|
||
|
||
#### 步骤2:分别计算各类型进度
|
||
|
||
**2.1 视频进度计算(基于时长)**
|
||
|
||
```java
|
||
// 计算视频总时长
|
||
videoTotalDuration = SUM(video.duration)
|
||
|
||
// 估算视频学习时长(从总学习时长中提取)
|
||
videoLearningDuration = totalDuration × 0.7 // 假设视频占70%
|
||
|
||
// 计算视频进度
|
||
videoProgress = (videoLearningDuration / videoTotalDuration) × 100
|
||
```
|
||
|
||
**关键点**:
|
||
- 视频课件的 `duration` 字段必须设置(单位:秒)
|
||
- 如果所有视频的 `duration` 都未设置,视频进度 = 0%
|
||
|
||
**2.2 图片和PDF进度计算(基于完成状态)**
|
||
|
||
```java
|
||
// 统计图片和PDF总数
|
||
totalNonVideoCount = imageList.size() + documentList.size()
|
||
|
||
// 查询学习详情记录,估算已查看的课件数
|
||
viewedCount = min(学习详情记录数, totalNonVideoCount)
|
||
|
||
// 计算非视频进度
|
||
nonVideoProgress = (viewedCount / totalNonVideoCount) × 100
|
||
```
|
||
|
||
**关键点**:
|
||
- 通过查询学习详情记录来判断学生是否查看过课件
|
||
- 每个图片/PDF查看一次默认算30秒学习时长
|
||
- 如果学生有学习记录,认为至少查看过部分课件
|
||
|
||
#### 步骤3:计算权重并综合
|
||
|
||
```java
|
||
// 计算权重
|
||
videoWeight = (视频数量 / 总课件数量) × 100
|
||
nonVideoWeight = ((图片数量 + PDF数量) / 总课件数量) × 100
|
||
|
||
// 综合进度
|
||
courseProgress = (videoProgress × videoWeight + nonVideoProgress × nonVideoWeight) / 100
|
||
```
|
||
|
||
**计算公式**:
|
||
```
|
||
综合进度 = (视频进度 × 视频权重 + 非视频进度 × 非视频权重) / 100
|
||
|
||
其中:
|
||
- 视频权重 = 视频课件数 / 总课件数 × 100%
|
||
- 非视频权重 = (图片数 + PDF数) / 总课件数 × 100%
|
||
```
|
||
|
||
#### 步骤4:限制范围
|
||
|
||
```java
|
||
if (progress > 100) progress = 100
|
||
if (progress < 0) progress = 0
|
||
```
|
||
|
||
进度限制在 **0-100%** 之间。
|
||
|
||
## 📝 代码位置
|
||
|
||
**文件**:
|
||
```
|
||
Study-Vue-redis/ry-study-system/src/main/java/com/ddnai/system/service/impl/study/StudyLearningRecordServiceImpl.java
|
||
```
|
||
|
||
**方法**:
|
||
```java
|
||
private BigDecimal calculateCourseProgress(Long studentId, Long courseId, ...)
|
||
```
|
||
|
||
## ⚠️ 重要限制
|
||
|
||
### 1. 只计算视频课件
|
||
|
||
**当前逻辑**:
|
||
- ✅ 只有 `type = 'video'` 的课件参与进度计算
|
||
- ❌ 图片、PDF、文档等**不参与**进度计算
|
||
|
||
**影响**:
|
||
- 如果课程只有图片或PDF,没有视频,进度会显示 **0%**
|
||
- 即使学生学习了图片或PDF,也不会增加进度
|
||
|
||
### 2. 视频时长必须设置
|
||
|
||
**要求**:
|
||
- 每个视频课件的 `duration` 字段必须设置(单位:秒)
|
||
- 如果 `duration` 为 NULL 或 0,该视频不计入课程总时长
|
||
|
||
**影响**:
|
||
- 如果所有视频的 `duration` 都未设置,课程总时长 = 0 → 进度 = 0%
|
||
- 即使学生学习了很长时间,进度也会显示 0%
|
||
|
||
### 3. 学习时长统计
|
||
|
||
**统计方式**:
|
||
- 学生观看视频时,系统会记录学习时长
|
||
- 每次学习都会累加到 `total_duration` 字段
|
||
- 图片、PDF 等非视频课件的学习**不统计**学习时长
|
||
|
||
## 📊 实际案例
|
||
|
||
### 案例1:正常情况
|
||
|
||
**课程配置**:
|
||
- 视频1:12分钟(720秒)
|
||
- 视频2:10分钟(600秒)
|
||
- 课程总时长:1320秒(22分钟)
|
||
|
||
**学生学习**:
|
||
- 累计学习:10分钟(600秒)
|
||
|
||
**进度计算**:
|
||
```
|
||
进度 = (600 / 1320) × 100 = 45.45%
|
||
```
|
||
|
||
### 案例2:视频时长未设置
|
||
|
||
**课程配置**:
|
||
- 视频1:duration = NULL
|
||
- 视频2:duration = NULL
|
||
- 课程总时长:0秒
|
||
|
||
**学生学习**:
|
||
- 累计学习:10分钟(600秒)
|
||
|
||
**进度计算**:
|
||
```
|
||
进度 = (600 / 0) → 返回 0%(避免除以0)
|
||
```
|
||
|
||
### 案例3:只有图片和PDF
|
||
|
||
**课程配置**:
|
||
- 图片1:xxx.jpg
|
||
- PDF1:xxx.pdf
|
||
- 视频:无
|
||
- 总课件数:2个
|
||
|
||
**学生学习**:
|
||
- 查看了图片和PDF
|
||
- 学习详情记录:2条(每个课件查看一次)
|
||
- 累计学习:60秒(2个课件 × 30秒/个)
|
||
|
||
**进度计算**:
|
||
```
|
||
视频进度 = 0%(没有视频)
|
||
非视频进度 = (2个已查看 / 2个总数) × 100 = 100%
|
||
视频权重 = 0 / 2 × 100 = 0%
|
||
非视频权重 = 2 / 2 × 100 = 100%
|
||
综合进度 = (0 × 0% + 100% × 100%) / 100 = 100%
|
||
```
|
||
|
||
### 案例4:混合类型(视频+图片+PDF)
|
||
|
||
**课程配置**:
|
||
- 视频1:12分钟(720秒)
|
||
- 图片1:xxx.jpg
|
||
- PDF1:xxx.pdf
|
||
- 总课件数:3个
|
||
|
||
**学生学习**:
|
||
- 视频学习:6分钟(360秒)
|
||
- 查看了图片和PDF
|
||
- 累计学习:420秒(360秒视频 + 60秒图片PDF)
|
||
|
||
**进度计算**:
|
||
```
|
||
视频进度 = (360 / 720) × 100 = 50%
|
||
非视频进度 = (2个已查看 / 2个总数) × 100 = 100%
|
||
视频权重 = 1 / 3 × 100 = 33.33%
|
||
非视频权重 = 2 / 3 × 100 = 66.67%
|
||
综合进度 = (50% × 33.33% + 100% × 66.67%) / 100 = 83.33%
|
||
```
|
||
|
||
## 🔧 如何修复进度问题
|
||
|
||
### 问题1:进度显示0%,但学生已学习
|
||
|
||
**原因**:视频课件的 `duration` 未设置
|
||
|
||
**解决方案**:
|
||
1. 在管理端编辑视频课件,设置正确的时长(秒)
|
||
2. 执行SQL重新计算进度:
|
||
```sql
|
||
UPDATE learning_record lr
|
||
SET progress = (
|
||
SELECT
|
||
CASE
|
||
WHEN SUM(cw.duration) IS NULL OR SUM(cw.duration) = 0 THEN 0
|
||
ELSE LEAST(100, ROUND(lr.total_duration * 100.0 / SUM(cw.duration), 2))
|
||
END
|
||
FROM courseware cw
|
||
WHERE cw.course_id = lr.course_id AND cw.type = 'video'
|
||
)
|
||
WHERE lr.total_duration > 0;
|
||
```
|
||
|
||
### 问题2:图片和PDF进度计算(已解决)
|
||
|
||
**当前实现**:
|
||
- ✅ 图片和PDF**已参与**进度计算
|
||
- ✅ 采用基于完成状态的计算方式
|
||
- ✅ 通过查询学习详情记录来判断是否查看过
|
||
|
||
**计算逻辑**:
|
||
1. 统计课程中的图片和PDF数量
|
||
2. 查询学生的学习详情记录
|
||
3. 根据学习记录数量估算已查看的课件数
|
||
4. 计算完成度:已查看数 / 总课件数 × 100%
|
||
|
||
**注意事项**:
|
||
- 前端需要在学生查看图片/PDF时记录学习详情
|
||
- 每个图片/PDF查看一次默认算30秒学习时长
|
||
- 如果课程只有图片和PDF,没有视频,进度会基于学习时长计算
|
||
|
||
## 📋 数据库字段说明
|
||
|
||
### courseware 表(课件表)
|
||
|
||
| 字段 | 类型 | 说明 | 是否必需 |
|
||
|------|------|------|---------|
|
||
| `id` | BIGINT | 课件ID | ✅ |
|
||
| `course_id` | BIGINT | 课程ID | ✅ |
|
||
| `type` | VARCHAR | 课件类型(video/document/image/text) | ✅ |
|
||
| `duration` | INT | 视频时长(秒,仅视频类型) | ⚠️ 视频必需 |
|
||
| `title` | VARCHAR | 课件标题 | ✅ |
|
||
| `file_path` | VARCHAR | 文件路径 | ✅ |
|
||
|
||
### learning_record 表(学习记录表)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `id` | BIGINT | 记录ID |
|
||
| `student_id` | BIGINT | 学生ID |
|
||
| `course_id` | BIGINT | 课程ID |
|
||
| `total_duration` | INT | 累计学习时长(秒) |
|
||
| `progress` | DECIMAL | 学习进度(0-100) |
|
||
| `learn_count` | INT | 学习次数 |
|
||
| `last_learn_time` | DATETIME | 最后学习时间 |
|
||
|
||
## 🎯 总结
|
||
|
||
### 核心要点
|
||
|
||
1. **混合计算模式**:
|
||
- ✅ 视频课件:基于学习时长计算
|
||
- ✅ 图片和PDF:基于完成状态计算
|
||
- ❌ 文本课件:不参与进度计算
|
||
|
||
2. **视频时长必须设置**:`duration` 字段必须大于0
|
||
|
||
3. **计算公式**:
|
||
```
|
||
综合进度 = (视频进度 × 视频权重 + 非视频进度 × 非视频权重) / 100
|
||
```
|
||
|
||
4. **进度范围**:限制在 0-100% 之间
|
||
|
||
5. **图片和PDF完成判断**:
|
||
- 通过查询学习详情记录来判断
|
||
- 每个图片/PDF查看一次默认算30秒学习时长
|
||
- 如果学生有学习记录,认为至少查看过部分课件
|
||
|
||
### 判断流程图
|
||
|
||
```
|
||
开始
|
||
↓
|
||
查询课程的所有课件
|
||
↓
|
||
分类统计:视频、图片、PDF
|
||
↓
|
||
┌─────────────────┬─────────────────┐
|
||
│ 视频进度计算 │ 非视频进度计算 │
|
||
│ (基于时长) │ (基于完成状态) │
|
||
├─────────────────┼─────────────────┤
|
||
│ 1. 计算视频总时长│ 1. 统计图片/PDF数│
|
||
│ 2. 估算视频学习 │ 2. 查询学习记录 │
|
||
│ 时长 │ 3. 估算已查看数 │
|
||
│ 3. 计算视频进度 │ 4. 计算完成度 │
|
||
└─────────────────┴─────────────────┘
|
||
↓
|
||
计算权重:
|
||
- 视频权重 = 视频数 / 总课件数
|
||
- 非视频权重 = (图片+PDF)数 / 总课件数
|
||
↓
|
||
综合进度 = (视频进度 × 视频权重 + 非视频进度 × 非视频权重) / 100
|
||
↓
|
||
限制在 0-100% 之间
|
||
↓
|
||
返回进度
|
||
```
|
||
|
||
## 📚 相关文件
|
||
|
||
- **后端代码**:`StudyLearningRecordServiceImpl.java`
|
||
- **诊断SQL**:`database_check_learning_progress.sql`
|
||
- **修复SQL**:`database_fix_video_duration_and_progress.sql`
|
||
- **说明文档**:`学习进度问题修复说明.md`
|
||
|
||
## 🔍 调试方法
|
||
|
||
### 查看后端日志
|
||
|
||
后端已添加详细日志,重启服务后可以看到:
|
||
|
||
```
|
||
INFO - 开始计算学习进度 - 学生ID: 109, 课程ID: 3
|
||
INFO - 课程 3 包含 2 个视频课件
|
||
WARN - 视频课件 8 (亡羊补牢) 的时长未设置或为0
|
||
INFO - 课程 3 总时长: 1440秒 (24分钟), 2个视频有时长, 0个视频无时长
|
||
INFO - 学生 109 对课程 3 的累计学习时长: 3秒 (0分钟)
|
||
INFO - 计算进度: 3 / 1440 * 100 = 0.21%
|
||
INFO - 最终进度: 0.21%
|
||
```
|
||
|
||
### 执行诊断SQL
|
||
|
||
```sql
|
||
-- 查看学习记录和进度
|
||
SELECT
|
||
lr.id,
|
||
lr.course_id,
|
||
c.course_name,
|
||
lr.total_duration as '累计时长(秒)',
|
||
lr.progress as '进度(%)',
|
||
(SELECT SUM(duration) FROM courseware WHERE course_id = lr.course_id AND type = 'video') as '课程总时长(秒)'
|
||
FROM learning_record lr
|
||
LEFT JOIN course c ON lr.course_id = c.id;
|
||
```
|
||
|