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.
12 KiB
12 KiB
Screenplay Services 实现
变更日期:2026-02-03
变更类型:功能实现
影响范围:后端服务层
变更概述
实现了剧本管理服务(ScreenplayService)和剧本标签管理服务(ScreenplayTagService),完善了 Repository 层的 CRUD 方法,为剧本功能提供完整的业务逻辑支持。
变更详情
1. Repository 层完善
1.1 ScreenplayRepository 增强
文件:server/app/repositories/screenplay_repository.py
新增方法:
create()- 创建剧本update()- 更新剧本delete()- 软删除剧本create_character()- 创建角色update_character()- 更新角色delete_character()- 删除角色create_location()- 创建场景update_location()- 更新场景delete_location()- 删除场景create_prop()- 创建道具update_prop()- 更新道具delete_prop()- 删除道具
日志规范:
- 使用
get_logger(__name__)替代logging.getLogger() - 使用
%-formatting格式化日志消息 - 异常日志使用
exc_info=True
1.2 ScreenplayTagRepository 增强
文件:server/app/repositories/screenplay_tag_repository.py
新增方法:
create()- 创建标签update()- 更新标签delete()- 删除标签count_resources_by_tag()- 统计标签关联的资源数量
日志规范:
- 修正 logging 导入为
get_logger(__name__) - 统一日志格式
2. Service 层实现
2.1 ScreenplayService
文件:server/app/services/screenplay_service.py
核心功能:
- 剧本查询(按 ID)
- 角色管理(创建)
- 场景管理(创建)
- 道具管理(创建)
- AI 解析数据存储(
store_parsed_elements()) - 权限检查(
_check_project_permission())
关键方法:
async def store_parsed_elements(
self,
screenplay_id: UUID,
parsed_data: Dict[str, Any]
) -> Dict[str, Any]:
"""
存储 AI 解析的剧本元素(供 Celery Worker 调用)
流程:
1. 存储角色 → character_id_map
2. 存储场景 → location_id_map
3. 存储道具 → prop_id_map
4. 调用 ScreenplayTagService.store_tags() 存储标签
5. 更新剧本统计(character_count, location_count)
6. 提交事务
返回:
- character_id_map: {角色名: UUID}
- location_id_map: {场景名: UUID}
- prop_id_map: {道具名: UUID}
- tag_id_maps: {element_type: {元素名-标签名: UUID}}
"""
技术栈符合性:
- ✅ 使用
async/await异步编程 - ✅ 使用
get_logger(__name__)获取 logger - ✅ 使用
%-formatting格式化日志 - ✅ 异常日志使用
exc_info=True - ✅ 时间戳使用
datetime.now(timezone.utc) - ✅ 枚举类型使用 IntEnum(
RoleType.from_string())
2.2 ScreenplayTagService
文件:server/app/services/screenplay_tag_service.py
核心功能:
- 标签 CRUD(创建、查询、更新、删除)
- 按元素查询标签(
get_tags_by_element()) - 按剧本查询标签(
get_tags_by_screenplay()) - AI 解析标签存储(
store_tags()) - has_tags 字段维护(
_update_element_has_tags()) - 权限检查
关键方法:
async def store_tags(
self,
screenplay_id: UUID,
parsed_data: Dict[str, Any],
character_id_map: Dict[str, UUID],
location_id_map: Dict[str, UUID],
prop_id_map: Dict[str, UUID]
) -> Dict[str, Dict[str, UUID]]:
"""
存储 AI 解析的标签(供 ScreenplayService 调用)
流程:
1. 遍历 character_tags,创建角色标签
2. 遍历 location_tags,创建场景标签
3. 遍历 prop_tags,创建道具标签
4. 更新元素的 has_tags 标志
返回:
- character_tags: {角色名-标签名: tag_id}
- location_tags: {场景名-标签名: tag_id}
- prop_tags: {道具名-标签名: tag_id}
"""
has_tags 字段维护逻辑:
- 创建标签时:自动设置
has_tags = true - 删除标签时:检查是否还有其他标签,如果没有则设置
has_tags = false - AI 解析时:批量设置
has_tags = true
技术栈符合性:
- ✅ 使用
async/await异步编程 - ✅ 使用
get_logger(__name__)获取 logger - ✅ 使用
%-formatting格式化日志 - ✅ 异常日志使用
exc_info=True - ✅ 枚举类型使用 IntEnum(
ElementType.from_string())
3. 数据流程
3.1 AI 解析剧本流程
AI Service (Celery Worker)
↓
ScreenplayService.store_parsed_elements()
├─ 创建角色 (ScreenplayRepository.create_character)
├─ 创建场景 (ScreenplayRepository.create_location)
├─ 创建道具 (ScreenplayRepository.create_prop)
↓
ScreenplayTagService.store_tags()
├─ 创建角色标签 (ScreenplayTagRepository.create)
├─ 创建场景标签 (ScreenplayTagRepository.create)
├─ 创建道具标签 (ScreenplayTagRepository.create)
└─ 更新 has_tags 标志 (ScreenplayRepository.update_*)
↓
返回 ID 映射 (用于后续分镜关联)
3.2 标签 ID 映射结构
{
'character_id_map': {
'孙悟空': UUID('019d1234-5678-7abc-def0-111111111111')
},
'location_id_map': {
'花果山': UUID('019d1234-5678-7abc-def0-222222222222')
},
'prop_id_map': {
'金箍棒': UUID('019d1234-5678-7abc-def0-333333333333')
},
'tag_id_maps': {
'character_tags': {
'孙悟空-少年': UUID('019d1234-5678-7abc-def0-444444444444'),
'孙悟空-成年': UUID('019d1234-5678-7abc-def0-555555555555')
},
'location_tags': {
'花果山-白天': UUID('019d1234-5678-7abc-def0-666666666666'),
'花果山-夜晚': UUID('019d1234-5678-7abc-def0-777777777777')
},
'prop_tags': {
'金箍棒-崭新': UUID('019d1234-5678-7abc-def0-888888888888')
}
}
}
技术栈符合性检查
✅ 异步编程
- 所有数据库操作使用
async/await - Repository 方法全部异步
- Service 方法全部异步
✅ 日志规范
- 使用
get_logger(__name__)获取模块级 logger - 使用
%-formatting格式化日志消息 - 异常日志使用
exc_info=True - 日志级别:info(正常操作)、warning(异常情况)、error(错误)、debug(调试信息)
✅ 时间戳
- 使用
datetime.now(timezone.utc)生成 UTC 时间 - 数据库字段使用
TIMESTAMPTZ类型
✅ 枚举类型
- 使用 IntEnum 定义枚举(
RoleType,ElementType) - 提供
from_string()方法进行字符串转换 - 数据库存储 SMALLINT 类型
✅ 异常处理
- 使用自定义异常(
NotFoundError,ValidationError,PermissionError) - 异常日志包含上下文信息
- 事务失败时回滚(
await self.db.rollback())
已实现功能(第二阶段)
3. Schema 层实现
3.1 剧本 Schema
文件:server/app/schemas/screenplay.py
定义的模型:
ScreenplayBase- 剧本基础模型ScreenplayCreate- 创建剧本请求ScreenplayUpdate- 更新剧本请求ScreenplayResponse- 剧本响应CharacterBase- 角色基础模型CharacterCreate- 创建角色请求CharacterUpdate- 更新角色请求CharacterResponse- 角色响应LocationBase- 场景基础模型LocationCreate- 创建场景请求LocationUpdate- 更新场景请求LocationResponse- 场景响应PropBase- 道具基础模型PropCreate- 创建道具请求PropUpdate- 更新道具请求PropResponse- 道具响应PaginatedResponse- 分页响应
特点:
- 使用 Pydantic v2 语法(
model_config = ConfigDict(from_attributes=True)) - 完整的字段描述和类型注解
- 支持 ORM 模型转换
3.2 标签 Schema
文件:server/app/schemas/screenplay_tag.py
定义的模型:
TagBase- 标签基础模型TagCreate- 创建标签请求TagUpdate- 更新标签请求TagResponse- 标签响应SetDefaultTagRequest- 设置默认标签请求SetDefaultTagResponse- 设置默认标签响应
4. API 路由层实现
4.1 剧本 API
文件:server/app/api/v1/screenplays.py
实现的端点:
| 方法 | 路径 | 功能 | 状态码 |
|---|---|---|---|
| POST | /{screenplay_id}/characters |
创建角色 | 201 |
| GET | /{screenplay_id}/characters |
获取角色列表 | 200 |
| POST | /{screenplay_id}/locations |
创建场景 | 201 |
| GET | /{screenplay_id}/locations |
获取场景列表 | 200 |
| POST | /{screenplay_id}/props |
创建道具 | 201 |
| GET | /{screenplay_id}/props |
获取道具列表 | 200 |
特点:
- 完整的权限检查(通过
get_current_user依赖) - 分页支持(page, page_size)
- 搜索过滤(search 参数)
- 标签过滤(has_tags 参数)
- 统一的错误处理
- 详细的 API 文档(summary, description)
4.2 标签 API
文件:server/app/api/v1/screenplay_tags.py
实现的端点:
| 方法 | 路径 | 功能 | 状态码 |
|---|---|---|---|
| POST | `` | 创建标签 | 201 |
| GET | /element/{element_type}/{element_id} |
获取元素的所有标签 | 200 |
| GET | /screenplay/{screenplay_id} |
获取剧本的所有标签 | 200 |
| PATCH | /{tag_id} |
更新标签 | 200 |
| DELETE | /{tag_id} |
删除标签 | 204 |
特点:
- RESTful 设计
- 支持按元素类型过滤
- 删除前检查资源关联
- 自动维护 has_tags 字段
未实现功能
以下功能在需求文档中定义,但本次未实现(留待后续迭代):
1. ScreenplayService 缺失功能
- ❌ 剧本列表查询(
get_screenplays()) - ❌ 创建文本剧本(
create_screenplay()) - ❌ 创建文件剧本(
create_screenplay_from_file()) - ❌ 更新剧本(
update_screenplay()) - ❌ 审批剧本(
approve_screenplay()) - ❌ 版本管理(
get_screenplay_versions(),_create_version()) - ❌ 设置默认标签(
set_character_default_tag(),set_location_default_tag(),set_prop_default_tag()) - ❌ 获取默认缩略图(
_get_default_thumbnail())
2. API 路由缺失功能
- ❌ 剧本 CRUD API(创建、查询、更新、删除剧本)
- ❌ 版本管理 API
- ❌ 审批 API
- ❌ 设置默认标签 API
3. 模型更新
- ❌
server/app/models/screenplay_element_tag.py- 添加element_name字段(冗余存储)
后续任务
-
完善 ScreenplayService:
- 实现剧本 CRUD 完整功能
- 实现版本管理
- 实现审批流程
- 实现默认标签设置
-
创建 Schema 层:
- 定义请求/响应模型
- 数据验证规则
-
创建 API 路由层:
- 实现所有 API 端点
- 集成 FastAPI 依赖注入
- 添加 API 文档
-
集成测试:
- 编写单元测试
- 编写集成测试
- 测试 AI 解析流程
相关文档
变更影响
新增文件
第一阶段:
server/app/services/screenplay_service.py- 剧本服务server/app/services/screenplay_tag_service.py- 标签服务docs/server/changelogs/2026-02-03-screenplay-services-implementation.md- 变更日志
第二阶段:
server/app/schemas/screenplay.py- 剧本相关 Schemaserver/app/schemas/screenplay_tag.py- 标签相关 Schemaserver/app/api/v1/screenplays.py- 剧本 API 路由server/app/api/v1/screenplay_tags.py- 标签 API 路由
修改文件
server/app/repositories/screenplay_repository.py- 新增 CRUD 方法server/app/repositories/screenplay_tag_repository.py- 新增 CRUD 方法,修正日志导入docs/server/changelogs/2026-02-03-screenplay-services-implementation.md- 更新变更记录
依赖关系
- ScreenplayService 依赖 ScreenplayRepository
- ScreenplayTagService 依赖 ScreenplayTagRepository、ScreenplayRepository
- ScreenplayService.store_parsed_elements() 调用 ScreenplayTagService.store_tags()
变更日期:2026-02-03
变更作者:System
审核状态:待审核