# 2026-04-15 课程巩固、单元强化、单元挑战统计逻辑学习笔记

[李承龙要求] 学习 vala_bi 仓库中关于课程巩固、单元强化、单元挑战的统计逻辑。

## 代码仓库
- 地址: https://git.valavala.com/vala/vala_bi
- 本地克隆: /tmp/vala_bi
- 语言: Go

---

## 一、课程巩固（Review）

### 概念
- 课程巩固是课时（Lesson）级别的功能，每个课时完成后有对应的巩固练习
- 巩固练习包含一组选择题（QuestionList），记录是否正确（isRight）和是否跳过（isSkip）

### 数据来源
- 源表: `unit_review_question_result`（MySQL vala_user 库）
- BI表: `user_chapter_time`（PostgreSQL vala_bi 库）

### 统计字段（存在 user_chapter_time 表中）
- `first_done_review_duration`: 巩固用时（秒），来源 `play_time / 1000`
- `first_done_review_right_rate`: 巩固正确率（万分比），计算方式 = `正确数 / 总题数 * 10000`

### 聚合统计（Lesson 维度，写入飞书多维表格）
- **巩固完成人数**: `COUNT(first_done_review_duration > 0)`
- **巩固平均完成时间/分钟**: `AVG(first_done_review_duration) / 60`
- **巩固平均正确率%**: `AVG(first_done_review_right_rate) / 100`

### 同步函数
- `userFirstDoneReview()` in `internal/service/user.go`
- cron 任务: `UserFirstDone` → `userService.UserFirstDone()` 中调用

---

## 二、单元强化（Summary）

### 概念
- 单元强化是单元（Unit）级别的功能，每个单元的5个课时全部完成后可进入强化
- 数据来源于 `user_unit_summary_km_result` 表（记录用户参与单元强化的行为）
- 通过 `user_learn_record_report_summary` 表记录完成时间

### 数据来源
- 源表1（进入）: `bi_user_unit_summary_km_result`（PostgreSQL，已同步到 BI 库，前缀 `bi_`）
- 源表2（完成）: `user_learn_record_report_summary`（MySQL vala_user 库），条件 `learn_card_type = 1 AND record_type = 3`
- BI表: `user_unit_time`（PostgreSQL vala_bi 库）

### 统计字段（存在 user_unit_time 表中）
- `summary_in_ts`: 首次进入强化的时间戳（Unix秒），从 `user_unit_summary_km_result.created_at` 获取
- `summary_done_ts`: 首次完成强化的时间戳（Unix秒），从 `user_learn_record_report_summary.complete_time` 获取

### 聚合统计（Unit 维度，写入飞书多维表格）
- **强化进入人数**: `COUNT(summary_in_ts > 0)`
- **强化完成人数**: `COUNT(summary_done_ts > 0)`

### 同步函数
- 进入: `UserUnitSummaryStart()` in `internal/service/user.go`
- 完成: `UserUnitSCDone()` in `internal/service/user.go`（`learn_card_type=1, record_type=3` 为 summary）
- cron 任务: `UserFirstDone` job 中依次调用

---

## 三、单元挑战（Challenge）

### 概念
- 单元挑战是单元（Unit）级别的功能，在单元强化之后可进入
- 挑战包含四个维度: listening（听力）、speaking（口语）、reading（阅读）、writing（写作）
- 每个维度评分等级: Perfect / Good / Oops

### 数据来源
- 源表1（进入&评分）: `bi_unit_challenge_question_result`（PostgreSQL，已同步到 BI 库）
- 源表2（完成）: `user_learn_record_report_summary`（MySQL vala_user 库），条件 `learn_card_type = 1 AND record_type = 4`
- BI表: `user_unit_time`（PostgreSQL vala_bi 库）

### 统计字段（存在 user_unit_time 表中）
- `challenge_in_ts`: 首次进入挑战的时间戳
- `challenge_done_ts`: 首次完成挑战的时间戳
- `challenge_listening`: 听力评分（Perfect/Good/Oops）
- `challenge_speaking`: 口语评分
- `challenge_reading`: 阅读评分
- `challenge_writing`: 写作评分

### 聚合统计（Unit 维度，写入飞书多维表格）
- **挑战参与人数**: `COUNT(challenge_in_ts > 0)`
- **挑战完成人数**: `COUNT(challenge_done_ts > 0)`
- 四个维度各自的 Perfect率%、Good率%、Oops率%:
  - 计算方式: 各评分人数 / (Perfect + Good + Oops 总人数) * 100

### 同步函数
- 进入&评分: `UserUnitChallengeStart()` in `internal/service/user.go`
- 完成: `UserUnitSCDone()` in `internal/service/user.go`（`learn_card_type=1, record_type=4` 为 challenge）
- cron 任务: `UserFirstDone` job 中依次调用

---

## 四、关键关联逻辑

### 课程结构层级
- CourseLevel (A1/A2) → SeasonGamePackage (S1~S4) → ValaGameInfo (Unit, 每Season 12个Unit) → ValaGameChapter (Lesson, 每Unit 5个Lesson)
- UnitIndex = (SeasonOfQuarter - 1) * 12 + GameInfo.Index
- ChapterIndex = UnitIndex * 5 + Chapter.Index

### 付费用户过滤
- Unit 统计使用 `pay_account_user_course` 表进行 INNER JOIN，确保只统计付费用户
- Lesson 统计使用 `account_user_course` 表

### 测试账号剔除
- `bi_vala_app_account.status = 2` 为测试账号
- 所有统计均通过 `IsTestUser()` 剔除测试用户

### BI 表（PostgreSQL vala_bi 库）
- `user_chapter_time`: 课时维度统计表（包含巩固数据）
- `user_unit_time`: 单元维度统计表（包含强化和挑战数据）

### Cron 调度顺序
1. `UserFirstDone()` - 课时首次进入/完成/巩固/组件正确率
2. `UserUnitSummaryStart()` - 强化进入
3. `UserUnitChallengeStart()` - 挑战进入&评分
4. `UserUnitSCDone()` - 强化完成 + 挑战完成

---

## 五、飞书多维表格输出

### Unit 维度数据表 (tblsA5CwmL3AZxG6)
字段包括: 课程单元、进入过人数、已通过人数、平均通关时间/天、平均间隔时间/天、强化进入人数、强化完成人数、挑战参与人数、挑战完成人数、四维度各三级评分率

### Lesson 维度数据表 (多个表，按时间窗口区分)
- 全量: tblMGuEjfdidefks
- 昨日起: tbltoiOQxsvS13d0
- 6日前起: tblIbb3fKowryQj0
- 29日前起: tbltyxhkYJjZhJma
字段包括: 课程Lesson、巩固完成人数、巩固平均完成时间/分钟、巩固平均正确率%、中互动/核心互动各评分比例等
