Skip to content

团队管理

版本: v0.34.0+ | 组织架构管理 | 层级团队结构

核心特性

  • 🏢 多级团队层次: 支持父子团队嵌套,构建完整组织架构树
  • 👤 角色体系: lead(负责人)、member(成员)、guest(访客)三级角色划分
  • 🔒 删除保护: 存在子团队时禁止删除,保障数据完整性
  • 🔄 负责人变更: 自动完成角色升降级,确保数据一致性
  • 📊 8 个 IPC 通道: 完整的团队 CRUD 和成员管理 API
  • 🔗 深度集成: 与权限引擎、企业审计模块无缝联动

系统架构

┌──────────────┐
│  Renderer    │
│  (Vue 页面)  │
└──────┬───────┘
       │ IPC (8 通道)

┌──────────────────────────────────┐
│     permission-ipc.js            │
│     (IPC 路由注册)               │
└──────────────┬───────────────────┘

      ┌────────▼────────┐
      │  TeamManager    │
      │  (Singleton)    │
      │  ┌────────────┐ │
      │  │ 团队 CRUD  │ │
      │  │ 成员管理   │ │
      │  │ 层级查询   │ │
      │  └────────────┘ │
      └────────┬────────┘

      ┌────────▼────────┐
      │  SQLite          │
      │  org_teams       │
      │  org_team_members│
      └─────────────────┘

系统概述

团队管理模块(TeamManager)是 ChainlessChain 企业版权限体系的核心组件之一,负责组织内子团队的创建、成员管理以及层级结构维护。该模块基于 SQLite 数据库实现持久化存储,支持多级团队嵌套、团队负责人指定、成员角色划分等企业级组织架构功能。

核心特性:

  • 支持多级团队层次结构(父子团队关系)
  • 团队负责人自动注册为团队成员
  • 成员角色体系:lead(负责人)、member(成员)、guest(访客)
  • 团队名称唯一性约束(同一组织内不允许重名)
  • 删除保护机制(存在子团队时禁止删除)
  • 与权限引擎(Permission Engine)、企业审计(Audit)等模块深度集成

源码位置: desktop-app-vue/src/main/permission/team-manager.js

IPC 注册位置: desktop-app-vue/src/main/permission/permission-ipc.js


核心功能

团队创建与管理

创建团队

通过 createTeam(teamData) 方法创建新团队。系统会自动生成 UUID 作为团队 ID,并记录创建时间戳。如果指定了团队负责人(leadDid),系统会自动将其添加为团队成员,角色设置为 lead

参数说明:

参数类型必填说明
orgIdString所属组织 ID
nameString团队名称(同一组织内唯一)
descriptionString团队描述
parentTeamIdString父团队 ID(用于构建层级结构)
leadDidString团队负责人 DID
leadNameString团队负责人名称
avatarString团队头像
settingsObject团队自定义设置(JSON 序列化存储)
createdByString创建者标识(用于记录邀请人)

返回值:

  • 成功:{ success: true, teamId: '<uuid>' }
  • 团队名重复:{ success: false, error: 'TEAM_NAME_EXISTS' }

更新团队

通过 updateTeam(teamId, updates) 方法更新团队信息。系统会自动将 camelCase 字段名转换为 snake_case 数据库字段名,并过滤非法字段。

允许更新的字段:

  • name - 团队名称
  • description - 团队描述
  • parentTeamId - 父团队 ID
  • leadDid - 负责人 DID
  • leadName - 负责人名称
  • avatar - 团队头像
  • settings - 团队设置(Object,自动 JSON 序列化)

返回值: { success: true }

删除团队

通过 deleteTeam(teamId) 方法删除团队。系统内置删除保护机制:如果团队下存在子团队,将拒绝删除并返回错误信息。

返回值:

  • 成功:{ success: true }
  • 存在子团队:{ success: false, error: 'HAS_SUB_TEAMS', message: 'Team has N sub-teams. Delete them first.' }

设置团队负责人

通过 setLead(teamId, leadDid, leadName) 方法设置或变更团队负责人。该方法会同时完成以下操作:

  1. 更新 org_teams 表中的 lead_didlead_name 字段
  2. 将原负责人的成员角色降级为 member
  3. 将新负责人的成员角色升级为 lead

返回值: { success: true }


成员管理

添加成员

通过 addMember(teamId, memberDid, memberName, role, invitedBy) 方法向团队添加成员。

