guoyu/Test/备份/_已清理文件备份_周六 22512/md/学习进度判断逻辑说明.md

376 lines
10 KiB
Markdown
Raw Normal View History

# 学习进度判断逻辑说明
## 📊 核心逻辑
**学习进度 = (累计学习时长 / 课程总时长) × 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正常情况
**课程配置**
- 视频112分钟720秒
- 视频210分钟600秒
- 课程总时长1320秒22分钟
**学生学习**
- 累计学习10分钟600秒
**进度计算**
```
进度 = (600 / 1320) × 100 = 45.45%
```
### 案例2视频时长未设置
**课程配置**
- 视频1duration = NULL
- 视频2duration = NULL
- 课程总时长0秒
**学生学习**
- 累计学习10分钟600秒
**进度计算**
```
进度 = (600 / 0) → 返回 0%避免除以0
```
### 案例3只有图片和PDF
**课程配置**
- 图片1xxx.jpg
- PDF1xxx.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
**课程配置**
- 视频112分钟720秒
- 图片1xxx.jpg
- PDF1xxx.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;
```