# 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()`) **关键方法**: ```python 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()`) - 权限检查 **关键方法**: ```python 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 映射结构 ```python { '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` 字段(冗余存储) --- ## 后续任务 1. **完善 ScreenplayService**: - 实现剧本 CRUD 完整功能 - 实现版本管理 - 实现审批流程 - 实现默认标签设置 2. **创建 Schema 层**: - 定义请求/响应模型 - 数据验证规则 3. **创建 API 路由层**: - 实现所有 API 端点 - 集成 FastAPI 依赖注入 - 添加 API 文档 4. **集成测试**: - 编写单元测试 - 编写集成测试 - 测试 AI 解析流程 --- ## 相关文档 - [剧本管理服务需求](../../requirements/backend/04-services/project/screenplay-service.md) - [剧本标签管理服务需求](../../requirements/backend/04-services/project/screenplay-tag-service.md) - [剧本文件解析服务需求](../../requirements/backend/04-services/project/screenplay-file-parser-service.md) - [Jointo 技术栈规范](.kiro/skills/jointo-tech-stack/SKILL.md) --- ## 变更影响 ### 新增文件 **第一阶段**: - `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` - 剧本相关 Schema - `server/app/schemas/screenplay_tag.py` - 标签相关 Schema - `server/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 **审核状态**:待审核