AI智能助手草稿与发布版本管理设计方案

59 阅读6分钟

1. 核心设计思想

我们采用“草稿-发布”分离的模式来管理智能助手的不同版本。其核心思想如下:

  1. 分离存储:系统中同时存在两种状态的智能助手,通过两个独立的数据库表进行管理:

    • 草稿 (assistant 表):这是主要的工作表,包含了所有版本的智能助手,无论是草稿状态还是已发布状态。通过 is_published 字段来区分。草稿是编辑中的版本,对它的任何修改都不会影响线上正在运行的版本。
    • 发布 (assistant_published 表):这张表可以看作是线上服务版本的“指针”或“快照”集合。当一个草稿被发布时,系统会将该草稿版本的数据复制一份到这张表中,作为线上服务的权威数据源。
  2. 版本关联:通过 parent_idassistant_id 字段建立草稿与发布版本之间的关联。

    • assistant 表中的 parent_id 指向 assistant_published 表的 id,用于标识一个草稿是从哪个发布版本创建的。
    • assistant_published 表中的 assistant_id 指向 assistant 表的 id,用于标识当前线上版本对应的是哪一条具体的记录。
  3. 原子化操作:发布、下线、创建草稿等核心操作都封装在独立的业务逻辑层(Service)中,确保数据在多个表之间操作时的一致性和完整性。

2. 数据模型 (models/assistant.py)

版本管理的核心是数据模型的设计。我们主要依赖以下两个模型:

Assistant 模型

该模型代表一个智能助手的具体实例,可以是草稿,也可以是已发布的版本。

# models/assistant.py

class Assistant(db.Model):
    # ...
    id = db.Column(db.Integer, primary_key=True)
    parent_id = db.Column(db.Integer, db.ForeignKey('assistant_published.id'), nullable=True, comment='父版本id')
    version = db.Column(db.Integer, nullable=False, default=1, comment='版本号')
    is_published = db.Column(db.Boolean, nullable=False, default=False, comment='是否已发布')
    status = db.Column(db.String(255), nullable=False, default=AssistantStatus.DRAFT.value, comment='状态')
    # ... (其他业务字段如 name, description, model_config 等)

    published_assistant = db.relationship('AssistantPublished', backref='assistants', foreign_keys=[parent_id])

关键字段说明

  • parent_id: 外键,指向 assistant_published.id。如果这是一个草稿,parent_id 指向它所基于的那个发布版本。
  • is_published: 状态标记。True 表示这是一个已发布的版本,False 表示这是一个草稿。
  • status: 状态枚举,如 DRAFT(草稿)、PUBLISHED(已发布)。

AssistantPublished 模型

该模型是 Assistant 模型的一个“快照”,专门用于存储当前线上唯一生效的发布版本。

# models/assistant.py

class AssistantPublished(db.Model):
    # ...
    id = db.Column(db.Integer, primary_key=True)
    assistant_id = db.Column(db.Integer, db.ForeignKey('assistant.id'), nullable=False, comment='助手id')
    # ... (其他业务字段,完全复制自 Assistant 模型)

    assistant = db.relationship('Assistant', foreign_keys=[assistant_id])

关键字段说明

  • id: 主键。这个 ID 会被 Assistant 模型的 parent_id 引用。
  • assistant_id: 外键,指向 assistant 表中已发布记录的 id

3. 核心业务逻辑 (services/assistant/assistant_service.py)

业务逻辑层封装了版本管理的全部复杂操作。

create_assistant_draft - 创建草稿

当用户需要为一个已发布的智能助手创建一个新的编辑版本时,调用此方法。

# services/assistant/assistant_service.py

class AssistantService:
    # ...
    @classmethod
    def create_assistant_draft(cls, published_assistant_id: int, operator: str):
        # 1. 查找发布版本
        published_assistant = AssistantPublished.query.filter_by(
            id=published_assistant_id,
            is_deleted=False
        ).first()
        if not published_assistant:
            raise AssistantNotFound()

        # 2. 复制数据到新的 Assistant 实例
        assistant_dict = published_assistant.to_dict(rules=('-id', '-created_at', '-updated_at'))
        new_draft = Assistant()
        for key, value in assistant_dict.items():
            setattr(new_draft, key, value)

        # 3. 设置草稿状态
        new_draft.is_published = False
        new_draft.status = AssistantStatus.DRAFT.value
        new_draft.parent_id = published_assistant_id  # 建立与父版本的关联
        new_draft.created_by = operator
        new_draft.updated_by = operator

        # 4. 保存草稿
        db.session.add(new_draft)
        db.session.commit()
        return new_draft

publish_assistant - 发布草稿

当一个草稿准备就绪,可以推向线上时,调用此方法。这是一个核心的复杂操作。

# services/assistant/assistant_service.py

