基于TypeORM+PostgreSQL实现记录版本控制与变更审批方案咨询
版本控制与变更审批方案优化
现有双实体方案的问题
你当前用两个相同实体分别存储待修改和已审批数据的方案,存在以下明显缺陷:
- 数据冗余:相同结构的表存储重复数据,浪费存储空间
- 同步复杂:审批时需要全量复制数据,容易出现字段遗漏或同步错误
- 无版本追溯:无法记录历史修改记录,只能保留当前待审批和已审批两个状态
基于VersionEntity的优化实现
你构思的VersionEntity思路方向是对的,但现有结构需要调整,核心是让它与业务实体一对一关联,记录每个业务实体的当前活跃版本号,同时配合单独的「业务版本表」存储各版本的具体数据。
1. 调整实体结构
业务版本表(以商品为例)
存储每个版本的业务数据及审批状态:
@Entity({ name: 'product_version' }) export class ProductVersionEntity { @PrimaryGeneratedColumn() id: number; @Column() productId: number; // 关联主业务实体的ID @Column() versionNumber: number; // 版本号,按业务实体自增 @Column() name: string; // 业务字段示例 @Column() price: number; // 业务字段示例 @Column({ default: 'pending_approval' }) status: 'draft' | 'pending_approval' | 'approved'; // 版本状态 }
版本控制实体(VersionEntity)
记录每个业务实体的当前活跃版本:
@Entity({ name: 'version' }) export class VersionEntity { @PrimaryColumn() // 用业务实体ID作为主键,实现一对一关联 entityId: number; // 对应productId @Column() activeVersionNumber: number; // 当前生效的版本号 @Column() entityType: string; // 可选,区分不同业务实体(如'product'、'order') }
2. 记录生成与流程逻辑
首次创建业务实体:
- 插入主业务实体(如
Product)记录,获取productId - 插入
ProductVersionEntity,versionNumber设为1,status设为approved - 插入对应的
VersionEntity,entityId为productId,activeVersionNumber设为1
- 插入主业务实体(如
用户提交修改:
- 查询该
productId对应的最大versionNumber,加1得到新版本号 - 插入新的
ProductVersionEntity,携带修改后的数据,status设为pending_approval
- 查询该
管理员审批通过:
- 更新该
ProductVersionEntity的status为approved - 更新对应的
VersionEntity,将activeVersionNumber改为新版本号
- 更新该
@VersionColumn的局限性
@VersionColumn是TypeORM提供的乐观锁机制,核心作用是防止并发修改冲突,每次更新时版本号自动加1,但它无法存储历史版本数据,也不支持审批状态管理,因此单独使用无法满足你的需求,必须配合其他表结构才能实现版本追溯与审批。
其他可行方案
1. 单表多版本模式
无需额外的VersionEntity,直接在业务表中增加版本、状态、业务标识字段,每次修改插入新记录而非更新旧记录:
@Entity({ name: 'product' }) export class ProductEntity { @PrimaryGeneratedColumn() id: number; @Column() businessId: string; // 业务唯一标识,同一实体的所有版本共享此ID @Column() versionNumber: number; @Column({ default: 'pending_approval' }) status: 'draft' | 'pending_approval' | 'approved'; @Column({ default: false }) isActive: boolean; // 标记当前生效版本 // 业务字段 @Column() name: string; @Column() price: number; }
- 审批时:将旧版本的
isActive设为false,新版本设为true,同时更新status为approved - 查询活跃版本:通过
businessId和isActive=true过滤
2. PostgreSQL JSONB存储历史快照
如果历史版本的查询需求较少,可以在主表中新增一个JSONB类型的字段,存储所有历史版本的快照,同时记录当前活跃版本的索引:
@Entity({ name: 'product' }) export class ProductEntity { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() price: number; @Column({ type: 'jsonb', default: [] }) versionHistory: Array<{ versionNumber: number; data: Record<string, any>; status: string; createTime: Date; }>; @Column() activeVersion: number; }
- 优点:无需额外表结构
- 缺点:JSONB中复杂字段的查询、修改效率较低,不适合频繁查询历史版本的场景
3. 数据库触发器实现版本记录
在PostgreSQL中给主表创建触发器,每次更新时自动将旧数据插入到历史表,再配合单独的审批表记录审批状态:
-- 创建历史表 CREATE TABLE product_history ( id SERIAL PRIMARY KEY, product_id INT REFERENCES product(id), version_number INT, name VARCHAR(255), price NUMERIC, create_time TIMESTAMP DEFAULT NOW() ); -- 创建触发器函数 CREATE OR REPLACE FUNCTION save_product_history() RETURNS TRIGGER AS $$ BEGIN INSERT INTO product_history (product_id, version_number, name, price) VALUES (OLD.id, OLD.version_number, OLD.name, OLD.price); RETURN NEW; END; $$ LANGUAGE plpgsql; -- 绑定触发器到主表 CREATE TRIGGER trigger_product_history BEFORE UPDATE ON product FOR EACH ROW EXECUTE FUNCTION save_product_history();
- 优点:无需代码层面处理版本记录
- 缺点:业务逻辑与数据库耦合,审批流程仍需代码实现,灵活性较差
内容的提问来源于stack exchange,提问作者danyalutsevich
相关产品推荐
相关产品推荐

