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.
7.6 KiB
7.6 KiB
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. 数据库设计 - 添加完整注释
修改前
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(),
);
修改后
-- 创建分镜表(应用层生成 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. 设计说明 - 添加引用完整性章节
新增内容
2. **引用完整性保证**:
- ⚠️ **禁止物理外键约束**:数据库层不创建 FOREIGN KEY
- ✅ **应用层验证**:Service 层负责验证所有引用关系
- ✅ **必须创建索引**:所有关联字段创建索引以保证查询性能
变更说明:
- ✅ 明确说明禁止物理外键的规范
- ✅ 强调应用层验证的责任
- ✅ 说明索引的重要性
3. Service 层 - 添加引用完整性验证(待实施)
需要在 Service 层添加以下验证逻辑:
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 层 - 添加日志记录(待实施)
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 响应格式 - 统一包装(待实施)
修改前
{
"items": [...],
"total": 10,
"page": 1,
"page_size": 50,
"total_pages": 1
}
修改后
{
"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 配置(待实施)
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]",
}
)
符合性检查清单
✅ 已完成
- 数据库表使用 TIMESTAMPTZ(ADR 006)
- 添加完整的 SQL 注释(COMMENT ON)
- 明确标注"应用层生成 UUID v7"
- 明确标注"禁止物理外键约束"
- 强调索引的必要性
- 添加引用完整性保证说明
⏳ 待实施(代码层)
- Service 层添加引用完整性验证
- Service 层添加日志记录
- Service 层添加事务处理
- API 响应格式统一包装
- 字段命名改为 camelCase
- Model 层完善 Relationship 配置
相关规范
- ADR 006: 使用 TIMESTAMPTZ 记录事件时间戳
- jointo-tech-stack skill - backend.md
- jointo-tech-stack skill - database.md
- jointo-tech-stack skill - api-design.md
后续步骤
-
实施 Service 层验证:
- 创建
StoryboardService类 - 实现引用完整性验证
- 添加日志记录
- 添加事务处理
- 创建
-
实施 API 层:
- 创建 API 路由
- 统一响应格式
- 字段命名改为 camelCase
-
实施 Model 层:
- 完善 Relationship 配置
- 添加 TYPE_CHECKING 导入
-
编写测试:
- 单元测试(Repository, Service)
- 集成测试(API)
修复日期: 2026-01-29
修复者: Architecture Team
审核者: Backend Team Lead