Skip to content

Workspace Management

概述

Workspace Management 是公共系统中的一个重要组件,负责管理用户的工作空间、项目组织和团队信息。它提供了以下主要功能:

  • 成员权限管理
  • 组织层级管理(工作空间、项目、库)
  • 用户相关
  • 通知管理

关联的外部系统

  • 用户系统
  • 消息通知系统

关联的中间件

  • 业务数据库
  • 消息队列
  • 缓存

用户相关

登录调用用户系统的登录接口,获取用户信息,其中通过前端用户角色/用户类型等信息判断跳转的系统或页面布局菜单渲染。如果未来存在一些和Flow业务强关联,不适合放在用户系统中直接维护的字段,可以将这部分逻辑交给Workspace模块,当前先不考虑。

工作空间管理

工作空间是 Flow 系统中最顶层的组织结构,一个工作空间可以包含多个项目,每个工作空间完全独立,后面权限部分都是基于工作空间内部的权限控制,对于工作空间的权限管理,是单独的。

工作空间包含以下操作:

  • 创建工作空间
  • 查询工作空间列表
  • 删除工作空间
  • 更新工作空间信息
    • 设置工作空间管理员
    • 设置工作空间成员
  • 权限控制
    • Owner和工作空间管理员可以修改工作空间信息
  • 转交工作空间(Owner 变更)

权限继承策略

权限继承模式

  • Workspace
    • 这是最高层级,通常定义了用户在整个工作空间中的角色和权限。例如,管理员可以管理所有项目和用户。
  • Project
    • 在项目层面,用户可以被分配不同的角色,如管理员、编辑、读者等。这些角色决定了用户在项目中的操作权限。
  • Repository
    • 在项目中的一个库中,可以进一步细化权限。例如,即使一个用户在项目中有读写权限,但在某个特定的库中可能只被赋予读取权限。 权限继承规则
  • 最小权限原则 在没有明确设置库级别权限的情况下,用户将继承项目级别的权限。如果库级别设置了更低的权限,则库级别的权限将覆盖项目级别的权限。
  • 最高权限优先 如果用户在项目中有较高的权限,但在库中被赋予较低的权限,则库中的较低权限将被应用。如果库中没有明确设置权限,则用户将使用项目中的较高权限。

权限体系

基于SaaS底层的角色

  • 租户管理员 租户管理员可以增加租户中的普通用户

基于工作空间的权限

层级的权限矩阵包括工作空间,项目,库

转让本期不实现

权限矩阵应增加以下规则:

角色数据权限增加成员删除成员修改成员角色查询成员转让所有权
Owner增删改查
Admin增改查✓*✓*✓*
Editor改查
Viewer
注意:Admin 不能新增删除或修改 Owner 角色的成员。

前端实现

对于数据权限,实现一个v-permission指令,接受一个元素对应的权限点(增删改查),传递一个当前用户对于这个结构的角色(Owner,Admin,Editor,Viewer),根据上表中的权限判断原则,进一步处理这个元素的显示隐藏。 对于Members菜单中的权限,可以按照权限矩阵表写死。