参数说明:

参数类型必填默认值说明
teamIdString-目标团队 ID
memberDidString-成员 DID 标识
memberNameString-成员名称
roleString'member'成员角色:lead / member / guest
invitedByStringnull邀请人标识

返回值:

  • 成功:{ success: true, memberId: '<uuid>' }
  • 已是成员:{ success: false, error: 'ALREADY_MEMBER' }

移除成员

通过 removeMember(teamId, memberDid) 方法从团队中移除指定成员。

返回值: { success: true }

查询团队成员

通过 getTeamMembers(teamId) 方法获取指定团队的所有成员列表。结果按角色降序排列(负责人在前),同角色按加入时间升序排列。

返回值:

javascript
{
  success: true,
  members: [
    {
      id: '<member-record-uuid>',
      memberDid: 'did:example:alice',
      memberName: 'Alice',
      role: 'lead',
      joinedAt: 1708900000000,
      invitedBy: 'did:example:admin'
    },
    // ...
  ]
}

层级结构(团队树)

查询团队列表

通过 getTeams(orgId, options) 方法获取组织下的团队列表。支持按父团队 ID 过滤,可用于逐层构建团队树形结构。

参数说明:

参数类型必填说明
orgIdString组织 ID
options.parentTeamIdString/null父团队 ID。传 null 获取顶级团队,传具体 ID 获取子团队,不传则获取所有团队

返回值:

javascript
{
  success: true,
  teams: [
    {
      id: '<team-uuid>',
      name: '研发部',
      description: '负责产品研发',
      parentTeamId: null,
      leadDid: 'did:example:lead1',
      leadName: '张三',
      avatar: null,
      settings: { maxMembers: 50 },
      memberCount: 12,
      createdAt: 1708900000000,
      updatedAt: 1708900000000
    },
    // ...
  ]
}

构建团队层级树

虽然 TeamManager 本身不提供递归树构建方法,但可以通过 getTeamsparentTeamId 过滤参数逐层查询来构建完整的团队层级树:

javascript
// 获取顶级团队
const topLevel = await teamManager.getTeams(orgId, { parentTeamId: null });

// 递归获取子团队
async function buildTree(teams) {
  for (const team of teams) {
    const children = await teamManager.getTeams(orgId, {
      parentTeamId: team.id,
    });
    team.children = children.teams;
    if (team.children.length > 0) {
      await buildTree(team.children);
    }
  }
}

await buildTree(topLevel.teams);

配置参考

团队管理模块通过数据库进行数据持久化,无需额外配置文件。模块采用单例模式初始化:

javascript
const { getTeamManager } = require("./permission/team-manager");

// 传入 database 实例初始化(仅首次需要)
const manager = getTeamManager(database);

// 后续调用无需再传 database
const manager = getTeamManager();

团队的自定义配置通过 settings 字段以 JSON 格式存储在数据库中,可用于存储团队级别的个性化设置,例如:

json
{
  "maxMembers": 50,
  "allowGuestAccess": true,
  "notificationPreferences": {
    "email": true,
    "inApp": true
  }
}

API 参考

TeamManager 类方法

方法参数返回值说明
createTeam(teamData){ orgId, name, description?, parentTeamId?, leadDid?, leadName?, avatar?, settings?, createdBy? }{ success, teamId }{ success: false, error: 'TEAM_NAME_EXISTS' }创建团队,自动添加负责人为成员
updateTeam(teamId, updates)teamId: string, updates: object{ success: true }更新团队信息,自动 camelCase 转 snake_case
deleteTeam(teamId)teamId: string{ success }{ success: false, error: 'HAS_SUB_TEAMS' }删除团队(有子团队时拒绝)
setLead(teamId, leadDid, leadName)teamId: string, leadDid: string, leadName: string{ success: true }设置团队负责人并更新成员角色
addMember(teamId, did, name, role?, invitedBy?)teamId: string, did: string, name: string, role?: string, invitedBy?: string{ success, memberId }{ success: false, error: 'ALREADY_MEMBER' }添加团队成员
removeMember(teamId, did)teamId: string, did: string{ success: true }移除团队成员
getTeams(orgId, options?)orgId: string, options?: { parentTeamId? }{ success, teams: [...] }查询团队列表,附带成员数量
getTeamMembers(teamId)teamId: string{ success, members: [...] }查询团队成员列表

工厂函数

