You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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. 记录生成与流程逻辑

  • 首次创建业务实体:

    1. 插入主业务实体(如Product)记录,获取productId
    2. 插入ProductVersionEntity,versionNumber设为1,status设为approved
    3. 插入对应的VersionEntity,entityId为productId,activeVersionNumber设为1
  • 用户提交修改:

    1. 查询该productId对应的最大versionNumber,加1得到新版本号
    2. 插入新的ProductVersionEntity,携带修改后的数据,status设为pending_approval
  • 管理员审批通过:

    1. 更新该ProductVersionEntity的status为approved
    2. 更新对应的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:14:55