Files
huike-back/docs/course-domain-jsonb-migration-runbook.md
weli 15dc2c1a3c refactor course_category naming and add jsonb migration runbook
Rename remaining qumu_category db artifacts to course_category and document jsonb key migration procedure for existing production data.

Made-with: Cursor
2026-04-23 21:39:13 +08:00

207 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 教学域命名重构:JSONB 数据迁移手册
## 1. 结论先看
- JSONB 结构字段已在 Rust 代码中切换为 `course_*` 命名。
- 已新增并提交 JSONB 存量迁移脚本(`up/down.sql`)。
- 对于线上已有数据,仍需要按本手册执行 migration + 校验,确保旧键 `qumu_*` 全量迁移到 `course_*`
## 2. 代码与迁移现状
### 2.1 Rust 结构已改为 `course_*`
核心结构体(`htyws_models`):
- `CourseSectionJsonData` 使用:
- `course_id`
- `course_type`
- `course_name`
- `course_text`
- `course_category_key`
- `course_category_name`
并且 `daka/jihua/kecheng` 相关请求与模型字段已统一到 `course_sections`
### 2.2 JSONB 存量迁移脚本
已存在以下 migration
- `htyws_models/migrations/2026-04-23-100003_migrate_qumu_jsonb_keys_to_course`
- 迁移对象:`daka.course_sections``jihua.course_sections`
- 逻辑:使用 `jsonb_array_elements + jsonb_agg` 重组 `vals` 数组元素,将 `qumu_*` 改为 `course_*`
- `htykc_models/migrations/2026-04-23-100004_migrate_qumu_jsonb_keys_to_course`
- 迁移对象:`kecheng.course_sections`
- 逻辑:同上
## 3. 迁移前检查
### 3.1 数据库与权限
确认目标库可连接并有执行 migration 权限:
```sql
\c htyws_local
SELECT current_user, current_database();
```
`htykc_local` 同样检查。
### 3.2 迁移前数据基线(建议保存结果)
`htyws_local`
```sql
-- daka 中仍含 qumu_* 键的记录数
SELECT count(*) AS daka_rows_with_qumu_keys
FROM daka
WHERE course_sections IS NOT NULL
AND EXISTS (
SELECT 1
FROM jsonb_array_elements(course_sections->'vals') AS item
WHERE item ? 'qumu_id'
OR item ? 'qumu_type'
OR item ? 'qumu_name'
OR item ? 'qumu_text'
OR item ? 'qumu_category_key'
OR item ? 'qumu_category_name'
);
-- jihua 中仍含 qumu_* 键的记录数
SELECT count(*) AS jihua_rows_with_qumu_keys
FROM jihua
WHERE course_sections IS NOT NULL
AND EXISTS (
SELECT 1
FROM jsonb_array_elements(course_sections->'vals') AS item
WHERE item ? 'qumu_id'
OR item ? 'qumu_type'
OR item ? 'qumu_name'
OR item ? 'qumu_text'
OR item ? 'qumu_category_key'
OR item ? 'qumu_category_name'
);
```
`htykc_local`
```sql
SELECT count(*) AS kecheng_rows_with_qumu_keys
FROM kecheng
WHERE course_sections IS NOT NULL
AND EXISTS (
SELECT 1
FROM jsonb_array_elements(course_sections->'vals') AS item
WHERE item ? 'qumu_id'
OR item ? 'qumu_type'
OR item ? 'qumu_name'
OR item ? 'qumu_text'
OR item ? 'qumu_category_key'
OR item ? 'qumu_category_name'
);
```
## 4. 执行步骤
### 4.1 执行 `htyws_models` migration
```bash
cd huike-back/htyws_models
DATABASE_URL=postgres://htyws@localhost/htyws_local diesel migration run
```
### 4.2 执行 `htykc_models` migration
```bash
cd huike-back/htykc_models
DATABASE_URL=postgres://htykc@localhost/htykc_local diesel migration run
```
> 若本地无 `htykc` DB 角色,先创建角色或使用可用 DB 用户连接串再执行。
## 5. 迁移后校验
### 5.1 校验旧键已清理
`htyws_local``htykc_local` 分别执行:
```sql
-- 应返回 0:仍包含 qumu_* 的元素数量
SELECT count(*) AS remaining_qumu_key_rows
FROM (
SELECT item
FROM daka, jsonb_array_elements(daka.course_sections->'vals') AS item
WHERE daka.course_sections IS NOT NULL
UNION ALL
SELECT item
FROM jihua, jsonb_array_elements(jihua.course_sections->'vals') AS item
WHERE jihua.course_sections IS NOT NULL
) t
WHERE item ? 'qumu_id'
OR item ? 'qumu_type'
OR item ? 'qumu_name'
OR item ? 'qumu_text'
OR item ? 'qumu_category_key'
OR item ? 'qumu_category_name';
```
`htykc_local` 可单独查:
```sql
SELECT count(*) AS remaining_qumu_key_rows
FROM (
SELECT item
FROM kecheng, jsonb_array_elements(kecheng.course_sections->'vals') AS item
WHERE kecheng.course_sections IS NOT NULL
) t
WHERE item ? 'qumu_id'
OR item ? 'qumu_type'
OR item ? 'qumu_name'
OR item ? 'qumu_text'
OR item ? 'qumu_category_key'
OR item ? 'qumu_category_name';
```
### 5.2 校验新键可读
```sql
SELECT count(*) AS rows_with_course_id
FROM (
SELECT item
FROM daka, jsonb_array_elements(daka.course_sections->'vals') AS item
WHERE daka.course_sections IS NOT NULL
UNION ALL
SELECT item
FROM jihua, jsonb_array_elements(jihua.course_sections->'vals') AS item
WHERE jihua.course_sections IS NOT NULL
) t
WHERE item ? 'course_id';
```
## 6. 回滚方案
若需要回滚 JSONB 键名,可执行:
```bash
cd huike-back/htyws_models
DATABASE_URL=postgres://htyws@localhost/htyws_local diesel migration revert
cd huike-back/htykc_models
DATABASE_URL=postgres://htykc@localhost/htykc_local diesel migration revert
```
对应 `down.sql` 会将 `course_*` 键改回 `qumu_*`
## 7. 上线建议顺序
1. 先部署支持新字段命名的后端代码。
2. 执行 DB migration(表/列 + JSONB 键)。
3. 执行上述 SQL 校验。
4. 再部署前端。
5. 观察日志与关键功能(课程、课程分组、计划、打卡、排课)至少一个业务周期。
## 8. 风险提示
- JSONB 迁移依赖 `course_sections->'vals'` 是数组结构;若历史脏数据不满足该结构,可能迁移失败。
- 执行前建议做 DB 备份或快照。
- 若线上写流量较高,建议在低峰期执行并观察慢查询。