函数参数说明
getTeamManager(database?)database?: Database获取 TeamManager 单例。首次调用需传入 database 实例

IPC 接口

团队管理模块通过 permission-ipc.js 注册了 8 个 IPC 通道,供渲染进程调用:

IPC 通道参数说明
team:create-team{ orgId, name, description?, parentTeamId?, leadDid?, leadName?, avatar?, settings?, createdBy? }创建团队
team:update-team{ teamId, updates: { name?, description?, parentTeamId?, leadDid?, leadName?, avatar?, settings? } }更新团队
team:delete-team{ teamId }删除团队
team:add-member{ teamId, memberDid, memberName, role?, invitedBy? }添加成员
team:remove-member{ teamId, memberDid }移除成员
team:set-lead{ teamId, leadDid, leadName }设置团队负责人
team:get-teams{ orgId, options?: { parentTeamId? } }查询团队列表
team:get-team-members{ teamId }查询团队成员

渲染进程调用示例:

javascript
// 创建团队
const result = await window.electronAPI.invoke("team:create-team", {
  orgId: "org-001",
  name: "前端开发组",
  description: "负责 Web 前端开发",
  leadDid: "did:example:alice",
  leadName: "Alice",
});

// 查询团队列表
const teams = await window.electronAPI.invoke("team:get-teams", {
  orgId: "org-001",
  options: { parentTeamId: null },
});

数据库 Schema

org_teams 表

存储团队基本信息,支持通过 parent_team_id 自引用构建层级结构。

sql
CREATE TABLE IF NOT EXISTS org_teams (
  id TEXT PRIMARY KEY,
  org_id TEXT NOT NULL,
  name TEXT NOT NULL,
  description TEXT,
  parent_team_id TEXT,
  lead_did TEXT,
  lead_name TEXT,
  avatar TEXT,
  settings TEXT,                -- JSON 格式的自定义设置
  team_type TEXT DEFAULT 'team', -- Phase 6 新增:团队类型(team/department)
  created_at INTEGER NOT NULL,  -- 时间戳(毫秒)
  updated_at INTEGER NOT NULL,  -- 时间戳(毫秒)
  FOREIGN KEY (parent_team_id) REFERENCES org_teams(id) ON DELETE CASCADE,
  UNIQUE(org_id, name)          -- 同一组织内团队名称唯一
);

org_team_members 表

存储团队成员关系,支持角色划分。

sql
CREATE TABLE IF NOT EXISTS org_team_members (
  id TEXT PRIMARY KEY,
  team_id TEXT NOT NULL,
  member_did TEXT NOT NULL,
  member_name TEXT,
  team_role TEXT DEFAULT 'member' CHECK(team_role IN ('lead', 'member', 'guest')),
  joined_at INTEGER NOT NULL,   -- 加入时间戳(毫秒)
  invited_by TEXT,              -- 邀请人标识
  FOREIGN KEY (team_id) REFERENCES org_teams(id) ON DELETE CASCADE,
  UNIQUE(team_id, member_did)   -- 同一团队内成员唯一
);

索引

sql
CREATE INDEX IF NOT EXISTS idx_org_teams_org ON org_teams(org_id);
CREATE INDEX IF NOT EXISTS idx_org_teams_parent ON org_teams(parent_team_id);
CREATE INDEX IF NOT EXISTS idx_org_teams_lead ON org_teams(lead_did);
CREATE INDEX IF NOT EXISTS idx_org_team_members_team ON org_team_members(team_id);
CREATE INDEX IF NOT EXISTS idx_org_team_members_member ON org_team_members(member_did);

使用示例

创建组织团队结构

javascript
const { getTeamManager } = require("./permission/team-manager");
const manager = getTeamManager(database);

// 1. 创建顶级团队(研发中心)
const devCenter = await manager.createTeam({
  orgId: "org-001",
  name: "研发中心",
  description: "负责全部产品研发工作",
  leadDid: "did:example:cto",
  leadName: "王总监",
  createdBy: "did:example:admin",
});
// => { success: true, teamId: 'abc-123-...' }

// 2. 创建子团队(前端组)
const frontendTeam = await manager.createTeam({
  orgId: "org-001",
  name: "前端开发组",
  description: "Web & 移动端前端开发",
  parentTeamId: devCenter.teamId,
  leadDid: "did:example:fe-lead",
  leadName: "李组长",
  settings: { techStack: ["Vue3", "React", "TypeScript"] },
});

