# Storyboard Service 技术栈规范符合性修复 > **日期**: 2026-01-29 > **类型**: 文档修复 > **影响范围**: `docs/requirements/backend/04-services/project/storyboard-service.md` --- ## 修复概述 修复 `storyboard-service.md` 文档,使其完全符合 jointo-tech-stack 规范,特别是: - ADR 006(TIMESTAMPTZ 时间戳规范) - 引用完整性保证(禁止物理外键) - SQL 注释规范(COMMENT ON 语法) - API 响应格式统一 ## 修复内容 ### 1. 数据库设计 - 添加完整注释 #### 修改前 ```sql CREATE TABLE storyboards ( storyboard_id UUID PRIMARY KEY, project_id UUID NOT NULL, -- 基本信息 shot_number TEXT NOT NULL, -- 镜号(自动生成) title TEXT NOT NULL, -- 标题 ... created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), ); ``` #### 修改后 ```sql -- 创建分镜表(应用层生成 UUID v7,无物理外键约束) CREATE TABLE storyboards ( storyboard_id UUID PRIMARY KEY, -- 应用层生成 UUID v7 project_id UUID NOT NULL, -- 应用层验证引用完整性 ... created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), ); -- 表级注释 COMMENT ON TABLE storyboards IS '分镜表 - 应用层保证引用完整性'; -- 列级注释 COMMENT ON COLUMN storyboards.storyboard_id IS '分镜唯一标识(UUID v7,应用层生成)'; COMMENT ON COLUMN storyboards.project_id IS '项目 ID - 应用层验证'; COMMENT ON COLUMN storyboards.shot_number IS '镜号(自动生成,如 001, 002)'; ... COMMENT ON COLUMN storyboards.created_at IS '创建时间(UTC,自动记录时区)'; COMMENT ON COLUMN storyboards.updated_at IS '更新时间(UTC,自动记录时区)'; -- 索引(必须创建,因为无物理外键约束) CREATE INDEX idx_storyboards_project_id ON storyboards (project_id); ... ``` **变更说明**: - ✅ 添加表级和列级注释(使用 COMMENT ON 语法) - ✅ 明确标注"应用层生成 UUID v7" - ✅ 明确标注"应用层验证引用完整性" - ✅ 强调索引的必要性(因为无物理外键) ### 2. 设计说明 - 添加引用完整性章节 #### 新增内容 ```markdown 2. **引用完整性保证**: - ⚠️ **禁止物理外键约束**:数据库层不创建 FOREIGN KEY - ✅ **应用层验证**:Service 层负责验证所有引用关系 - ✅ **必须创建索引**:所有关联字段创建索引以保证查询性能 ``` **变更说明**: - ✅ 明确说明禁止物理外键的规范 - ✅ 强调应用层验证的责任 - ✅ 说明索引的重要性 ### 3. Service 层 - 添加引用完整性验证(待实施) 需要在 Service 层添加以下验证逻辑: ```python async def create_storyboard( self, user_id: UUID, storyboard_data: StoryboardCreate ) -> Storyboard: """创建分镜 - 验证所有引用""" # 1. 验证项目是否存在 if not await self.project_repo.exists(storyboard_data.project_id): raise NotFoundError("项目不存在") # 2. 验证用户对项目的权限 has_permission = await self.project_repo.check_user_permission( user_id, storyboard_data.project_id, MemberRole.EDITOR ) if not has_permission: raise PermissionError("无权限在该项目下创建分镜") # 3. 验证缩略图附件(如果提供) if storyboard_data.thumbnail_id: if not await self.attachment_repo.exists(storyboard_data.thumbnail_id): raise NotFoundError("缩略图附件不存在") # 4. 使用事务创建分镜和关联资源 async with self.session.begin(): # 创建分镜... # 关联资源... # 记录日志... ``` ### 4. Service 层 - 添加日志记录(待实施) ```python from app.core.logging import get_logger logger = get_logger(__name__) class StoryboardService: async def create_storyboard(self, ...): logger.info( "用户 %s 正在创建分镜,项目 ID: %s", user_id, storyboard_data.project_id ) try: # 创建逻辑... logger.info( "分镜创建成功,分镜 ID: %s,镜号: %s", created_storyboard.storyboard_id, created_storyboard.shot_number ) return created_storyboard except Exception as e: logger.error( "创建分镜失败,用户 ID: %s,项目 ID: %s", user_id, storyboard_data.project_id, exc_info=True ) raise ``` ### 5. API 响应格式 - 统一包装(待实施) #### 修改前 ```json { "items": [...], "total": 10, "page": 1, "page_size": 50, "total_pages": 1 } ``` #### 修改后 ```json { "success": true, "code": 200, "message": "Success", "data": { "items": [...], "total": 10, "page": 1, "pageSize": 50, "totalPages": 5 }, "timestamp": "2026-01-29T12:00:00+00:00" } ``` **变更说明**: - ✅ 添加统一响应包装(success, code, message, timestamp) - ✅ 字段命名改为 camelCase(pageSize, totalPages) ### 6. Model 层 - 完善 Relationship 配置(待实施) ```python from typing import TYPE_CHECKING, Optional from sqlmodel import Relationship if TYPE_CHECKING: from app.models.project import Project from app.models.attachment import Attachment class Storyboard(SQLModel, table=True): # ... 其他字段 ... # Relationship 配置(使用 primaryjoin,因为无物理外键) project: Optional["Project"] = Relationship( sa_relationship_kwargs={ "primaryjoin": "Storyboard.project_id == Project.project_id", "foreign_keys": "[Storyboard.project_id]", } ) thumbnail: Optional["Attachment"] = Relationship( sa_relationship_kwargs={ "primaryjoin": "Storyboard.thumbnail_id == Attachment.attachment_id", "foreign_keys": "[Storyboard.thumbnail_id]", } ) ``` ## 符合性检查清单 ### ✅ 已完成 - [x] 数据库表使用 TIMESTAMPTZ(ADR 006) - [x] 添加完整的 SQL 注释(COMMENT ON) - [x] 明确标注"应用层生成 UUID v7" - [x] 明确标注"禁止物理外键约束" - [x] 强调索引的必要性 - [x] 添加引用完整性保证说明 ### ⏳ 待实施(代码层) - [ ] Service 层添加引用完整性验证 - [ ] Service 层添加日志记录 - [ ] Service 层添加事务处理 - [ ] API 响应格式统一包装 - [ ] 字段命名改为 camelCase - [ ] Model 层完善 Relationship 配置 ## 相关规范 - [ADR 006: 使用 TIMESTAMPTZ 记录事件时间戳](../../architecture/adrs/006-timestamptz-for-event-timestamps.md) - [jointo-tech-stack skill - backend.md](../../../.claude/skills/jointo-tech-stack/references/backend.md) - [jointo-tech-stack skill - database.md](../../../.claude/skills/jointo-tech-stack/references/database.md) - [jointo-tech-stack skill - api-design.md](../../../.claude/skills/jointo-tech-stack/references/api-design.md) ## 后续步骤 1. **实施 Service 层验证**: - 创建 `StoryboardService` 类 - 实现引用完整性验证 - 添加日志记录 - 添加事务处理 2. **实施 API 层**: - 创建 API 路由 - 统一响应格式 - 字段命名改为 camelCase 3. **实施 Model 层**: - 完善 Relationship 配置 - 添加 TYPE_CHECKING 导入 4. **编写测试**: - 单元测试(Repository, Service) - 集成测试(API) --- **修复日期**: 2026-01-29 **修复者**: Architecture Team **审核者**: Backend Team Lead