class AssistantService:
    # ...
    @classmethod
    def publish_assistant(cls, assistant_id: int, operator: str):
        # 1. 查找待发布的草稿
        assistant = Assistant.query.filter_by(id=assistant_id, is_deleted=False).first()
        if not assistant:
            raise AssistantNotFound()
        
        # ... (省略权限校验)

        # 2. 如果是从一个已有版本创建的草稿,则处理旧的发布版本
        if assistant.parent_id:
            old_published_assistant = AssistantPublished.query.filter_by(id=assistant.parent_id).first()
            if old_published_assistant:
                # 2.1 下线旧的 Assistant 记录
                old_assistant = Assistant.query.filter_by(id=old_published_assistant.assistant_id).first()
                if old_assistant:
                    old_assistant.is_published = False
                    db.session.add(old_assistant)
                
                # 2.2 删除旧的 AssistantPublished 记录
                db.session.delete(old_published_assistant)

        # 3. 计算新版本号
        max_version = db.session.query(func.max(Assistant.version)).filter(
            Assistant.parent_id == assistant.parent_id,
            Assistant.is_deleted == False
        ).scalar() or 0
        assistant.version = max_version + 1

        # 4. 更新草稿状态为“已发布”
        assistant.is_published = True
        assistant.status = AssistantStatus.PUBLISHED.value
        assistant.published_at = datetime.now()
        assistant.updated_by = operator
        db.session.add(assistant)
        db.session.flush()

        # 5. 创建新的发布记录 (快照)
        published_assistant = AssistantPublished()
        assistant_dict = assistant.to_dict(rules=('-id', '-created_at', '-updated_at'))
        for key, value in assistant_dict.items():
            setattr(published_assistant, key, value)
        published_assistant.assistant_id = assistant.id
        db.session.add(published_assistant)
        db.session.flush()

        # 6. 更新 assistant 的 parent_id 指向新的发布记录
        assistant.parent_id = published_assistant.id
        db.session.add(assistant)
        db.session.commit()

        return assistant

unpublish_assistant - 下线

将一个已发布的智能助手从线上撤下,使其恢复为草稿状态。

# services/assistant/assistant_service.py

class AssistantService:
    # ...
    @classmethod
    def unpublish_assistant(cls, assistant_id: int, operator: str):
        # 1. 查找已发布的 Assistant 记录
        assistant = Assistant.query.filter_by(id=assistant_id, is_published=True, is_deleted=False).first()
        if not assistant:
            raise AssistantNotFound()

        # 2. 查找并删除对应的 AssistantPublished 记录
        published_assistant = AssistantPublished.query.filter_by(assistant_id=assistant.id).first()
        if published_assistant:
            db.session.delete(published_assistant)

        # 3. 更新状态为草稿
        assistant.is_published = False
        assistant.status = AssistantStatus.DRAFT.value
        assistant.updated_by = operator
        db.session.add(assistant)
        db.session.commit()

        return assistant

4. API 接口层 (controllers/admin/assistant.py)

接口层将业务逻辑暴露为 HTTP 端点,供前端管理后台调用。

  • POST /api/admin/assistants/<int:published_assistant_id>/drafts

    • 功能:创建草稿。
    • 调用AssistantService.create_assistant_draft()
  • POST /api/admin/assistants/<int:assistant_id>/publish

    • 功能:发布指定 ID 的草稿。
    • 调用AssistantService.publish_assistant()
  • POST /api/admin/assistants/<int:assistant_id>/unpublish

    • 功能:下线指定 ID 的已发布版本。
    • 调用AssistantService.unpublish_assistant()
  • GET /api/admin/assistants

    • 功能:获取智能助手列表。通过查询参数 is_published 可以筛选出草稿列表 (False) 或已发布版本列表 (True)。

5. 工作流总结

典型的版本管理工作流如下:

  1. 首次创建:用户创建一个新的智能助手,此时它是一个 is_published=False 的草稿,parent_idNone
  2. 首次发布:用户调用发布接口。
    • 系统将此草稿的 is_published 状态变为 True
    • 将草稿数据复制到 assistant_published 表,创建一条新记录。
    • 将原草稿的 parent_id 更新为新创建的 assistant_published 记录的 ID。
  3. 创建新草稿:用户需要修改线上版本时,调用创建草稿接口。
    • 系统会基于 assistant_published 的数据,生成一个新的、is_published=False 的草稿记录。
    • 这个新草稿的 parent_id 指向它所来源的 assistant_published 记录。
  4. 编辑草稿:用户对新的草稿进行任意修改,这些修改不会影响线上版本。
  5. 发布新版本:当新草稿修改完成后,用户再次调用发布接口。
    • 系统找到旧的 assistant_published 记录并删除它。
    • 将旧的 assistant 记录的 is_published 状态置为 False
    • 将当前要发布的新草稿 is_published 变为 True
    • 用这个新草稿的数据创建一条新的 assistant_published 记录,实现版本的无缝切换。
    • 更新新草稿的 parent_id 指向这个最新的 assistant_published 记录。
  6. 下线:如果需要,可以随时将线上版本下线,使其变回草稿状态。

通过以上设计,我们实现了一个健壮、安全且易于管理的版本控制系统,确保了线上服务的稳定性和内容更新的灵活性。