// 3. 创建子团队(后端组)
const backendTeam = await manager.createTeam({
  orgId: "org-001",
  name: "后端开发组",
  parentTeamId: devCenter.teamId,
  leadDid: "did:example:be-lead",
  leadName: "陈组长",
});

管理团队成员

javascript
// 添加普通成员
await manager.addMember(
  frontendTeam.teamId,
  "did:example:dev1",
  "张工程师",
  "member",
  "did:example:fe-lead",
);

// 添加访客(只读权限)
await manager.addMember(
  frontendTeam.teamId,
  "did:example:designer1",
  "刘设计师",
  "guest",
  "did:example:fe-lead",
);

// 查询团队成员
const members = await manager.getTeamMembers(frontendTeam.teamId);
console.log(members);
// => { success: true, members: [
//   { memberDid: 'did:example:fe-lead', memberName: '李组长', role: 'lead', ... },
//   { memberDid: 'did:example:dev1', memberName: '张工程师', role: 'member', ... },
//   { memberDid: 'did:example:designer1', memberName: '刘设计师', role: 'guest', ... }
// ]}

// 移除成员
await manager.removeMember(frontendTeam.teamId, "did:example:designer1");

变更团队负责人

javascript
// 将团队负责人从李组长变更为张工程师
await manager.setLead(frontendTeam.teamId, "did:example:dev1", "张工程师");
// 此操作会自动将李组长的角色降级为 member,张工程师的角色升级为 lead

查询团队层级

javascript
// 获取所有顶级团队
const topTeams = await manager.getTeams("org-001", { parentTeamId: null });

// 获取研发中心下的子团队
const subTeams = await manager.getTeams("org-001", {
  parentTeamId: devCenter.teamId,
});

// 获取组织下所有团队(不过滤层级)
const allTeams = await manager.getTeams("org-001");

处理错误场景

javascript
// 创建重名团队
const duplicate = await manager.createTeam({
  orgId: "org-001",
  name: "前端开发组", // 已存在
  leadDid: "did:example:someone",
});
// => { success: false, error: 'TEAM_NAME_EXISTS' }

// 删除含有子团队的团队
const deleteResult = await manager.deleteTeam(devCenter.teamId);
// => { success: false, error: 'HAS_SUB_TEAMS', message: 'Team has 2 sub-teams. Delete them first.' }

// 重复添加成员
const addResult = await manager.addMember(
  frontendTeam.teamId,
  "did:example:dev1",
  "张工程师",
);
// => { success: false, error: 'ALREADY_MEMBER' }

故障排除

常见问题

问题原因解决方案
TEAM_NAME_EXISTS同一组织内已存在同名团队使用不同的团队名称,或先删除已有同名团队
HAS_SUB_TEAMS尝试删除的团队下还有子团队先递归删除所有子团队,再删除父团队
ALREADY_MEMBER成员已在该团队中无需重复添加,如需变更角色请使用 setLead 方法
团队列表为空orgId 不匹配或尚未创建团队检查传入的 orgId 是否正确
settings 字段为 null创建时未传入 settings 参数通过 updateTeam 方法补充设置
更新字段未生效字段名不在允许列表中检查字段名是否正确,仅支持 7 个可更新字段

调试日志

团队管理模块的日志标签为 [Team],可通过日志系统查看操作记录:

[Team] Created team abc-123-...
[Team] Added member did:example:dev1 to team abc-123-...
[Team] Removed member did:example:dev1 from team abc-123-...
[Team] Set lead did:example:dev1 for team abc-123-...
[Team] Deleted team abc-123-...

数据一致性注意事项

  • 删除团队时,org_team_members 表中的关联成员记录会通过外键 ON DELETE CASCADE 自动删除
  • setLead 方法会同时更新 org_teams 表和 org_team_members 表,确保数据一致
  • createTeam 中自动添加负责人为成员的操作不在事务中,如果添加成员失败,团队已创建但负责人未加入成员列表

相关文档

关键文件

文件职责行数
src/main/permission/team-manager.js团队管理核心逻辑(单例模式)~300
src/main/permission/permission-ipc.jsIPC 通道注册与路由~200
src/main/permission/permission-engine.jsRBAC 权限引擎(团队角色联动)~450
src/renderer/stores/team.ts团队 Pinia 状态管理~150

基于 MIT 许可发布