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

项目管理服务 - 流程图与关系图

文档版本:v1.0
最后更新:2026-02-02
关联文档项目管理服务


目录

  1. 数据库表关系图
  2. 项目创建流程图
  3. 项目克隆流程图
  4. 权限检查流程图
  5. 项目生命周期状态图
  6. 子项目关系图

数据库表关系图

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_idscreenplay_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

流程说明

  1. 身份验证:检查用户登录状态
  2. 用户校验:确认用户存在(引用完整性)
  3. 文件夹校验:如果指定文件夹,检查存在性和权限
  4. 封面图片校验:如果指定封面,检查附件存在性和所属权
  5. 类型验证:确保项目类型为 minecollab
  6. UUID 生成:应用层生成 UUID v7
  7. 创建项目:插入数据库记录
  8. 成员管理:协作项目自动添加创建者为 owner
  9. 日志记录:记录操作日志

项目克隆流程图

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

流程说明

  1. 权限检查:确保用户至少有 viewer 权限
  2. 名称生成:自动生成唯一的副本名称(避免重名)
  3. 异步任务:创建任务并立即返回 task_id
  4. 基本信息复制:复制项目名称、描述、类型等
  5. 配置复制:复制 settings(分辨率、帧率等)
  6. 分镜复制:复制所有分镜及其关联资源
  7. 资源复制:复制视频、附件等文件
  8. 进度更新:实时更新任务进度
  9. 状态通知:任务完成或失败后更新状态

权限检查流程图

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):查看权限(只读访问)

权限检查逻辑

  1. 项目所有者:自动拥有所有权限
  2. 个人项目:只有所有者有权限
  3. 协作项目:检查成员表中的角色
  4. 权限对比:成员角色权限 >= 所需权限时通过

示例

  • 用户角色为 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 软删除,用户不可见,数据保留

状态转换规则

  1. 创建项目 → Active
  2. 用户归档 → Active ↔ Archived(预留功能)
  3. 移至回收站 → Active/Archived → Trashed
  4. 从回收站恢复 → Trashed → Active
  5. 永久删除 → Trashed → SoftDeleted
  6. 自动清理 → Trashed(30天后)→ SoftDeleted
  7. 物理删除 → 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

子项目特性

  1. 一对一关系:每个子项目关联一个剧本
  2. 权限继承:子项目自动继承父项目的成员权限
  3. 设置继承:子项目复制父项目的配置(分辨率、帧率等)
  4. 独立管理:子项目可独立管理分镜、资源、导出等
  5. 自动创建:上传剧本时自动创建子项目

数据库约束

-- 子项目约束: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