15dc2c1a3c
Rename remaining qumu_category db artifacts to course_category and document jsonb key migration procedure for existing production data. Made-with: Cursor
207 lines
5.4 KiB
Markdown
207 lines
5.4 KiB
Markdown
# 教学域命名重构: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 备份或快照。
|
||
- 若线上写流量较高,建议在低峰期执行并观察慢查询。
|