diff --git a/AGENTS.md b/AGENTS.md index a2835f932..51c1fc812 100755 --- a/AGENTS.md +++ b/AGENTS.md @@ -189,6 +189,16 @@ Harness: .harness/ (init.sh, PROGRESS.md, feature_list.json, ...) ⑤ 统计数据:检查聚合 SQL → 确认统计包含新状态 ``` +### 数据库变更必须通过 Flyway 迁移(铁律) +凡涉及**新建表、新增字段、修改字段、加索引**等 DDL 变更,**必须**通过 Flyway 框架实现: +1. 在 `openhis-server-new/openhis-application/src/main/resources/db/migration/` 创建 `V{n}__描述.sql` +2. 版本号递增(`V2`, `V3`, `V4`...),双下划线分隔 +3. **禁止**直接在数据库执行 DDL 而不创建迁移文件 +4. **禁止**修改已执行的迁移文件(Flyway 会校验 checksum) +5. 新表必须包含:`tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `valid_flag` +6. 多租户表还需在 `MybatisPlusConfig.java` 的 `TENANT_TABLES` 中注册 +7. 详细使用指南见 `docs/FLYWAY_USAGE_GUIDE.md` + ### 禁止修改已有公开方法签名 - 不能删除或重命名已有的 public 方法 - 不能修改已有方法的参数列表 diff --git a/docs/FLYWAY_USAGE_GUIDE.md b/docs/FLYWAY_USAGE_GUIDE.md new file mode 100644 index 000000000..5b3436128 --- /dev/null +++ b/docs/FLYWAY_USAGE_GUIDE.md @@ -0,0 +1,326 @@ +# Flyway 数据库迁移使用指南 + +> **项目**: OpenHIS 医院管理系统 +> **数据库**: PostgreSQL 192.168.110.252:15432 (schema: hisdev) +> **Flyway 版本**: 8.5.x (Spring Boot 2.7 管理) +> **编制日期**: 2026-06-04 + +--- + +## 一、当前配置 + +| 配置项 | 值 | 说明 | +|---|---|---| +| `spring.flyway.enabled` | `true` | 启用 Flyway | +| `spring.flyway.baseline-on-migrate` | `true` | 首次启用时对现有表建基线 | +| `spring.flyway.baseline-version` | `0` | 基线版本号 | +| `spring.flyway.locations` | `classpath:db/migration` | 迁移文件目录 | +| `spring.flyway.validate-on-migrate` | `true` | 执行前校验 | + +**迁移文件目录:** +``` +openhis-server-new/openhis-application/src/main/resources/db/migration/ +``` + +**当前状态:** +``` +V0 << Flyway Baseline >> (自动基线,覆盖现有所有表) +V1 baseline_marker (空标记文件) +``` + +--- + +## 二、文件命名规范 + +``` +V{版本号}__{描述}.sql +``` + +| 规则 | 示例 | 说明 | +|---|---|---| +| 版本号必须递增 | `V2`, `V3`, `V4` | 整数,不可重复 | +| 双下划线分隔 | `V2__add_column.sql` | 单下划线会被当作版本号一部分 | +| 描述用下划线连接 | `V2__add_user_avatar.sql` | 不要用空格或中文 | +| 大小写敏感 | `V2__Add_Column.sql` | 建议全小写 | + +**✅ 正确示例:** +``` +V2__add_practitioner_avatar.sql +V3__create_nurse_station_table.sql +V4__modify_encounter_diagnosis_index.sql +V5__add_yb_catalog_fields.sql +``` + +**❌ 错误示例:** +``` +v2__add_column.sql # 版本号必须大写 V +V2 add column.sql # 缺少双下划线 +V2__Add Column.sql # 描述中有空格 +V2.1__add_column.sql # 不支持小数版本号 +``` + +--- + +## 三、新增表(完整示例) + +### 场景:新建一个「手术排班统计」表 + +**Step 1:创建迁移文件** + +```sql +-- 文件:db/migration/V2__create_surgery_schedule_stats.sql + +CREATE TABLE IF NOT EXISTS surgery_schedule_stats ( + id BIGINT PRIMARY KEY, + schedule_id BIGINT NOT NULL COMMENT '排程ID', + doctor_code VARCHAR(64) COMMENT '医生编码', + surgery_count INT DEFAULT 0 COMMENT '手术数量', + total_duration INT DEFAULT 0 COMMENT '总时长(分钟)', + tenant_id INT DEFAULT 1 COMMENT '租户ID', + create_by VARCHAR(64) DEFAULT 'system' COMMENT '创建人', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + update_by VARCHAR(64) COMMENT '更新人', + update_time TIMESTAMP COMMENT '更新时间', + valid_flag INT DEFAULT 1 COMMENT '有效标志 1=有效 0=无效' +); + +COMMENT ON TABLE surgery_schedule_stats IS '手术排班统计表'; +CREATE INDEX idx_surgery_stats_schedule ON surgery_schedule_stats(schedule_id); +CREATE INDEX idx_surgery_stats_tenant ON surgery_schedule_stats(tenant_id); +``` + +**Step 2:启动应用** + +```bash +cd openhis-server-new +mvn clean package -DskipTests +java -jar openhis-application/target/openhis-application.jar --spring.profiles.active=dev --server.port=18082 +``` + +**Step 3:Flyway 自动执行** + +启动日志中会看到: +``` +Flyway 迁移完成,执行了 1 个迁移 +``` + +数据库中 `flyway_schema_history` 表新增一条记录: +``` +installed_rank | version | description | type | success +---------------+---------+--------------------------+------+--------- +3 | 2 | create surgery schedule.. | SQL | t +``` + +--- + +## 四、修改表结构(ALTER) + +### 场景:给 practitioners 表加一个 phone 字段 + +```sql +-- 文件:db/migration/V3__add_practitioner_phone.sql + +-- PostgreSQL +ALTER TABLE practitioner ADD COLUMN IF NOT EXISTS phone VARCHAR(32) COMMENT '联系电话'; +``` + +### 场景:给表加索引 + +```sql +-- 文件:db/migration/V4__add_encounter_index.sql + +CREATE INDEX IF NOT EXISTS idx_encounter_patient ON adm_encounter(patient_id); +CREATE INDEX IF NOT EXISTS idx_encounter_tenant ON adm_encounter(tenant_id); +``` + +### 场景:修改字段类型 + +```sql +-- 文件:db/migration/V5__extend_charge_item_code.sql + +-- PostgreSQL: 修改 varchar 长度 +ALTER TABLE adm_charge_item ALTER COLUMN charge_item_code TYPE VARCHAR(128); +``` + +--- + +## 五、多租户表迁移 + +项目有 50+ 张多租户表(`tenant_id` 字段),新增的多租户表需要: + +```sql +-- 文件:db/migration/V6__create_clinic_referral_table.sql + +CREATE TABLE IF NOT EXISTS clinic_referral ( + id BIGINT PRIMARY KEY, + encounter_id BIGINT NOT NULL, + referral_reason TEXT, + tenant_id INT DEFAULT 1, + create_by VARCHAR(64) DEFAULT 'system', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + valid_flag INT DEFAULT 1 +); + +COMMENT ON TABLE clinic_referral IS '转诊记录表'; +``` + +然后在 `MybatisPlusConfig.java` 的 `TENANT_TABLES` 集合中添加表名: + +```java +private static final Set TENANT_TABLES = new HashSet<>(Arrays.asList( + // ... 现有表 ... + "clinic_referral" // 新增 +)); +``` + +--- + +## 六、开发规范 + +### 必须遵守 + +| 规则 | 原因 | +|---|---| +| **不要修改已执行的迁移文件** | Flyway 会校验 checksum,修改后启动报错 | +| **版本号只能递增** | 不能回退版本号 | +| **每次只改一个表** | 方便回滚和排查 | +| **使用 `IF NOT EXISTS`** | 防止重复执行报错 | +| **迁移文件加入 Git** | 全团队共享迁移历史 | + +### 推荐做法 + +| 做法 | 说明 | +|---|---| +| 先在测试环境验证 | 生产部署前确认迁移无误 | +| 一个迁移文件改一张表 | 便于追踪和回滚 | +| 文件名描述清晰 | `V2__add_yb_catalog_drug_name.sql` 比 `V2__update.sql` 好 | +| DDL 和 DML 分开 | 建表用 `V2__create_xxx.sql`,数据初始化用 `V3__init_xxx_data.sql` | + +--- + +## 七、回滚方案 + +Flyway **不支持自动回滚**,需要手动处理: + +### 情况 1:迁移刚执行,还没提交代码 + +```bash +# 1. 删除 flyway_schema_history 中的记录 +PGPASSWORD=Jchl1528 psql -h 192.168.110.252 -p 15432 -U postgresql -d postgresql \ + -c "SET search_path TO hisdev; DELETE FROM flyway_schema_history WHERE version = '6';" + +# 2. 手动撤销 DDL +PGPASSWORD=Jchl1528 psql -h 192.168.110.252 -p 15432 -U postgresql -d postgresql \ + -c "SET search_path TO hisdev; DROP TABLE IF EXISTS clinic_referral;" + +# 3. 删除迁移文件 +rm openhis-server-new/openhis-application/src/main/resources/db/migration/V6__create_clinic_referral_table.sql + +# 4. 重启应用 +``` + +### 情况 2:已提交代码,需要紧急回滚 + +```sql +-- 手动执行逆向 SQL +DROP TABLE IF EXISTS clinic_referral; +DELETE FROM flyway_schema_history WHERE version = '6'; +``` + +--- + +## 八、常用排查命令 + +```sql +-- 查看所有已执行的迁移 +SELECT installed_rank, version, description, type, success, installed_on +FROM flyway_schema_history +ORDER BY installed_rank; + +-- 查看是否有失败的迁移 +SELECT * FROM flyway_schema_history WHERE success = false; + +-- 查看当前最新版本 +SELECT MAX(version) AS current_version FROM flyway_schema_history; + +-- 手动标记某版本为成功(紧急修复用) +-- UPDATE flyway_schema_history SET success = true WHERE version = '6'; +``` + +--- + +## 九、文件清单 + +``` +openhis-server-new/openhis-application/src/main/resources/db/migration/ +├── README.md # 使用说明 +├── V1__baseline_marker.sql # 基线标记(空文件) +├── V2__xxx.sql # 你的第一个迁移 +├── V3__xxx.sql # 第二个迁移 +└── ... +``` + +--- + +## 十、注意事项(HIS 系统特有) + +| 场景 | 处理方式 | +|---|---| +| **新功能开发** | 建表/改表时创建 `V{n}__xxx.sql` | +| **代码生成器生成的表** | 生成后把 DDL 放入迁移文件 | +| **Flowable 工作流表** | 由 Flowable 自己管理,不要用 Flyway 管 | +| **多租户字段** | 新表必须加 `tenant_id INT DEFAULT 1` | +| **逻辑删除字段** | 新表必须加 `valid_flag INT DEFAULT 1` | +| **审计字段** | 新表必须加 `create_by`, `create_time`, `update_by`, `update_time` | + +--- + +## 十一、PostgreSQL 常用 DDL 速查 + +### 建表模板 + +```sql +CREATE TABLE IF NOT EXISTS {表名} ( + id BIGINT PRIMARY KEY, + {字段} {类型} {默认值} COMMENT '{说明}', + tenant_id INT DEFAULT 1 COMMENT '租户ID', + create_by VARCHAR(64) DEFAULT 'system' COMMENT '创建人', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + update_by VARCHAR(64) COMMENT '更新人', + update_time TIMESTAMP COMMENT '更新时间', + valid_flag INT DEFAULT 1 COMMENT '有效标志 1=有效 0=无效' +); + +COMMENT ON TABLE {表名} IS '{表说明}'; +CREATE INDEX idx_{表名}_{字段} ON {表名}({字段}); +``` + +### 加字段 + +```sql +ALTER TABLE {表名} ADD COLUMN IF NOT EXISTS {字段} {类型} COMMENT '{说明}'; +``` + +### 加索引 + +```sql +CREATE INDEX IF NOT EXISTS idx_{表名}_{字段} ON {表名}({字段}); +``` + +### 改字段类型 + +```sql +ALTER TABLE {表名} ALTER COLUMN {字段} TYPE {新类型}; +``` + +### 删字段 + +```sql +ALTER TABLE {表名} DROP COLUMN IF EXISTS {字段}; +``` + +### 删表 + +```sql +DROP TABLE IF EXISTS {表名}; +```