# 剧本管理服务完整实现 > **变更日期**:2026-02-04 > **变更类型**:功能实现 > **影响范围**:后端 - 剧本管理模块 --- ## 变更概述 完整实现剧本管理服务(Screenplay Service)的所有功能,包括 Repository 层、Service 层、Schema 层和 API 层,覆盖剧本 CRUD、版本管理、默认标签设置等核心功能。 --- ## 变更详情 ### 1. Repository 层补充 **文件**:`server/app/repositories/screenplay_repository.py` **新增方法**: 1. `get_by_project()` - 获取项目的剧本列表(分页) 2. `count_by_project()` - 统计项目剧本数量 3. `create_version()` - 创建版本记录 4. `get_versions()` - 获取版本历史 **技术要点**: - 使用 SQLModel 的 `select()` 构建查询 - 支持状态过滤和分页 - 按创建时间倒序排序 - 使用 `flush()` 而非 `commit()`(符合 Repository 职责) ### 2. Model 层补充 **新增文件**:`server/app/models/screenplay_version.py` **模型定义**: ```python class ScreenplayVersion(SQLModel, table=True): """剧本版本表""" version_id: UUID screenplay_id: UUID version_number: int content_snapshot: Optional[str] change_summary: Optional[str] word_count: int scene_count: int character_count: int created_by: UUID created_at: datetime ``` **技术要点**: - 使用 UUID v7 作为主键 - 使用 TIMESTAMPTZ 存储时间戳 - 使用 `generate_uuid_v7()` 生成 UUID ### 3. Service 层补充 **文件**:`server/app/services/screenplay_service.py` **新增方法**: 1. `get_screenplays()` - 获取剧本列表 2. `create_screenplay()` - 创建文本剧本 3. `update_screenplay()` - 更新剧本 4. `approve_screenplay()` - 审批剧本 5. `get_screenplay_versions()` - 获取版本历史 6. `set_character_default_tag()` - 设置角色默认标签 7. `set_location_default_tag()` - 设置场景默认标签 8. `set_prop_default_tag()` - 设置道具默认标签 9. `_get_default_thumbnail()` - 获取默认缩略图 10. `_create_version()` - 创建版本记录 **业务逻辑**: #### 剧本创建 - 验证内容非空 - 计算字数统计 - 创建初始版本(version = 1) - 使用 `commit()` 提交事务 #### 剧本更新 - 检测内容变化 - 内容变化时自动创建新版本 - 版本号自增 - 记录变更摘要 #### 默认标签设置 - 验证标签属于该元素 - 更新 `default_tag_id` 字段 - 自动计算默认缩略图(查询标签下的第一个资源) - 优先使用 `thumbnail_url`,若为空则使用 `file_url` **技术要点**: - 所有方法使用 `async/await` - 使用 `%-formatting` 格式化日志 - 异常日志使用 `exc_info=True` - Service 层使用 `commit()` 提交事务 - 权限检查使用 `_check_project_permission()` ### 4. Schema 层补充 **文件**:`server/app/schemas/screenplay.py` **新增 Schema**: 1. `VersionResponse` - 版本响应模型 2. `ScreenplayListItem` - 剧本列表项模型 3. `ScreenplayListResponse` - 剧本列表响应模型 **已存在 Schema**: - `ScreenplayCreate` - 创建剧本请求 - `ScreenplayUpdate` - 更新剧本请求 - `ScreenplayResponse` - 剧本响应 - `SetDefaultTagRequest` - 设置默认标签请求(在 screenplay_tag.py) - `SetDefaultTagResponse` - 设置默认标签响应(在 screenplay_tag.py) ### 5. API 层补充 **文件**:`server/app/api/v1/screenplays.py` **新增 API 端点**: #### 剧本 CRUD 1. `GET /api/v1/screenplays` - 获取剧本列表 2. `POST /api/v1/screenplays` - 创建文本剧本 3. `GET /api/v1/screenplays/{id}` - 获取剧本详情 4. `PATCH /api/v1/screenplays/{id}` - 更新剧本 5. `POST /api/v1/screenplays/{id}/approve` - 审批剧本 6. `GET /api/v1/screenplays/{id}/versions` - 获取版本历史 #### 默认标签管理 7. `PUT /api/v1/screenplays/characters/{id}/default-tag` - 设置角色默认标签 8. `PUT /api/v1/screenplays/locations/{id}/default-tag` - 设置场景默认标签 9. `PUT /api/v1/screenplays/props/{id}/default-tag` - 设置道具默认标签 **已存在 API 端点**: - `POST /api/v1/screenplays/{id}/characters` - 创建角色 - `GET /api/v1/screenplays/{id}/characters` - 获取角色列表 - `POST /api/v1/screenplays/{id}/locations` - 创建场景 - `GET /api/v1/screenplays/{id}/locations` - 获取场景列表 - `POST /api/v1/screenplays/{id}/props` - 创建道具 - `GET /api/v1/screenplays/{id}/props` - 获取道具列表 - `POST /api/v1/screenplays/{id}/parse` - AI 解析剧本 - `POST /api/v1/screenplays/upload-and-parse` - 上传并解析文件 - `POST /api/v1/screenplays/{id}/parse-file` - 手动触发文件解析 - `GET /api/v1/screenplays/{id}/parse-status` - 查询解析状态 **技术要点**: - 使用统一响应格式 `success_response()` - 所有端点都需要认证(`Depends(get_current_user)`) - 使用 Query 参数进行分页和过滤 - 使用 Pydantic 模型验证请求体 - 日志记录关键操作 --- ## 技术栈符合性 ### 1. 异步编程 - ✅ 所有数据库操作使用 `async/await` - ✅ Repository 使用 `flush()`,Service 使用 `commit()` - ✅ 使用 `AsyncSession` 而非 `Session` ### 2. 日志规范 - ✅ 使用 `get_logger(__name__)` 获取模块级 logger - ✅ 使用 `%-formatting` 格式化日志消息 - ✅ 异常日志使用 `exc_info=True` - ✅ 日志消息使用中文 ### 3. 时间戳 - ✅ 使用 `datetime.now(timezone.utc)` 生成 UTC 时间 - ✅ 数据库字段使用 `TIMESTAMPTZ` 类型 - ✅ 模型使用 `TIMESTAMP(timezone=True)` ### 4. UUID 生成 - ✅ 使用 `generate_uuid_v7()` 生成 UUID v7 - ✅ 数据库字段使用 `PG_UUID(as_uuid=True)` ### 5. API 响应格式 - ✅ 使用统一响应格式 `success_response()` - ✅ 包含 `success`, `message`, `data` 字段 - ✅ 包含 `timestamp` 字段 --- ## 业务流程 ### 1. 创建文本剧本流程 ``` 用户提交 → API 验证 → Service 检查权限 → Repository 创建剧本 → Service 创建初始版本 → Service commit → 返回结果 ``` ### 2. 更新剧本流程 ``` 用户提交 → API 验证 → Service 检查权限 → Service 检测内容变化 → Repository 更新剧本 → Service 创建新版本(如果内容变化) → Service commit → 返回结果 ``` ### 3. 设置默认标签流程 ``` 用户提交 → API 验证 → Service 检查权限 → Service 验证标签 → Repository 更新 default_tag_id → Service 查询默认缩略图 → Service commit → 返回结果(含缩略图 URL) ``` ### 4. 获取剧本列表流程 ``` 用户请求 → API 验证 → Service 检查权限 → Repository 查询列表 → Repository 统计总数 → Service 计算分页 → 返回结果 ``` --- ## 数据库影响 ### 新增表 - `screenplay_versions` - 剧本版本表(已在迁移中创建) ### 已存在表 - `screenplays` - 剧本表 - `screenplay_characters` - 角色表 - `screenplay_locations` - 场景表 - `screenplay_props` - 道具表 ### 字段说明 - `default_tag_id` - 默认标签 ID(角色/场景/道具表) - `version` - 版本号(剧本表) - `approved_by` - 审批人 ID(剧本表) - `approved_at` - 审批时间(剧本表) --- ## API 示例 ### 1. 获取剧本列表 **请求**: ```http GET /api/v1/screenplays?project_id=019d1234-5678-7abc-def0-111111111111&page=1&page_size=20 Authorization: Bearer {token} ``` **响应**: ```json { "success": true, "code": 200, "message": "Success", "data": { "items": [ { "screenplay_id": "019d1234-5678-7abc-def0-222222222222", "project_id": "019d1234-5678-7abc-def0-111111111111", "name": "第一集剧本", "type": "text", "version": 2, "word_count": 5000, "status": "draft", "created_at": "2026-02-04T10:00:00Z", "updated_at": "2026-02-04T11:00:00Z" } ], "total": 15, "page": 1, "page_size": 20, "total_pages": 1 }, "timestamp": "2026-02-04T12:00:00+00:00" } ``` ### 2. 创建文本剧本 **请求**: ```http POST /api/v1/screenplays Authorization: Bearer {token} Content-Type: application/json { "project_id": "019d1234-5678-7abc-def0-111111111111", "name": "第一集剧本", "content": "场景1:室内 - 咖啡厅 - 白天\n\n张三坐在窗边..." } ``` **响应**: ```json { "success": true, "code": 201, "message": "剧本创建成功", "data": { "screenplay_id": "019d1234-5678-7abc-def0-222222222222", "project_id": "019d1234-5678-7abc-def0-111111111111", "name": "第一集剧本", "type": "text", "content": "场景1:室内 - 咖啡厅 - 白天\n\n张三坐在窗边...", "version": 1, "word_count": 5000, "status": "draft", "created_at": "2026-02-04T10:00:00Z", "updated_at": "2026-02-04T10:00:00Z" }, "timestamp": "2026-02-04T10:00:00+00:00" } ``` ### 3. 设置角色默认标签 **请求**: ```http PUT /api/v1/screenplays/characters/019d1234-5678-7abc-def0-333333333333/default-tag Authorization: Bearer {token} Content-Type: application/json { "tag_id": "019d1234-5678-7abc-def0-444444444444" } ``` **响应**: ```json { "success": true, "code": 200, "message": "角色默认标签设置成功", "data": { "character_id": "019d1234-5678-7abc-def0-333333333333", "name": "孙悟空", "default_tag_id": "019d1234-5678-7abc-def0-444444444444", "default_thumbnail_url": "https://storage.jointo.ai/resources/abc123_thumb.jpg" }, "timestamp": "2026-02-04T12:00:00+00:00" } ``` --- ## 测试建议 ### 单元测试 1. `test_screenplay_repository.py` - Repository 层测试 - 测试 `get_by_project()` 分页和过滤 - 测试 `count_by_project()` 统计 - 测试 `create_version()` 版本创建 - 测试 `get_versions()` 版本查询 2. `test_screenplay_service.py` - Service 层测试 - 测试 `create_screenplay()` 创建剧本 - 测试 `update_screenplay()` 更新剧本(内容变化时创建版本) - 测试 `set_character_default_tag()` 设置默认标签 - 测试 `_get_default_thumbnail()` 获取缩略图 ### 集成测试 1. `test_screenplay_api.py` - API 集成测试 - 测试剧本 CRUD 完整流程 - 测试版本管理流程 - 测试默认标签设置流程 - 测试权限控制 --- ## 相关文档 - [剧本管理服务需求文档](../../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) - [后端架构指南](../guides/backend-architecture.md) - [Jointo 技术栈规范](.claude/skills/jointo-tech-stack/SKILL.md) --- ## 后续工作 ### 1. 测试编写 - [ ] 编写 Repository 层单元测试 - [ ] 编写 Service 层单元测试 - [ ] 编写 API 集成测试 ### 2. 功能增强 - [ ] 实现剧本搜索功能(全文搜索) - [ ] 实现剧本导出功能(PDF/DOCX) - [ ] 实现剧本对比功能(版本对比) - [ ] 实现剧本协作功能(多人编辑) ### 3. 性能优化 - [ ] 添加剧本列表缓存 - [ ] 优化版本历史查询(分页) - [ ] 优化默认缩略图查询(缓存) --- ## 变更影响 ### 影响范围 - ✅ 后端 - 剧本管理模块 - ✅ 数据库 - screenplay_versions 表 - ⚠️ 前端 - 需要对接新增的 API 端点 ### 兼容性 - ✅ 向后兼容:所有新增功能不影响现有功能 - ✅ 数据库兼容:使用已存在的表结构 - ✅ API 兼容:新增端点不影响现有端点 ### 风险评估 - 🟢 低风险:所有功能都是新增,不修改现有逻辑 - 🟢 低风险:完全符合技术栈规范 - 🟢 低风险:日志完善,便于调试 --- **变更人**:AI Assistant **审核人**:待审核 **变更日期**:2026-02-04