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.
 

6.7 KiB

跨项目资源共享:选择资源后的入库与关联

本文说明在「创建项目时选择共享资源」场景下,前端选中项如何映射为 API 请求、后端如何写入数据库并建立关联。设计依据:ADR 02: 跨项目资源共享


1. 数据库关联方式

关联关系保存在表 project_resource_shares 中,不复制资源数据,只记录「谁共享给谁、共享粒度」。

字段 含义
share_id 主键(UUID v7,应用层生成)
source_project_id 资源来源项目(被共享的项目)
target_project_id 资源目标项目(新建的、使用资源的项目)
share_type 1=整个项目,2=特定资源类型,3=特定资源
resource_type 1=角色,2=场景,3=道具,4=素材(share_type=2 或 3 时使用)
resource_id 具体资源 ID(share_type=3 时使用)
status 1=激活,2=已断开
created_by 创建人(目标项目所有者)
  • 禁止物理外键,引用完整性在 Service/Repository 层校验。
  • 唯一约束(source_project_id, target_project_id, share_type, resource_type, resource_id),避免重复共享。
  • 断开共享时更新 status=2,不物理删除。

2. 前端选中项 → API 请求体映射

创建项目接口:POST /api/v1/projects

请求体中的 shared_resources 数组元素格式(与 ADR 6.1 一致):

interface SharedResourceItem {
  source_type: 'folder' | 'project' | 'resource';
  source_id: string;           // 文件夹ID / 项目ID / 资源ID
  share_type: 1 | 2 | 3;      // 1=整个项目, 2=特定资源类型, 3=特定资源
  resource_type?: number | null; // share_type=2 或 3 时必填:1=角色, 2=场景, 3=道具, 4=素材
}

从 CreateProjectModal / ResourceSelectorPanel 的 SharedResource 构建 shared_resources

前端选中类型 (SharedResource.type) source_type source_id share_type resource_type
folder folder 文件夹 ID 1 null
project project 项目 ID 1 null
character resource 角色资源 ID 3 1
scene resource 场景资源 ID 3 2
prop resource 道具资源 ID 3 3

注意:当前前端若未区分类别,可暂不传 source_type: 'folder' 的「按类型共享」(share_type=2);仅实现「整个项目」和「特定资源」即可。

去重与展开(建议在前端或后端二选一统一规则):

  • 文件夹:后端根据 source_type: 'folder' + source_id 查询该文件夹下所有父项目parent_project_id IS NULL),为每个项目插入一条 share_type=1 的记录;无需前端先展开为多个 project。
  • 项目:一条 source_type: 'project', share_type=1 对应一条 project_resource_shares 记录。
  • 具体资源:一条 source_type: 'resource', share_type=3, resource_type + source_id 对应一条记录;后端需用 resource_id + resource_type 反查 source_project_id(见下节)。

ADR 文档中的 「构建 shared_resources 数组」 示例(见 6.1 与前端交互流程)可直接复用,只需保证前端 SharedResourceidtype 与上表一致。


3. 后端处理流程(入库与关联)

  1. 创建项目
    先执行现有 ProjectService.create_project(),得到 target_project_id(新项目 ID)。

  2. 若请求体无 shared_resources 或为空
    跳过以下步骤,直接返回项目信息。

  3. 逐条处理 shared_resources(建议在 ProjectResourceShareService.create_shares(target_project_id, shared_resources, user_id) 中):

    • source_type === 'folder'
      • source_id 查该文件夹下所有父项目(folder_id = source_id AND parent_project_id IS NULL AND deleted_at IS NULL)。
      • 对每个父项目插入一条:
        (source_project_id=该父项目ID, target_project_id, share_type=1, resource_type=NULL, resource_id=NULL, status=1, created_by=user_id)
      • 唯一约束冲突时 ON CONFLICT DO NOTHING 或忽略重复。
    • source_type === 'project'
      • 插入一条:
        (source_project_id=source_id, target_project_id, share_type=1, resource_type=NULL, resource_id=NULL, status=1, created_by=user_id)
    • source_type === 'resource'
      • 根据 resource_type 在对应表查资源所属项目:
        • 1 → project_characters(或当前项目资源表名)
        • 2 → project_locations
        • 3 → project_props
        • 4 → project_resources(素材)
      • 得到 source_project_id 后插入一条:
        (source_project_id, target_project_id, share_type=3, resource_type=resource_type, resource_id=source_id, status=1, created_by=user_id)
      • 若资源不存在或无权限共享,则校验失败,不插入。
  4. 权限
    插入前需校验:当前用户对 source_project_id 有访问权限(所有者或成员),参见 ADR 5.1 check_share_permission

  5. 响应
    可按 ADR 6.1 在创建项目响应中返回 shared_resources: { total, shares: [...] },便于前端展示或调试。


4. 与 CreateProjectModal 的对接要点

  • 提交时:将 ResourceSelectorPanelselectedResourcesSharedResource[])按上表转换为 shared_resources 数组,随 POST /api/v1/projects 的 body 一起提交。
  • 当前代码:CreateProjectModal 中已有 TODO(如 // TODO: 将 selectedResources 传递给后端 API),只需:
    1. 实现 selectedResourcesshared_resources 的转换(folder/project/character/scene/prop → source_type/share_type/resource_type);
    2. createProject.mutateAsync 的请求参数中增加 sharedResources(或与后端约定字段名 shared_resources)。
  • 后续:若支持「按资源类型共享」(勾选某项目下全部角色),再增加 share_type=2 与对应 resource_type 的构建逻辑。

5. 相关文档与代码位置

  • ADR02-cross-project-resource-sharing.md(表结构、共享粒度、API、权限、查询)
  • 前端client/src/components/features/project/CreateProjectModal.tsxResourceSelectorPanel.tsx,类型 SharedResourceshared-resource.ts
  • 后端(待实现):ProjectResourceShareRepositoryProjectResourceShareService.create_shares()ProjectService.create_project(..., shared_resources=...),以及 POST /api/v1/projects 的请求体解析与校验。