增量更新方案,避免新的版本全量复制
1.整体核心流程
一、整体核心架构:方案主表 + 子表 Delta 链
1. 核心表关系(树状结构)
t_quality_control_plan(方案主表,版本链载体)
├── t_plan_process_rel(工序)
├── t_plan_facility_rel(设施)
├── t_plan_main_ledger_rel(主要计量器具)
├── t_plan_virtual_ledger_rel(虚拟计算)
├── t_plan_flue_gas_device_rel(烟气监测设备)
├── t_plan_emission_source_rel(排放源)
│ ├── t_plan_emission_source_facility_rel(排放源-设施)
│ ├── t_plan_emission_source_ledger_rel(实体计量器具)
│ │ └── t_plan_emission_source_ledger_ref_item(引用明细)
│ ├── t_plan_emission_source_virtual_ledger_rel(虚拟计量器具)
│ └── t_plan_emission_source_ef_config(排放因子,非Delta,直接覆盖)
-
主表
t_quality_control_plan:通过parent_plan_id串联版本链,比如V1(id=100) ← V2(id=200) ← V3(id=300),每个版本都是一条独立记录。 -
所有子表:均采用 Delta 增量日志模式,不修改历史数据,仅新增变更记录,通过公共字段实现版本控制。
二、子表公共版本控制字段(核心契约)
| 字段 | 类型 | 说明 |
|---|---|---|
id |
bigint | 物理主键,自增,每条记录唯一标识 |
plan_id |
bigint | 所属方案版本 ID,决定这条记录 归哪个版本管 |
operate_type |
int | 操作类型: 1 = 新增 (ADD),2 = 修改 (MODIFY),3 = 删除墓碑 (DELETE),4 = 恢复 (RESTORE) |
source_id |
bigint | 逻辑根 ID,始终指向同一条业务数据的最初那条记录,用于合并时分组 |
prev_id |
bigint | 前驱记录 ID,指向被操作的上一条记录,用于版本链追溯 |
status |
int | 状态:0 = 正常,2 = 已逻辑删除 |
三、新增/修改/删除/恢复
todo(数据示例+流程图介绍)
3.1 新增(ADD)
直接插入一条全新记录,source_id 和 prev_id 均为 null,表示这是业务数据的首次创建。
INSERT INTO t_plan_process_rel
(plan_id, operate_type, source_id, prev_id, ...)
VALUES
(200, 1, null, null, ...);
3.2 修改(MODIFY,分两种场景)
场景 1:跨版本编辑(编辑的记录plan_id≠当前版本)
核心规则:不改动原记录,新插入一行 MODIFY Delta
-- 原记录id=1(属于V1),在V2中修改
INSERT INTO t_plan_process_rel
(plan_id, operate_type, source_id, prev_id, ...)
VALUES
(200, 2, 1, 1, ...);
-
source_id指向逻辑根(原记录的source_id,无则用原记录id) -
prev_id指向被修改的原记录id,形成版本链
场景 2:同版本编辑(编辑的记录plan_id= 当前版本)
核心规则:直接 UPDATE 原记录,不新增 Delta
-- 当前V2版本下,新增的id=5的工序C,直接修改
UPDATE t_plan_process_rel SET ... WHERE id=5;
3.3 删除(DELETE,分三种场景)
场景 1:删除继承记录(plan_id≠当前版本)
核心规则:原记录不动,插入 DELETE 墓碑记录
INSERT INTO t_plan_process_rel
(plan_id, operate_type, source_id, prev_id, status)
VALUES
(200, 3, 原rootId, 原id, 0);
- 墓碑记录会在合并时覆盖原 ADD 记录,实现 “逻辑删除” 效果
场景 2:删除同版本的 MODIFY Delta
核心规则:同样插入 DELETE 墓碑,遮蔽继承的原始记录
INSERT INTO t_plan_process_rel
(plan_id, operate_type, source_id, prev_id)
VALUES
(200, 3, 原rootId, 原id);
场景 3:删除同版本的 ADD 记录
核心规则:直接物理删除,或逻辑删除(status=2)
-- 物理删除
DELETE FROM t_plan_process_rel WHERE id=5;
-- 或逻辑删除
UPDATE t_plan_process_rel SET status=2 WHERE id=5;
3.4 恢复(RESTORE)
核心规则:针对已删除的记录,插入一条**operate_type=4**的记录,撤销 DELETE 墓碑
《场景》
配置排放源:删除某一个排放源,又重新添加回来,因为排放源还关联很多数据(设施/计量器具/排放因子),配置排放源页面又容易误删除操作,因此设计恢复功能,删除后,又重新添加回来,所关联的数据不会进行删除。
INSERT INTO t_plan_process_rel
(plan_id, operate_type, source_id, prev_id)
VALUES
(200, 4, 原DELETE的source_id, 原DELETE的id);
四、查询(核心合并逻辑)
todo(数据示例+流程图介绍)
以 t_plan_process_rel 为例,场景:
- v1【新增工序A、新增工序B】
- v2【修改工序A、删除工序B、新增工序C】
1. 原始数据示例
| id | plan_id | operate_type | source_id | prev_id | 业务字段 (name 等) | 说明 |
|---|---|---|---|---|---|---|
| 1 | 100(V1) | 1(ADD) | null | null | 工序 A | ← V1 新增 |
| 2 | 100(V1) | 1(ADD) | null | null | 工序 B | ← V1 新增 |
| 3 | 200(V2) | 2(MODIFY) | 1 | 1 | 工序 A (改过分厂) | ← V2 修改 A |
| 4 | 200(V2) | 3(DELETE) | 2 | 2 | (无业务数据) | ← V2 删除 B |
| 5 | 200(V2) | 1(ADD) | null | null | 工序 C | ← V2 新增 C |
2. resolve(planId=200) 合并过程
-
收集版本链数据:查询
plan_id IN (100, 200)的所有记录(通过主表版本链回溯)。 -
按逻辑根分组:按
source_id(无则用自身id)分组,同一条业务数据的所有版本归为一组:-
logicalId=1→id=1和id=3(MODIFY 覆盖 ADD) -
logicalId=2→id=2和id=4(DELETE 覆盖 ADD) -
logicalId=5→id=5(新增,无竞争)
-
-
过滤无效记录:遇到
operate_type=3(DELETE 墓碑)的分组,直接过滤掉。 -
生成有效视图:最终得到 V2 的有效数据:
id=3(工序A改后)、id=5(工序C)。
五、版本链追溯(prev_id 的作用)
通过 prev_id 可以完整回溯同一条业务数据的所有变更历史,形成链式轨迹。
示例:同一逻辑记录在 3 个版本中的演变:
-
V1:
id=1, source_id=null, prev_id=null→ 首次创建 -
V2:
id=6, source_id=1, prev_id=1→ 修改了id=1的记录 -
V3:
id=12, source_id=1, prev_id=6→ 修改了id=6的记录 -
source_id始终指向根记录(id=1),用于合并分组 -
prev_id形成链式追溯:12 → 6 → 1 → null,可完整还原变更过程
六、方案主表版本链(parent_plan_id 的作用)
主表 t_quality_control_plan 通过 parent_plan_id 串联版本,示例:
| id | parent_plan_id | state | version | max_ver | 说明 |
|---|---|---|---|---|---|
| 100 | null | 2 | 1 | 2 | ← V1 历史版本 |
| 200 | 100 | 1 | 2 | 2 | ← V2 应用中 |
构建版本链的方式:从当前版本(如200)开始,沿parent_plan_id回溯,得到链 [100, 200],查询子表时需包含链上所有plan_id的记录。
一句话总结
所有子表共用一套 Delta 机制:
-
plan_id标识版本归属 -
operate_type标识操作类型 -
source_id做合并去重 -
prev_id服务版本追溯
查询时通过resolve()沿版本链收集所有记录,按source_id分组取最新、过滤 DELETE 墓碑,得到当前版本的有效视图。
2.后端代码层面
接入方式
实现原理
(查询/新增/修改/删除/恢复)
resolve()方法的核心实现。。。
评论区