# 教学域命名重构: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 备份或快照。 - 若线上写流量较高,建议在低峰期执行并观察慢查询。