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.
15 KiB
15 KiB
项目管理服务 - 流程图与关系图
文档版本:v1.0
最后更新:2026-02-02
关联文档:项目管理服务
目录
数据库表关系图
erDiagram
projects ||--o{ project_members : "has members"
projects ||--o{ project_shares : "has shares"
projects ||--o{ project_versions : "has versions"
projects ||--o{ projects : "parent-child"
projects }o--|| users : "owned by"
projects }o--o| folders : "belongs to"
projects }o--o| screenplays : "linked to"
projects }o--o| attachments : "cover image"
projects {
uuid project_id PK "UUID v7"
text name "项目名称"
smallint type "1=mine, 2=collab"
text owner_type "user | organization"
uuid owner_id FK "逻辑外键"
uuid folder_id FK "逻辑外键"
uuid parent_project_id FK "父项目ID"
uuid screenplay_id FK "剧本ID(子项目)"
uuid cover_image_id FK "封面图片ID"
smallint content_type "1=ad, 2=movie..."
smallint aspect_ratio "1=16:9, 2=9:16..."
integer planned_duration "计划时长(秒)"
integer actual_duration "实际时长(秒)"
text visual_style "视觉风格"
jsonb settings "项目配置"
smallint status "0=active, 2=trashed, 3=deleted"
timestamptz created_at
timestamptz updated_at
timestamptz trashed_at
timestamptz deleted_at
}
project_members {
uuid member_id PK "UUID v7"
uuid project_id FK "逻辑外键"
uuid user_id FK "逻辑外键"
smallint role "1=owner, 2=editor, 3=viewer"
uuid invited_by FK "邀请人ID"
timestamptz created_at
timestamptz updated_at
}
project_shares {
uuid share_id PK "UUID v7"
uuid project_id FK "逻辑外键"
text share_token UK "分享令牌"
text password_hash "密码哈希"
smallint permission "1=viewer, 2=editor"
uuid created_by FK "创建者ID"
timestamptz created_at
timestamptz expires_at
timestamptz last_accessed_at
integer access_count
}
project_versions {
uuid version_id PK "UUID v7"
uuid project_id FK "逻辑外键"
integer version_number "版本号"
jsonb snapshot_data "项目快照"
uuid created_by FK "创建者ID"
text note "版本说明"
timestamptz created_at
}
users {
uuid user_id PK
text nickname
}
folders {
uuid folder_id PK
text name
}
screenplays {
uuid screenplay_id PK
text title
}
attachments {
uuid attachment_id PK
text file_url
}
说明:
- 所有外键关系均为逻辑外键(无物理约束)
- 引用完整性由应用层(Service/Repository)保证
projects表支持自引用(父子项目关系)- 子项目必须关联剧本(
parent_project_id和screenplay_id同时存在或同时为空)
项目创建流程图
flowchart TD
Start([用户请求创建项目]) --> ValidateAuth{验证用户身份}
ValidateAuth -->|未登录| Error1[返回 401 未授权]
ValidateAuth -->|已登录| ValidateUser{校验用户存在性}
ValidateUser -->|用户不存在| Error2[返回 400 用户不存在]
ValidateUser -->|用户存在| CheckFolder{是否指定文件夹?}
CheckFolder -->|是| ValidateFolder{校验文件夹存在性}
CheckFolder -->|否| CheckCover
ValidateFolder -->|文件夹不存在| Error3[返回 400 文件夹不存在]
ValidateFolder -->|文件夹存在| CheckFolderPerm{检查文件夹权限}
CheckFolderPerm -->|无权限| Error4[返回 403 无权限]
CheckFolderPerm -->|有权限| CheckCover
CheckCover{是否指定封面图片?} -->|是| ValidateCover{校验附件存在性和所属权}
CheckCover -->|否| ValidateType
ValidateCover -->|附件不存在或无权限| Error5[返回 400/403]
ValidateCover -->|校验通过| ValidateType
ValidateType{验证项目类型} -->|无效类型| Error6[返回 400 无效类型]
ValidateType -->|有效类型| GenerateUUID[生成 UUID v7]
GenerateUUID --> CreateProject[创建项目记录]
CreateProject --> CheckCollab{是否为协作项目?}
CheckCollab -->|是| AddOwnerMember[添加创建者为成员<br/>role=owner]
CheckCollab -->|否| LogCreate
AddOwnerMember --> LogCreate[记录日志]
LogCreate --> Success([返回项目信息])
style Start fill:#e1f5e1
style Success fill:#e1f5e1
style Error1 fill:#ffe1e1
style Error2 fill:#ffe1e1
style Error3 fill:#ffe1e1
style Error4 fill:#ffe1e1
style Error5 fill:#ffe1e1
style Error6 fill:#ffe1e1
流程说明:
- 身份验证:检查用户登录状态
- 用户校验:确认用户存在(引用完整性)
- 文件夹校验:如果指定文件夹,检查存在性和权限
- 封面图片校验:如果指定封面,检查附件存在性和所属权
- 类型验证:确保项目类型为
mine或collab - UUID 生成:应用层生成 UUID v7
- 创建项目:插入数据库记录
- 成员管理:协作项目自动添加创建者为 owner
- 日志记录:记录操作日志
项目克隆流程图
flowchart TD
Start([用户请求克隆项目]) --> CheckPerm{检查源项目权限}
CheckPerm -->|无权限| Error1[返回 403 无权限]
CheckPerm -->|有 viewer 权限| GetSource[获取源项目信息]
GetSource --> GenerateName[生成唯一副本名称<br/>基础名称-副本N]
GenerateName --> CreateTask{创建异步任务}
CreateTask --> ReturnTaskId[返回 task_id<br/>status=pending]
ReturnTaskId --> AsyncStart([异步任务开始])
AsyncStart --> CloneBasic[复制项目基本信息]
CloneBasic --> CloneSettings[复制项目配置]
CloneSettings --> CloneStoryboards{复制分镜?}
CloneStoryboards -->|是| CopyStoryboards[复制所有分镜]
CloneStoryboards -->|否| CloneResources
CopyStoryboards --> CloneResources{复制资源?}
CloneResources -->|是| CopyResources[复制视频/附件]
CloneResources -->|否| UpdateProgress
CopyResources --> UpdateProgress[更新任务进度]
UpdateProgress --> Commit[提交事务]
Commit --> Success{克隆成功?}
Success -->|是| TaskComplete[任务状态=completed<br/>progress=100]
Success -->|否| TaskFailed[任务状态=failed<br/>记录错误信息]
TaskComplete --> End([用户查询任务状态])
TaskFailed --> End
style Start fill:#e1f5e1
style End fill:#e1f5e1
style Error1 fill:#ffe1e1
style AsyncStart fill:#fff4e1
style TaskComplete fill:#e1f5e1
style TaskFailed fill:#ffe1e1
流程说明:
- 权限检查:确保用户至少有 viewer 权限
- 名称生成:自动生成唯一的副本名称(避免重名)
- 异步任务:创建任务并立即返回 task_id
- 基本信息复制:复制项目名称、描述、类型等
- 配置复制:复制 settings(分辨率、帧率等)
- 分镜复制:复制所有分镜及其关联资源
- 资源复制:复制视频、附件等文件
- 进度更新:实时更新任务进度
- 状态通知:任务完成或失败后更新状态
权限检查流程图
flowchart TD
Start([检查用户权限]) --> GetProject{获取项目信息}
GetProject -->|项目不存在| Return1[返回 False]
GetProject -->|项目存在| CheckOwner{是否为项目所有者?}
CheckOwner -->|是| Return2[返回 True<br/>所有者拥有所有权限]
CheckOwner -->|否| CheckType{项目类型?}
CheckType -->|个人项目 mine| Return3[返回 False<br/>非所有者无权限]
CheckType -->|协作项目 collab| QueryMember[查询成员表<br/>project_members]
QueryMember --> IsMember{是否为成员?}
IsMember -->|否| Return4[返回 False]
IsMember -->|是| GetRole[获取成员角色]
GetRole --> CompareRole{角色权限对比}
CompareRole --> CalcHierarchy[权限层级:<br/>owner=3<br/>editor=2<br/>viewer=1]
CalcHierarchy --> CheckRequired{成员权限 >= 所需权限?}
CheckRequired -->|是| Return5[返回 True]
CheckRequired -->|否| Return6[返回 False]
style Start fill:#e1f5e1
style Return2 fill:#e1f5e1
style Return5 fill:#e1f5e1
style Return1 fill:#ffe1e1
style Return3 fill:#ffe1e1
style Return4 fill:#ffe1e1
style Return6 fill:#ffe1e1
权限层级说明:
owner(3):所有权限(创建、编辑、删除、管理成员、分享)editor(2):编辑权限(创建、编辑分镜和资源)viewer(1):查看权限(只读访问)
权限检查逻辑:
- 项目所有者:自动拥有所有权限
- 个人项目:只有所有者有权限
- 协作项目:检查成员表中的角色
- 权限对比:成员角色权限 >= 所需权限时通过
示例:
- 用户角色为
editor,所需权限为viewer→ 通过(2 >= 1) - 用户角色为
viewer,所需权限为editor→ 拒绝(1 < 2)
项目生命周期状态图
stateDiagram-v2
[*] --> Active: 创建项目
Active --> Archived: 用户归档
Archived --> Active: 取消归档
Active --> Trashed: 移至回收站<br/>(DELETE /projects/{id})
Archived --> Trashed: 移至回收站
Trashed --> Active: 从回收站恢复<br/>(POST /projects/{id}/restore)
Trashed --> SoftDeleted: 永久删除<br/>(DELETE /projects/{id}/permanent)
Trashed --> SoftDeleted: 30天后自动清理
SoftDeleted --> [*]: 物理删除<br/>(仅管理员)
note right of Active
status = 0
用户可见、可编辑
end note
note right of Archived
status = 1
用户可见、只读
end note
note right of Trashed
status = 2
回收站可见
30天内可恢复
trashed_at 记录时间
end note
note right of SoftDeleted
status = 3
用户不可见
数据保留
deleted_at 记录时间
end note
状态说明:
| 状态 | 数值 | 用户可见 | 可编辑 | 说明 |
|---|---|---|---|---|
| Active | 0 | ✅ | ✅ | 活跃项目,正常使用 |
| Archived | 1 | ✅ | ❌ | 归档项目,只读(预留功能) |
| Trashed | 2 | ⚠️ | ❌ | 回收站,30天内可恢复 |
| SoftDeleted | 3 | ❌ | ❌ | 软删除,用户不可见,数据保留 |
状态转换规则:
- 创建项目 → Active
- 用户归档 → Active ↔ Archived(预留功能)
- 移至回收站 → Active/Archived → Trashed
- 从回收站恢复 → Trashed → Active
- 永久删除 → Trashed → SoftDeleted
- 自动清理 → Trashed(30天后)→ SoftDeleted
- 物理删除 → SoftDeleted → 数据库删除(仅管理员)
回收站机制:
- 用户删除项目时,状态变为
Trashed,设置trashed_at - 30天内可通过 API 恢复
- 30天后自动转为
SoftDeleted - 用户可在回收站中立即永久删除(转为
SoftDeleted)
子项目关系图
flowchart TD
subgraph "父项目 (Parent Project)"
P1[项目: 电视剧制作<br/>parent_project_id = NULL<br/>screenplay_id = NULL]
end
subgraph "子项目 (Subprojects)"
S1[子项目: 第1集<br/>parent_project_id = P1.id<br/>screenplay_id = SC1.id]
S2[子项目: 第2集<br/>parent_project_id = P1.id<br/>screenplay_id = SC2.id]
S3[子项目: 第3集<br/>parent_project_id = P1.id<br/>screenplay_id = SC3.id]
end
subgraph "剧本 (Screenplays)"
SC1[剧本: 第1集剧本<br/>screenplay_id = SC1]
SC2[剧本: 第2集剧本<br/>screenplay_id = SC2]
SC3[剧本: 第3集剧本<br/>screenplay_id = SC3]
end
P1 -->|创建子项目| S1
P1 -->|创建子项目| S2
P1 -->|创建子项目| S3
S1 -.->|关联剧本<br/>一对一| SC1
S2 -.->|关联剧本<br/>一对一| SC2
S3 -.->|关联剧本<br/>一对一| SC3
subgraph "继承关系"
I1[权限继承:<br/>子项目继承父项目的成员权限]
I2[设置继承:<br/>子项目复制父项目的 settings]
I3[文件夹继承:<br/>子项目与父项目在同一文件夹]
end
P1 -.->|继承| I1
P1 -.->|继承| I2
P1 -.->|继承| I3
style P1 fill:#e1f5e1
style S1 fill:#fff4e1
style S2 fill:#fff4e1
style S3 fill:#fff4e1
style SC1 fill:#e1e5ff
style SC2 fill:#e1e5ff
style SC3 fill:#e1e5ff
子项目特性:
- 一对一关系:每个子项目关联一个剧本
- 权限继承:子项目自动继承父项目的成员权限
- 设置继承:子项目复制父项目的配置(分辨率、帧率等)
- 独立管理:子项目可独立管理分镜、资源、导出等
- 自动创建:上传剧本时自动创建子项目
数据库约束:
-- 子项目约束:parent_project_id 和 screenplay_id 必须同时存在或同时为空
CONSTRAINT projects_subproject_screenplay_check CHECK (
(parent_project_id IS NULL AND screenplay_id IS NULL) OR
(parent_project_id IS NOT NULL AND screenplay_id IS NOT NULL)
)
使用场景:
- 电视剧制作:父项目为整部剧,每集为一个子项目
- 系列广告:父项目为广告系列,每个广告为一个子项目
- 多章节内容:父项目为整体内容,每章节为一个子项目
API 接口:
# 获取子项目列表
GET /api/v1/projects/{parent_project_id}/subprojects
# 创建子项目(通常由剧本服务自动调用)
POST /api/v1/projects/{parent_project_id}/subprojects
相关文档
文档版本:v1.0
最后更新:2026-02-02