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.
5.5 KiB
5.5 KiB
Changelog: 修复剧本解析接口项目ID问题
日期: 2026-02-11
类型: Bug Fix
影响范围: 剧本解析、项目资源管理
问题描述
剧本解析接口 /api/v1/screenplays/{screenplay_id}/parse 在存储角色、场景、道具到项目级别表时,使用的是剧本所属的 project_id。
当剧本属于子项目时,screenplay.project_id 是子项目ID,但 project_characters/locations/props 表的 project_id 字段设计为存储"父级项目ID"(parent_project_id = NULL 的项目),导致数据存储到错误的项目。
根本原因
数据库设计约定:
project_characters/locations/props表的project_id字段存储父级项目ID- 所有子项目共享父项目的角色/场景/道具资源
- 但代码实现中直接使用了
screenplay.project_id,未判断是否为子项目
解决方案
1. 新增辅助方法 _get_parent_project_id
在 ScreenplayService 中添加辅助方法,用于获取父级项目ID:
async def _get_parent_project_id(self, project_id: UUID) -> UUID:
"""获取父级项目ID
如果项目是子项目(parent_project_id != NULL),返回其父项目ID
如果项目是父项目(parent_project_id == NULL),返回自身ID
"""
project_repo = ProjectRepository(self.db)
project = await project_repo.get_by_id(str(project_id))
if not project:
raise Exception(f"项目不存在: {project_id}")
# 如果是子项目,返回父项目ID;否则返回自身ID
if project.parent_project_id:
return project.parent_project_id
else:
return project.id
2. 修改 store_parsed_elements 方法
在存储解析结果前,先获取父级项目ID:
# 0. 获取剧本和父级项目 ID
screenplay = await self.repository.get_by_id(screenplay_id)
if not screenplay:
raise Exception(f"剧本不存在: {screenplay_id}")
# ✅ 获取父级项目ID(如果是子项目,需要获取其父项目ID)
parent_project_id = await self._get_parent_project_id(screenplay.project_id)
logger.info("剧本所属项目 ID: %s, 父级项目 ID: %s", screenplay.project_id, parent_project_id)
3. 更新所有资源创建调用
将所有 project_id 参数替换为 parent_project_id:
- 创建项目角色:
char_repo.create_or_update(project_id=parent_project_id, ...) - 创建项目场景:
loc_repo.create_or_update(project_id=parent_project_id, ...) - 创建项目道具:
prop_repo.create_or_update(project_id=parent_project_id, ...) - 创建元素标签:
ProjectElementTag(project_id=parent_project_id, ...) - 创建分镜:
_create_storyboards_from_ai(project_id=parent_project_id, ...)
影响范围
修改文件
server/app/services/screenplay_service.py- 新增方法:
_get_parent_project_id - 修改方法:
store_parsed_elements
- 新增方法:
影响功能
- ✅ 剧本解析(AI 提取角色/场景/道具)
- ✅ 项目资源管理(角色/场景/道具列表)
- ✅ 分镜创建(元素关联)
测试建议
1. 测试场景1:父项目剧本解析
# 1. 创建父项目
POST /api/v1/projects
{
"name": "测试父项目",
"type": "mine"
}
# 2. 上传剧本到父项目(不自动创建子项目)
POST /api/v1/screenplays/upload
{
"project_id": "<父项目ID>",
"name": "测试剧本",
"auto_create_subproject": false,
"file": <剧本文件>
}
# 3. 触发解析
POST /api/v1/screenplays/{screenplay_id}/parse
# 4. 验证:角色/场景/道具的 project_id 应该是父项目ID
SELECT * FROM project_characters WHERE project_id = '<父项目ID>';
2. 测试场景2:子项目剧本解析
# 1. 创建父项目
POST /api/v1/projects
{
"name": "测试父项目",
"type": "mine"
}
# 2. 上传剧本并自动创建子项目
POST /api/v1/screenplays/upload
{
"project_id": "<父项目ID>",
"name": "测试剧本",
"auto_create_subproject": true,
"file": <剧本文件>
}
# 3. 触发解析
POST /api/v1/screenplays/{screenplay_id}/parse
# 4. 验证:角色/场景/道具的 project_id 应该是父项目ID(不是子项目ID)
SELECT * FROM project_characters WHERE project_id = '<父项目ID>';
SELECT * FROM projects WHERE parent_project_id = '<父项目ID>';
3. 验证SQL
-- 验证角色/场景/道具是否存储到父项目
SELECT
pc.character_id,
pc.name AS character_name,
pc.project_id,
p.name AS project_name,
p.parent_project_id
FROM project_characters pc
JOIN projects p ON pc.project_id = p.id
WHERE pc.project_id = '<父项目ID>';
-- 验证分镜是否关联到正确的项目
SELECT
s.storyboard_id,
s.title,
s.project_id,
p.name AS project_name,
p.parent_project_id
FROM storyboards s
JOIN projects p ON s.project_id = p.id
WHERE s.project_id IN ('<父项目ID>', '<子项目ID>');
向后兼容性
✅ 完全向后兼容
- 对于父项目剧本:行为不变(
parent_project_id返回自身ID) - 对于子项目剧本:修复了错误行为(现在正确返回父项目ID)
相关文档
- 数据库迁移:
server/alembic/versions/20260208_1000_add_project_level_resources.py - 架构决策:
docs/architecture/adrs/003-screenplay-resource-architecture.md - 项目模型:
server/app/models/project.py - 剧本服务:
server/app/services/screenplay_service.py
总结
此次修复确保剧本解析接口正确处理子项目场景,将角色/场景/道具资源存储到父级项目,符合数据库设计约定和业务逻辑。