1. 核心设计思想
我们采用“草稿-发布”分离的模式来管理智能助手的不同版本。其核心思想如下:
-
分离存储:系统中同时存在两种状态的智能助手,通过两个独立的数据库表进行管理:
- 草稿 (
assistant表):这是主要的工作表,包含了所有版本的智能助手,无论是草稿状态还是已发布状态。通过is_published字段来区分。草稿是编辑中的版本,对它的任何修改都不会影响线上正在运行的版本。 - 发布 (
assistant_published表):这张表可以看作是线上服务版本的“指针”或“快照”集合。当一个草稿被发布时,系统会将该草稿版本的数据复制一份到这张表中,作为线上服务的权威数据源。
- 草稿 (
-
版本关联:通过
parent_id和assistant_id字段建立草稿与发布版本之间的关联。assistant表中的parent_id指向assistant_published表的id,用于标识一个草稿是从哪个发布版本创建的。assistant_published表中的assistant_id指向assistant表的id,用于标识当前线上版本对应的是哪一条具体的记录。
-
原子化操作:发布、下线、创建草稿等核心操作都封装在独立的业务逻辑层(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. 工作流总结
典型的版本管理工作流如下:
- 首次创建:用户创建一个新的智能助手,此时它是一个
is_published=False的草稿,parent_id为None。 - 首次发布:用户调用发布接口。
- 系统将此草稿的
is_published状态变为True。 - 将草稿数据复制到
assistant_published表,创建一条新记录。 - 将原草稿的
parent_id更新为新创建的assistant_published记录的 ID。
- 系统将此草稿的
- 创建新草稿:用户需要修改线上版本时,调用创建草稿接口。
- 系统会基于
assistant_published的数据,生成一个新的、is_published=False的草稿记录。 - 这个新草稿的
parent_id指向它所来源的assistant_published记录。
- 系统会基于
- 编辑草稿:用户对新的草稿进行任意修改,这些修改不会影响线上版本。
- 发布新版本:当新草稿修改完成后,用户再次调用发布接口。
- 系统找到旧的
assistant_published记录并删除它。 - 将旧的
assistant记录的is_published状态置为False。 - 将当前要发布的新草稿
is_published变为True。 - 用这个新草稿的数据创建一条新的
assistant_published记录,实现版本的无缝切换。 - 更新新草稿的
parent_id指向这个最新的assistant_published记录。
- 系统找到旧的
- 下线:如果需要,可以随时将线上版本下线,使其变回草稿状态。
通过以上设计,我们实现了一个健壮、安全且易于管理的版本控制系统,确保了线上服务的稳定性和内容更新的灵活性。