API 接口设计

  1. 创建工作空间

    • 说明:创建新的工作空间
    • 方法:POST
    • URL:/api/v1/workspaces
    • 请求示例:
      json
      {
        "name": "示例工作空间",
        "description": "工作空间描述信息",
        "owner_id": "主用户UUID",
        "tenant_id": "租户UUID",
        "members": ["成员UUID1", "成员UUID2"]  // 可选初始成员列表
      }
    • 响应示例:
      json
      {
        "code": 200,
        "message": "工作空间创建成功",
        "data": {
          "workspace_id": "生成的工作空间UUID",
        }
      }
  2. 查询工作空间列表

    • 说明:获取当前用户可访问的工作空间列表
    • 方法:GET
    • URL:/api/v1/workspaces
    • 响应示例:
      json
      [
        {
          "workspace_id": "UUID",
          "name": "示例工作空间",
          "description": "工作空间描述信息",
          "owner_id": "主用户UUID",
          "created_at": "时间戳"
        }
      ]
  3. 获取工作空间详情

    • 说明:获取指定工作空间的详细信息
    • 方法:GET
    • URL:/api/v1/workspaces/
    • 响应示例:
      json
      {
        "workspace_id": "UUID",
        "name": "示例工作空间",
        "description": "工作空间描述信息",
        "owner_id": "主用户UUID",
        "members": [
          {
            "user_id": "UUID",
            "role": "OWNER | ADMIN | EDITOR | VIEWER"
          }
        ],
        "created_at": "时间戳",
        "updated_at": "时间戳"
      }
  4. 更新工作空间信息

    • 说明:更新工作空间的基本信息和成员配置
    • 方法:PATCH
    • URL:/api/v1/workspaces/
    • 请求示例:
      json
      {
        "name": "更新后的工作空间名称",
        "description": "更新后的描述信息",
        "admin_ids": ["管理员UUID1", "管理员UUID2"],
        "member_ids": ["成员UUID1", "成员UUID2"]
      }
    • 响应示例:
      json
      {
        "code": 200,
        "message": "工作空间信息更新成功"
      }
  5. 删除工作空间

    • 说明:删除指定的工作空间
    • 方法:DELETE
    • URL:/api/v1/workspaces/
    • 响应示例:
      json
      {
        "code": 200,
        "message": "工作空间删除成功"
      }
  6. 转交工作空间

    • 说明:变更工作空间的所有者
    • 方法:PUT
    • URL:/api/v1/workspaces/{workspaceId}/transfer
    • 请求示例:
      json
      {
        "new_owner_id": "新拥有者UUID",
        "reason": "转交原因说明"
      }
    • 响应示例:
      json
      {
        "code": 200,
        "message": "工作空间所有权转交成功"
      }

数据库设计

工作空间表 (workspaces)

