Files
hms/wiki/database.md
iven 1602b7bbad
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
docs(wiki): Wiki 全面刷新 + Q2 路线图 + 测试补强设计规格
- Wiki 7 文件关键数字刷新:迁移 96→103、实体 45→46、前端 163→225、测试 5→36
- 修复 architecture.md PostgreSQL 版本不一致(18→16)
- 修复 erp-ai.md 实体数 3→6、erp-health.md 实体数 45→46
- 更新 index.md 文档索引:specs 41、plans 38、discussions 18
- 新增事件注册表/方法论/分析报告引用
- 新增页面/组件测试设计规格(模式化工厂方案)
- 新增 Q2 路线图规格(技术债 + 新功能并行 8 周)
2026-05-03 22:33:08 +08:00

156 lines
7.1 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.
---
title: 数据库迁移与模式
updated: 2026-04-28
status: stable
tags: [database, seaorm, migration, multi-tenant]
---
# 数据库迁移与模式
> 从 [[index]] 导航。关联: [[erp-core]] [[erp-server]] [[infrastructure]] [[erp-health]]
## 1. 设计决策
- **SeaORM Migration** — 异步、类型安全、幂等(`if_not_exists`),每个迁移必须实现 `down()` 可回滚
- **所有表必须含标准字段** — `id`(UUIDv7), `tenant_id`, `created_at`, `updated_at`, `created_by`, `updated_by`, `deleted_at`, `version`
- **软删除** — 不硬删除,设置 `deleted_at` 时间戳
- **乐观锁** — 更新时检查 `version` 字段
- **多租户** — 所有业务表含 `tenant_id`,中间件自动过滤
## 2. 关键文件 + 数据流
### 核心文件
| 文件 | 职责 |
|------|------|
| `crates/erp-server/migration/src/lib.rs` | Migrator 注册所有迁移 |
| `crates/erp-server/migration/src/m*.rs` | 103 个迁移文件 |
| `crates/erp-core/src/types.rs` | BaseFields 标准字段定义 |
### 迁移命名规则
```
m{YYYYMMDD}_{6位序号}_{描述}.rs
例: m20260410_000001_create_tenant.rs
```
### 当前表概览70+ 张)
| 模块 | 表 |
|------|-----|
| 基础 | tenant, tenant_crypto_keys |
| 认证 (auth) | users, user_credentials, user_tokens, roles, permissions, role_permissions, user_roles, organizations, departments, positions, user_departments |
| 配置 (config) | dictionaries, dictionary_items, menus, menu_roles, settings, numbering_rules |
| 工作流 (workflow) | process_definitions, process_instances, tokens, tasks, process_variables |
| 消息 (message) | message_templates, messages, message_subscriptions |
| 审计 | audit_logs, domain_events |
| 插件 (plugin) | plugins, entity_registry, plugin_market, plugin_user_views |
| **健康 (health)** | patient, patient_family_member, patient_tag, patient_tag_relation, patient_doctor_relation, doctor_profile, health_record, vital_signs, daily_monitoring, lab_report, health_trend, diagnosis, dialysis_record, critical_value_thresholds, consent, appointment, doctor_schedule, follow_up_task, follow_up_record, consultation_session, consultation_message |
| **内容 (article)** | article, article_category, article_tag, article_article_tag, article_revision |
| **积分 (points)** | points_account, points_rule, points_product, points_order, points_transaction, points_checkin |
| **线下活动** | offline_event, offline_event_registration |
| **AI (ai)** | ai_prompt, ai_analysis, ai_usage |
| **微信 (wechat)** | wechat_users |
### 健康模块迁移m000042 - m000072
| 迁移 | 变更 |
|------|------|
| m000042 | 创建 17 张健康业务表 |
| m000043 | 创建 wechat_users 表 |
| m000044 | 创建 article 表 |
| m000045 | 健康模块索引优化 |
| m000046 | 健康模块约束修复 |
| m000047 | 健康模块索引修复 |
| m000048 | 添加 patient.id_number_hash 列 |
| m000049 | 拓宽 patient.id_number 列 |
| m000050 | 添加 appointment.doctor_name 列 |
| m000051 | 透析/化验增强字段 |
| m000052 | 创建 AI 分析表ai_prompt/ai_analysis/ai_usage |
| m000053 | 创建积分商城表points_account/rule/product/order/transaction |
| m000054 | 创建日常监测表daily_monitoring |
| m000055 | 积分签到标准字段 |
| m000056 | 创建诊断表diagnosis |
| m000057 | 重命名 points_transaction 类型列 |
| m000058 | 合并 daily_monitoring 到 vital_signs |
| m000059 | 种子菜单数据 |
| m000060 | 创建危急值阈值表 |
| m000061 | 创建知情同意表 |
| m000062 | 创建租户加密密钥表tenant_crypto_keys |
| m000063 | 内容管理表article_category/article_tag/article_article_tag/article_revision |
| m000064-000068 | PII 加密扩展patient/consultation/follow_up/family_member/doctor_profile |
| m000069-000071 | 加密字段 key_versiondialysis_record/lab_report/diagnosis |
| m000072 | 拓宽加密手机号列 |
| m000073-000075 | 设备数据采集device_readings |
| m000076 | 告警系统alerts, alert_rules |
| m000077 | 危急值告警critical_alert, critical_alert_response |
| m000078-000079 | 随访模板follow_up_template, follow_up_template_field |
| m000080 | 用药记录medication_record |
| m000081 | 透析处方增强 |
| m000082 | AI 用量统计表增强 |
| m000083-000085 | 索引和约束优化 |
| m000086 | 启用 RLS 所有表 |
| m000087 | 审计日志哈希链 |
| m000088 | RLS 严格策略FORCE |
| m000089 | 盲索引blind_index 表) |
| m000090 | 告警阈值表增强 |
| m000091 | Dead Letter 事件表 |
| m000092-000100 | 健康业务扩展(日常监测/体征合并/透析/AI 分析) |
| m000101-000104 | 配置/索引优化/插件表扩展 |
### 集成契约
| 方向 | 模块 | 触发时机 |
|------|------|---------|
| 消费 ← | [[erp-server]] | 启动时自动运行 `Migrator::up()` |
| 依赖 ← | [[erp-core]] | BaseFields 定义标准字段规范 |
| 提供 → | 所有业务模块 | 表结构供 SeaORM Entity 使用 |
| 提供 → | [[erp-health]] | 44 张健康业务表 |
## 3. 代码逻辑
**不变量**: 所有业务表必须含 `tenant_id` 列 — 多租户是核心能力,不可事后补
**不变量**: 迁移必须幂等 — 使用 `if_not_exists`,可重复执行
**不变量**: 迁移执行由 erp-server 启动自动触发,不手动执行 SQL
**不变量**: 健康模块 PII 数据使用 AES-256-GCM 加密存储,身份证号额外 HMAC-SHA256 哈希
### 关键结构变更迁移
| 迁移 | 变更 |
|------|------|
| m000027 | 修复唯一索引 + 软删除冲突 |
| m000034 | 种子插件权限 |
| m000035 | pg_trgm 扩展 + entity 列 |
| m000036 | role_permissions 添加 data_scope行级数据权限 |
| m000038 | 修复 CRM 权限码 |
| m000039 | entity_registry 列 |
| m000041 | plugin_user_views |
| m000042 | 17 张健康业务表 |
| m000048 | patient.id_number_hash HMAC 列 |
## 4. 活跃问题 + 陷阱
### 历史教训
- 唯一索引 + 软删除冲突 — 已删除记录的 unique key 阻止新建m000027 修复)
- tenant 表缺少 `created_by`/`updated_by`/`version` 字段 — 首个迁移早于 BaseFields 规范
- wechat_users 表初始缺少标准字段列 — 需 ALTER TABLE 补充m000043 已包含完整字段)
⚠️ settings 表的唯一索引曾需修复m000032
⚠️ 新增表时务必对齐 `crates/erp-core/src/types.rs` 中的 BaseFields
⚠️ 已应用的迁移文件被删除会导致启动失败 — 需创建空 stub 迁移m000050 案例)
## 5. 变更记录
| 日期 | 变更 |
|------|------|
| 2026-05-01 | 更新至 96 迁移,刷新迁移文件数 |
| 2026-04-28 | 更新至 76 迁移,新增设备采集/告警/RLS/审计哈希链/盲索引/Dead Letter/透析处方等 19 个迁移 |
| 2026-04-26 | 更新至 72 迁移、67+ 表,新增积分商城/透析/诊断/内容管理/线下活动/PII 加密扩展等 22 个迁移 |
| 2026-04-25 | 更新至 50 迁移、48 表新增健康模块迁移m000042-m000050和 18 张健康业务表 |
| 2026-04-23 | 重构为 5 节结构,更新表清单至 41 个迁移 |
| 2026-04-19 | CRM 权限码修复迁移 (m000038) |