You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

7.8 KiB

Changelog: 项目资源自动关联实现

日期: 2026-02-08
类型: Feature / Enhancement
优先级: P1
关联文档:


📋 问题背景

核心问题(P1-01)

在 Phase 1 实现中,AI 拆解剧本后:

  • 角色/场景/道具元素已存储
  • 元素标签已创建(screenplay_element_tags
  • 分镜及分镜元素关联已创建(storyboardsstoryboard_items
  • project_resources 表未关联

业务影响

根据 ADR 01 的三层架构设计:

元素(Element)→ 标签(Tag)→ 资源(Resource)
screenplay_characters     screenplay_element_tags     project_resources
screenplay_locations  →   (统一标签表)         →   (图片/视频素材)
screenplay_props

缺失环节

  • 标签创建后,没有为其创建对应的 project_resources 记录
  • 用户无法通过 "素材管理" 界面查看 AI 拆解的元素
  • AI 生成图片功能缺少目标资源记录

架构约束

根据 ADR 01:

  • 资源归属于项目级project_id),而非剧本级
  • 多集项目共享资源库
  • 标签与资源是 1:N 关系(一个标签可有多张图片)

解决方案

1. 新增 _sync_storyboard_resources 方法

文件: server/app/services/screenplay_service.py

功能: 为 AI 拆解的所有标签自动创建占位符资源

方法签名:

async def _sync_storyboard_resources(
    self,
    screenplay_id: UUID,
    project_id: UUID,
    tag_id_maps: Dict[str, Dict[str, UUID]]
) -> int:
    """为标签创建占位符项目资源记录
    
    逻辑说明:
    1. 遍历所有标签(角色/场景/道具标签)
    2. 为每个标签创建一条 project_resources 记录(占位符)
    3. 不创建实际文件,仅关联 element_tag_id
    4. 后续由 AI 生成图片并更新 file_url
    """

关键特性:

  • 占位符机制: file_url = "placeholder://pending-ai-generation"
  • 去重逻辑: 检查 element_tag_id 是否已有关联资源
  • 元数据追踪: 标记 source: 'ai_screenplay_parsing', auto_generated: true
  • 冗余字段: 存储 element_nametag_label 便于查询
  • 事务安全: 使用 flush() 而非 commit(),依赖外层事务

2. 集成到 AI 解析流程

调用时机: 在 store_parsed_elements 方法的分镜创建后

# 5. 存储分镜(如果启用)
storyboard_ids = await self._create_storyboards_from_ai(...)

# 6. 同步项目资源关联(新增)
await self._sync_storyboard_resources(
    screenplay_id=screenplay_id,
    project_id=screenplay.project_id,
    tag_id_maps=tag_id_maps
)

3. 占位符资源示例

ProjectResource(
    project_resource_id=UUID_v7,
    project_id=project_id,
    name="孙悟空 - 青年",
    type=ResourceType.CHARACTER,  # 1
    description="AI 拆解剧本自动生成的 孙悟空 (青年) 占位符",
    file_url="placeholder://pending-ai-generation",  # 占位符
    checksum="pending",  # 占位符
    element_tag_id=tag_id,  # 关联标签
    element_name="孙悟空",  # 冗余字段
    tag_label="青年",  # 冗余字段
    created_by=screenplay.created_by,
    meta_data={
        'source': 'ai_screenplay_parsing',
        'screenplay_id': str(screenplay_id),
        'element_type': 1,  # CHARACTER
        'element_id': str(character_id),
        'status': 'pending',  # pending → generating → completed
        'auto_generated': True
    }
)

🧪 测试验证

测试文件

server/tests/integration/test_data_integrity.py

新增验证:项目资源占位符

# 验证 7: 项目资源占位符(project_resources)
stmt = select(ProjectResource).where(
    ProjectResource.project_id == subproject.id,
    ProjectResource.deleted_at.is_(None)
)
result = await db_session.execute(stmt)
resources = result.scalars().all()

assert len(resources) == 9, f"应创建 9 个占位符资源"

测试结果

✅ 项目资源占位符验证通过
   - 总资源数: 9 个
🎉 数据完整性测试通过!
📊 最终统计:
   - 角色: 3 个
   - 场景: 2 个
   - 道具: 2 个
   - 标签: 9 个
   - 分镜: 3 个
   - 元素关联: 9 个
   - 项目资源: 9 个 ← 新增验证

验证指标: 100% 通过

  • 创建资源数 = 标签数(9 个)
  • 每个标签一个占位符资源
  • 资源正确关联到 project_id

📊 技术影响分析

代码变更统计

文件 变更类型 行数
screenplay_service.py 新增方法 +130
screenplay_service.py 集成调用 +5
test_data_integrity.py 新增验证 +15

数据流程

AI 解析剧本
    ↓
创建元素(角色/场景/道具)
    ↓
创建标签(screenplay_element_tags)
    ↓
创建分镜(storyboards + storyboard_items)
    ↓
[新增] 创建占位符资源(project_resources)← 本次实现
    ↓
[未来] AI 生成图片并更新 file_url

关键技术点

  1. 避免 Lazy Loading

    • 问题:tag.element_name@property,访问时触发 relationship 查询
    • 解决:使用预先构建的 element_type_map 字典
  2. 事务管理

    • 使用 self.db.add() + await self.db.flush()
    • 不调用 commit(),依赖外层事务控制
  3. 数据库约束

    • created_by 不能为 NULL → 从 screenplay.created_by 获取
  4. 字段映射

    • ElementType.CHARACTER (1) → ResourceType.CHARACTER (1)
    • ElementType.LOCATION (2) → ResourceType.SCENE (2)
    • ElementType.PROP (3) → ResourceType.PROP (3)

🔗 关联问题

已解决

  • P1-01: project_resources 表未关联(本次实现)

待解决(Phase 3)

  • P1-02: AI Prompt 文档与实际实现不一致(标签格式已修复)
  • 实现 AI 图片生成功能(更新 file_url

📝 开发者注意事项

占位符资源查询

查询所有待生成资源:

stmt = select(ProjectResource).where(
    ProjectResource.file_url == "placeholder://pending-ai-generation",
    ProjectResource.deleted_at.is_(None)
)

按标签查询资源:

stmt = select(ProjectResource).where(
    ProjectResource.element_tag_id == tag_id,
    ProjectResource.deleted_at.is_(None)
)

AI 图片生成流程(待实现)

  1. 查询待生成资源: 筛选 status = 'pending'
  2. 调用 AI 图片生成服务: 传入 element_name, tag_label, description
  3. 上传图片到存储: 获取 file_url, checksum, width, height
  4. 更新资源记录:
    resource.file_url = uploaded_url
    resource.checksum = file_checksum
    resource.width = image_width
    resource.height = image_height
    resource.meta_data['status'] = 'completed'
    

用户界面影响

素材管理页面:

  • 现在可以看到 AI 拆解的所有元素占位符
  • 占位符资源显示为 "待生成" 状态
  • 用户可以手动上传图片替换占位符

总结

本次实现完成了 项目资源自动关联功能,填补了 AI 拆解剧本与素材管理之间的数据链路。

核心价值:

  1. 打通了三层架构的完整数据流
  2. 为 AI 图片生成功能准备了目标记录
  3. 用户可在素材库中查看 AI 拆解的所有元素
  4. 支持多集项目的资源共享

下一步:

  • Phase 3: 实现 AI 图片生成服务
  • Phase 3: 完成 file_url 更新逻辑
  • Phase 3: 前端展示占位符资源状态

审核: Pending
部署: Pending
文档更新: 已完成