sql
CREATE TABLE workspaces (
  workspace_id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  type INT NOT NULL, -- 工作空间类型,1LCMS,2为TMS,默认1
  name VARCHAR(255) NOT NULL,
  description TEXT,
  owner_id UUID NOT NULL,  -- 不再需要外键约束
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

工作空间成员表 (workspace_members)

sql
CREATE TABLE workspace_members (
  workspace_id UUID NOT NULL,
  user_id UUID NOT NULL,
  role VARCHAR(50) DEFAULT 'MEMBER',  -- 角色:OWNER、ADMIN、EDITOR、VIEWER
  joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (workspace_id, user_id),
  CONSTRAINT fk_workspace FOREIGN KEY (workspace_id) REFERENCES workspaces(workspace_id)
);

权限管理

根据用户在本系统内的角色不同,管理用户可访问的资源和可执行的操作,本质上基于RBAC的权限管理。

API 接口设计

  1. 用户角色分配接口

    • 说明:为指定工作空间内的用户分配系统角色
    • 方法:POST
    • URL:/api/v1/workspaces/{workspaceId}/users/{userId}/roles
    • 请求示例:
      json
      {
        "roles": ["EDITOR", "VIEWER"]
      }
    • 响应示例:
      json
      {
        "code": 200,
        "message": "User roles updated successfully"
      }
  2. 角色权限配置接口 (全局统一,不需要指定工作空间,项目前期可以先固定,无需支持动态配置)

    • 说明:配置指定的某个角色所具备的权限
    • 方法:PUT
    • URL:/api/v1/roles/{roleId}/permissions
    • 请求示例:
      json
      {
        "permissions": ["CREATE_PROJECT", "DELETE_PROJECT", "UPDATE_USER"]
      }
    • 响应示例:
      json
      {
        "code": 200,
        "message": "Role permissions updated successfully"
      }
  3. 当前用户权限查询接口

    • 说明:获取当前用户在当前工作空间中所拥有的权限
    • 方法:GET
    • URL:/api/v1/workspaces/{workspaceId}/users/{userId}/permissions
    • 响应示例:
      json
      {
        "code": 200,
        "message": "当前用户权限获取成功",
        "data": [
          "CREATE_PROJECT",
          "VIEW_CONTENT",
          "MANAGE_TEAM"
        ]
      }

数据库设计

角色表 (workspace_roles)

sql
CREATE TABLE workspace_roles (
  role_id UUID PRIMARY KEY,
  role_name VARCHAR(255) UNIQUE NOT NULL,
  description TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

用户角色映射表 (workspace_user_roles)

sql
CREATE TABLE workspace_user_roles (
  workspace_id UUID NOT NULL,  -- 新增工作空间ID,用于实现权限隔离
  user_id UUID NOT NULL,
  role_id UUID NOT NULL,
  assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (workspace_id, user_id, role_id),
  CONSTRAINT fk_workspace FOREIGN KEY (workspace_id) REFERENCES workspaces(workspace_id),
  CONSTRAINT fk_workspace_roles FOREIGN KEY (role_id) REFERENCES workspace_roles(role_id)
);

角色权限映射表 (workspace_role_permissions)

sql
CREATE TABLE workspace_role_permissions (
  role_id UUID NOT NULL,
  permission VARCHAR(100) NOT NULL,
  assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (role_id, permission),
  CONSTRAINT fk_role FOREIGN KEY (role_id) REFERENCES workspace_roles(role_id)
);

权限拒绝规则表 (workspace_deny_rules)

sql
CREATE TABLE workspace_deny_rules (
    rule_id UUID PRIMARY KEY,
    user_id UUID NOT NULL,
    scope_type VARCHAR(50) NOT NULL, -- 'WORKSPACE', 'PROJECT', 'REPOSITORY'
    scope_id UUID NOT NULL,          -- workspace_id, project_id 或 repository_id
    permission VARCHAR(100) NOT NULL, -- 具体被拒绝的权限
    created_by UUID NOT NULL,        -- 创建规则的用户
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    reason TEXT,                     -- 记录创建该规则的原因
    CONSTRAINT uq_deny_rule UNIQUE (user_id, scope_type, scope_id, permission)
);

-- 创建索引以提高查询性能
CREATE INDEX idx_deny_rules_user ON workspace_deny_rules(user_id);
CREATE INDEX idx_deny_rules_scope ON workspace_deny_rules(scope_type, scope_id);

权限检查实现说明

在实际使用这些权限表时,系统应该按照以下逻辑进行权限检查:

  1. 检查顺序

    • 首先检查用户在仓库级别是否有明确的权限设置
    • 如果没有仓库级别权限,检查项目级别权限
    • 如果没有项目级别权限,检查工作空间级别权限
    • 最后检查是否存在针对该权限的拒绝规则
  2. 权限判断规则

    • 如果在某一级别找到明确的权限设置,使用该权限
    • 如果没有找到明确的权限设置,继承上一级的权限
    • 如果存在针对特定权限的拒绝规则,则该权限将被移除
    • 如果都没有找到权限设置,默认拒绝访问
  3. 权限继承示例

    sql
    WITH user_permissions AS (
        -- 检查仓库级别权限
        SELECT 'REPOSITORY' as level, role, array_agg(p.permission) as permissions
        FROM workspace_repository_members m
        JOIN workspace_role_permissions p ON m.role = p.role
        WHERE repository_id = :repository_id AND user_id = :user_id
        GROUP BY role
        
        UNION
        
        -- 检查项目级别权限
        SELECT 'PROJECT' as level, role, array_agg(p.permission) as permissions
        FROM workspace_project_members m
        JOIN workspace_role_permissions p ON m.role = p.role
        WHERE project_id = :project_id AND user_id = :user_id
        GROUP BY role
        
        UNION
        
        -- 检查工作空间级别权限
        SELECT 'WORKSPACE' as level, role, array_agg(p.permission) as permissions
        FROM workspace_members m
        JOIN workspace_role_permissions p ON m.role = p.role
        WHERE workspace_id = :workspace_id AND user_id = :user_id
        GROUP BY role
    ),
    denied_permissions AS (
        -- 获取所有适用的拒绝规则
        SELECT permission
        FROM workspace_deny_rules
        WHERE user_id = :user_id
        AND (
            (scope_type = 'REPOSITORY' AND scope_id = :repository_id) OR
            (scope_type = 'PROJECT' AND scope_id = :project_id) OR
            (scope_type = 'WORKSPACE' AND scope_id = :workspace_id)
        )
    )
    SELECT 
        up.level,
        up.role,
        array(
            SELECT unnest(up.permissions)
            EXCEPT
            SELECT permission FROM denied_permissions
        ) as effective_permissions
    FROM user_permissions up
    ORDER BY 
        CASE up.level
            WHEN 'REPOSITORY' THEN 1
            WHEN 'PROJECT' THEN 2
            WHEN 'WORKSPACE' THEN 3
        END
    LIMIT 1;

权限拒绝规则 API 接口设计

1. 创建拒绝规则

  • 说明:创建新的权限拒绝规则
  • 方法:POST
  • URL:/api/v1/deny-rules
  • 请求示例:
    json
    {
        "user_id": "UUID",
        "scope_type": "WORKSPACE",
        "scope_id": "workspace-uuid",
        "permission": "CREATE_PROJECT",
        "reason": "临时限制用户创建项目权限"
    }
  • 响应示例:
    json
    {
        "code": 200,
        "message": "拒绝规则创建成功",
        "data": {
            "rule_id": "生成的规则UUID"
        }
    }

2. 删除拒绝规则

  • 说明:删除指定的权限拒绝规则
  • 方法:DELETE
  • URL:/api/v1/deny-rules/
  • 响应示例:
    json
    {
        "code": 200,
        "message": "拒绝规则删除成功"
    }

3. 查询用户的拒绝规则

  • 说明:查询指定用户在特定范围内的拒绝规则
  • 方法:GET
  • URL:/api/v1/users/{userId}/deny-rules?scope_type=WORKSPACE&scope_id=xxx
  • 响应示例:
    json
    {
        "code": 200,
        "data": {
            "rules": [
                {
                    "rule_id": "UUID",
                    "scope_type": "WORKSPACE",
                    "scope_id": "workspace-uuid",
                    "permission": "CREATE_PROJECT",
                    "created_at": "2024-03-20T10:00:00Z",
                    "reason": "临时限制用户创建项目权限"
                }
            ]
        }
    }

项目管理

项目管理在LCMS中负责创建管理项目相关的容器,并关联到对应的工作空间,提供对容器的增删改查,成员管理等,但仅限于容器结构,不涉及内部的内容,所以本质上只是维护项目结构。

项目结构分为,Project和Repository,Project是Repository的容器,Repository是项目的内容,Repository可以理解为项目中的一个文档,Project可以理解为项目中的一个文件夹,他们都有扩展表维护更多的可扩展的元数据,权限为集成关系,也就是说如果一个用户有Project的权限,那么他同时也拥有该Project下所有Repository的权限,除非单独设置Repository的权限。

项目管理 API 接口设计

1. 创建项目

  • 说明:在指定工作空间下创建新的项目
  • 方法:POST
  • URL:/api/v1/workspaces/{workspaceId}/projects
  • 请求示例:
json
{
  "name": "示例项目",
  "description": "项目描述信息",
  "members": ["成员UUID1", "成员UUID2"]  // 可选初始成员列表
}
  • 响应示例:
json
{
  "code": 200,
  "message": "项目创建成功",
  "data": {
    "project_id": "生成的项目UUID",
  }
}

2. 查询项目列表

  • 说明:获取指定工作空间下的所有项目
  • 方法:GET
  • URL:/api/v1/workspaces/{workspaceId}/projects
  • 响应示例:
json
[
  {
    "project_id": "UUID",
    "name": "示例项目",
    "description": "项目描述信息",
    "owner_id": "项目负责人UUID",
    "created_at": "时间戳"
  }
]

3. 获取项目详情

  • 说明:获取指定项目的详细信息,包括项目信息和成员列表
  • 方法:GET
  • URL:/api/v1/workspaces/{workspaceId}/projects/
  • 响应示例:
json
{
  "project_id": "UUID",
  "name": "示例项目",
  "description": "项目描述信息",
  "owner_id": "项目负责人UUID",
  "members": [
    {
      "user_id": "UUID",
      "role": "OWNER | ADMIN | MEMBER"
    }
  ],
  "created_at": "时间戳",
  "updated_at": "时间戳"
}

4. 更新项目

  • 说明:更新项目的基本信息和成员配置
  • 方法:PATCH
  • URL:/api/v1/workspaces/{workspaceId}/projects/
  • 请求示例:
json
{
  "name": "更新后的项目名称",
  "description": "更新后的项目描述",
  "member_ids": ["成员UUID1", "成员UUID2"]
}
  • 响应示例:
json
{
  "code": 200,
  "message": "项目更新成功"
}

5. 删除项目

  • 说明:删除指定的项目
  • 方法:DELETE
  • URL:/api/v1/workspaces/{workspaceId}/projects/
  • 响应示例:
json
{
  "code": 200,
  "message": "项目删除成功"
}

仓库管理 API 接口设计

仓库(Repository)作为项目中的文档管理单元,主要用于管理项目中的内容文档。

1. 创建文档

  • 说明:在指定项目下创建新的文档
  • 方法:POST
  • URL:/api/v1/workspaces/{workspaceId}/projects/{projectId}/repositories
  • 请求示例:
json
{
  "name": "仓库标题",
}
  • 响应示例:
json
{
  "code": 200,
  "message": "仓库创建成功",
  "data": {
    "repository_id": "仓库UUID",
  }
}

权限调整接口

调整项目中成员权限接口

  • 说明:用于调整指定项目中成员在项目层级的权限
  • 方法:PATCH
  • URL:/api/v1/workspaces/{workspaceId}/projects/{projectId}/members/{memberId}/permissions
  • 请求示例:
    json
    {
      "permissions": ["ADMIN", "MEMBER"]
    }
  • 响应示例:
    json
    {
      "code": 200,
      "message": "项目成员权限更新成功"
    }

调整仓库中成员权限接口

  • 说明:用于调整指定仓库中成员的权限
  • 方法:PATCH
  • URL:/api/v1/workspaces/{workspaceId}/projects/{projectId}/repositories/{repositoryId}/members/{memberId}/permissions
  • 请求示例:
    json
    {
      "permissions": ["EDITOR", "VIEWER"]
    }
  • 响应示例:
    json
    {
      "code": 200,
      "message": "仓库成员权限更新成功"
    }

数据库设计

工作空间项目表 (workspace_projects)

sql
CREATE TABLE workspace_projects (
  project_id UUID PRIMARY KEY,
  workspace_id UUID NOT NULL,
  name VARCHAR(255) NOT NULL,
  description TEXT,
  owner_id UUID NOT NULL,  -- 不再需要外键约束
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_project_workspace FOREIGN KEY (workspace_id) REFERENCES workspaces(workspace_id)
);

项目仓库表 (workspace_repositories)

sql
CREATE TABLE workspace_repositories (
  repository_id UUID PRIMARY KEY,
  project_id UUID NOT NULL,
  title VARCHAR(255) NOT NULL,
  content TEXT,
  creator_id UUID NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_repository_project FOREIGN KEY (project_id) REFERENCES workspace_projects(project_id)
);

扩展项目元数据表:用于存储项目的额外元数据(key-value 结构)

sql
CREATE TABLE workspace_project_metadata (
  project_id UUID NOT NULL,
  meta_key VARCHAR(255) NOT NULL,
  meta_value TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (project_id, meta_key),
  CONSTRAINT fk_workspace_project_metadata FOREIGN KEY (project_id) REFERENCES workspace_projects(project_id)
);

扩展仓库元数据表:用于存储仓库的额外元数据(key-value 结构)

sql
CREATE TABLE workspace_repository_metadata (
  repository_id UUID NOT NULL,
  meta_key VARCHAR(255) NOT NULL,
  meta_value TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (repository_id, meta_key),
  CONSTRAINT fk_workspace_repository_metadata FOREIGN KEY (repository_id) REFERENCES workspace_repositories(repository_id)
);

项目成员权限表 (workspace_project_members)

sql
CREATE TABLE workspace_project_members (
  project_id UUID NOT NULL,
  user_id UUID NOT NULL,
  role VARCHAR(50) NOT NULL,  -- OWNER、ADMIN、EDITOR、VIEWER
  assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (project_id, user_id),
  CONSTRAINT fk_project_member_project FOREIGN KEY (project_id) REFERENCES workspace_projects(project_id)
);

CREATE INDEX idx_project_members_user ON workspace_project_members(user_id);

仓库成员权限表 (workspace_repository_members)

sql
CREATE TABLE workspace_repository_members (
  repository_id UUID NOT NULL,
  user_id UUID NOT NULL,
  role VARCHAR(50) NOT NULL,  -- OWNER、ADMIN、EDITOR、VIEWER
  assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (repository_id, user_id),
  CONSTRAINT fk_repository_member_repository FOREIGN KEY (repository_id) REFERENCES workspace_repositories(repository_id)
);

CREATE INDEX idx_repository_members_user ON workspace_repository_members(user_id);

字典服务

字典服务负责为Flow中各个服务提供字典(枚举)服务,通过字典的key获取字典的value,字典项value支持多种数据类型,包括字符串、数字、布尔值、JSON等,字典项value支持国际化,支持排序。

数据库设计

字典表 (dictionaries)

sql
CREATE TABLE dictionaries (
  dictionary_id UUID PRIMARY KEY,
  code VARCHAR(100) NOT NULL,  -- 字典编码,用于API调用
  name VARCHAR(255) NOT NULL,  -- 字典名称
  description TEXT,            -- 字典描述
  workspace_id UUID,           -- 工作空间ID,为空表示租户级字典
  is_system BOOLEAN DEFAULT false,  -- 是否为系统预设字典(目前不允许用户创建字典,所以目前应该都为true)
  status VARCHAR(20) DEFAULT 'ENABLED',  -- 状态:ENABLED | DISABLED
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT uq_dictionary_code UNIQUE (code, workspace_id)
);

CREATE INDEX idx_dictionaries_workspace ON dictionaries(workspace_id);

字典项表 (dictionary_items)

sql
CREATE TABLE dictionary_items (
  item_id UUID PRIMARY KEY,
  dictionary_id UUID NOT NULL,
  value_type VARCHAR(20) NOT NULL,  -- 值类型:STRING | NUMBER | BOOLEAN | JSON
  value_string VARCHAR(1000),       -- 字符串值
  value_number DECIMAL(20, 6),      -- 数字值
  value_boolean BOOLEAN,            -- 布尔值
  value_json JSONB,                 -- JSON值
  sort_order INT DEFAULT 0,         -- 排序顺序
  status VARCHAR(20) DEFAULT 'ENABLED',  -- 状态:ENABLED | DISABLED
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_dictionary_item FOREIGN KEY (dictionary_id) REFERENCES dictionaries(dictionary_id),
  CONSTRAINT uq_dictionary_item_value UNIQUE (dictionary_id, value_string, value_number, value_boolean)
);

CREATE INDEX idx_dictionary_items_dictionary ON dictionary_items(dictionary_id);
CREATE INDEX idx_dictionary_items_sort ON dictionary_items(dictionary_id, sort_order);

字典项国际化表 (dictionary_item_i18n)

sql
CREATE TABLE dictionary_item_i18n (
  i18n_id UUID PRIMARY KEY,
  item_id UUID NOT NULL,
  locale VARCHAR(20) NOT NULL,  -- 语言代码,如 zh_CN, en_US
  label VARCHAR(500) NOT NULL,  -- 显示标签
  description TEXT,             -- 描述
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_dictionary_item_i18n FOREIGN KEY (item_id) REFERENCES dictionary_items(item_id),
  CONSTRAINT uq_dictionary_item_locale UNIQUE (item_id, locale)
);

CREATE INDEX idx_dictionary_item_i18n_item ON dictionary_item_i18n(item_id);
CREATE INDEX idx_dictionary_item_i18n_locale ON dictionary_item_i18n(locale);

API 接口设计

1. 创建字典(本期先不实现)

  • 说明:创建新的字典
  • 方法:POST
  • URL:/api/v1/dictionaries
  • 请求示例:
    json
    {
      "code": "user_status",
      "name": "用户状态",
      "description": "系统中用户的状态枚举",
      "workspace_id": "UUID"  // 可选
    }
  • 响应示例:
    json
    {
      "code": 200,
      "message": "字典创建成功",
      "data": {
        "dictionary_id": "生成的字典UUID"
      }
    }

2. 添加字典项(本期先不实现)

  • 说明:向指定字典添加字典项
  • 方法:POST
  • URL:/api/v1/dictionaries/{dictionaryId}/items
  • 请求示例:
    json
    {
      "value_type": "STRING",
      "value_string": "ACTIVE",
      "sort_order": 1,
      "i18n": [
        {
          "locale": "zh_CN",
          "label": "激活",
          "description": "用户处于激活状态"
        },
        {
          "locale": "en_US",
          "label": "Active",
          "description": "User is in active state"
        }
      ]
    }
  • 响应示例:
    json
    {
      "code": 200,
      "message": "字典项添加成功",
      "data": {
        "item_id": "生成的字典项UUID"
      }
    }

3. 获取字典列表(本期先不实现)

  • 说明:获取字典列表,支持按工作空间筛选
  • 方法:GET
  • URL:/api/v1/dictionaries?workspace_id=xxx
  • 响应示例:
    json
    {
      "code": 200,
      "data": {
        "items": [
          {
            "dictionary_id": "UUID",
            "code": "user_status",
            "name": "用户状态",
            "description": "系统中用户的状态枚举",
            "is_system": false,
            "status": "ENABLED",
            "created_at": "2024-03-20T10:00:00Z"
          }
        ]
      }
    }

4. 获取字典项列表(本期先不实现)

  • 说明:获取指定字典的所有字典项
  • 方法:GET
  • URL:/api/v1/dictionaries/{dictionaryId}/items?locale=zh_CN
  • 响应示例:
    json
    {
      "code": 200,
      "data": {
        "items": [
          {
            "item_id": "UUID",
            "value_type": "STRING",
            "value": "ACTIVE",  // 根据value_type返回对应类型的值
            "label": "激活",    // 根据请求的locale返回对应的标签
            "description": "用户处于激活状态",
            "sort_order": 1,
            "status": "ENABLED"
          }
        ]
      }
    }

5. 更新字典项(本期先不实现)

  • 说明:更新字典项信息
  • 方法:PATCH
  • URL:/api/v1/dictionaries/{dictionaryId}/items/
  • 请求示例:
    json
    {
      "value_string": "ACTIVE_USER",
      "sort_order": 2,
      "i18n": [
        {
          "locale": "zh_CN",
          "label": "已激活"
        }
      ]
    }
  • 响应示例:
    json
    {
      "code": 200,
      "message": "字典项更新成功"
    }

6. 删除字典项(本期先不实现)

  • 说明:删除指定的字典项
  • 方法:DELETE
  • URL:/api/v1/dictionaries/{dictionaryId}/items/
  • 响应示例:
    json
    {
      "code": 200,
      "message": "字典项删除成功"
    }

7. 批量获取字典(本期先不实现)

  • 说明:根据字典编码批量获取字典数据
  • 方法:GET
  • URL:/api/v1/dictionaries/batch?codes=source_language,target_language&locale=zh_CN
  • 响应示例:
    json
    {
      "code": 200,
      "data": {
        "dictionaries": {
          "source_language": [
            {
              "value": "zh_CN",
              "label": "中文(简体)",
              "sort_order": 1
            },
            {
              "value": "en_US",
              "label": "英文(美国)",
              "sort_order": 2
            }
          ],
          "target_language": [
            {
              "value": "zh_CN",
              "label": "中文(简体)",
              "sort_order": 1
            },
            {
              "value": "en_US",
              "label": "英文(美国)",
              "sort_order": 2
            }
          ]
        }
      }
    }

8. 获取工作空间下所有系统内置字典(本期仅实现本接口)

  • 说明:获取当前工作空间下所有系统内置的字典及其字典项
  • 方法:GET
  • URL:/api/v1/workspaces/{workspaceId}/system-dictionaries?locale=zh_CN
  • 响应示例:
    json
    {
      "code": 200,
      "data": {
        "dictionaries": [
          {
            "dictionary_id": "UUID1",
            "code": "source_language",
            "name": "源语言",
            "description": "系统支持的源语言列表",
            "items": [
              {
                "value": "zh_CN",
                "label": "中文(简体)",
                "description": "中国大陆使用的简体中文",
                "sort_order": 1
              },
              {
                "value": "en_US",
                "label": "英文(美国)",
                "description": "美国英语",
                "sort_order": 2
              }
            ]
          },
          {
            "dictionary_id": "UUID2",
            "code": "target_language",
            "name": "目标语言",
            "description": "系统支持的目标语言列表",
            "items": [
              {
                "value": "zh_CN",
                "label": "中文(简体)",
                "description": "中国大陆使用的简体中文",
                "sort_order": 1
              },
              {
                "value": "en_US",
                "label": "英文(美国)",
                "description": "美国英语",
                "sort_order": 2
              }
            ]
          },
          {
            "dictionary_id": "UUID3",
            "code": "user_status",
            "name": "用户状态",
            "description": "系统中用户的状态枚举",
            "items": [
              {
                "value": "ACTIVE",
                "label": "激活",
                "description": "用户处于激活状态",
                "sort_order": 1
              },
              {
                "value": "INACTIVE",
                "label": "未激活",
                "description": "用户处于未激活状态",
                "sort_order": 2
              }
            ]
          }
        ]
      }
    }

SQL查询示例

以下是实现字典服务API接口的SQL查询示例(主要是展示逻辑,非最终实现定义):

获取工作空间下所有系统内置字典查询

sql
-- 获取工作空间下所有系统内置字典及其字典项
-- 对应API: GET /api/v1/workspaces/{workspaceId}/system-dictionaries?locale=zh_CN

-- 第一步:获取所有系统内置字典
WITH system_dictionaries AS (
    SELECT 
        d.dictionary_id,
        d.code,
        d.name,
        d.description,
        d.is_system
    FROM 
        dictionaries d
    WHERE 
        d.is_system = true
        AND (d.workspace_id = :workspace_id OR d.workspace_id IS NULL)
        AND d.status = 'ENABLED'
),
-- 第二步:获取每个字典的所有字典项
dictionary_items_with_i18n AS (
    SELECT 
        di.dictionary_id,
        di.item_id,
        CASE di.value_type
            WHEN 'STRING' THEN di.value_string
            WHEN 'NUMBER' THEN CAST(di.value_number AS VARCHAR)
            WHEN 'BOOLEAN' THEN CAST(di.value_boolean AS VARCHAR)
            WHEN 'JSON' THEN CAST(di.value_json AS VARCHAR)
        END AS value,
        i18n.label,
        i18n.description,
        di.sort_order
    FROM 
        dictionary_items di
    LEFT JOIN 
        dictionary_item_i18n i18n ON di.item_id = i18n.item_id AND i18n.locale = :locale
    WHERE 
        di.status = 'ENABLED'
)
-- 最终查询:组合字典和字典项
SELECT 
    sd.dictionary_id,
    sd.code,
    sd.name,
    sd.description,
    sd.is_system,
    json_agg(
        json_build_object(
            'value', di.value,
            'label', di.label,
            'description', di.description,
            'sort_order', di.sort_order
        ) ORDER BY di.sort_order ASC
    ) AS items
FROM 
    system_dictionaries sd
LEFT JOIN 
    dictionary_items_with_i18n di ON sd.dictionary_id = di.dictionary_id
GROUP BY 
    sd.dictionary_id, sd.code, sd.name, sd.description, sd.is_system
ORDER BY 
    sd.code;