- 后端
- 前端
- 协同办公
- 即时通讯
- 企业应用
【免费下载链接】dootask
DooTask是一款开源在线项目任务管理工具,提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM,文件管理等工具;同时消息功能使用非对称加密技术让你的沟通更安全。
本篇围绕 DooTask 的项目任务权限体系展开:ProjectPermission表如何把「谁能对任务执行什么操作」拆成 4 类权限主体 × 11 个权限点的矩阵,矩阵以 JSON 形式按项目存储,并贯穿ProjectController中加列、改任务、归档、跨项目移动等操作的鉴权链路。读完本文,你将掌握权限矩阵的数据结构、默认值初始化、逐主体校验流程、配置 API 的参数含义,以及该体系的边界与限制。
一、ProjectPermission 解决什么问题
DooTask 用ProjectPermission表细化「谁能做什么任务级操作」:每条规则按4 类主体 + 11 个权限点的矩阵展开。规则只针对owner=0的普通成员——拥有者 / 管理员默认全开,不受矩阵限制。
需要明确它的边界(文档标注的 negative 约束):
- 权限规则只控制任务操作(add / update / remove / move 等 11 个权限点),不控制项目设置(如改项目名称、项目成员管理等);
- 拥有者 / 管理员的权限不可被 ProjectPermission 限制(默认全开);
- 权限规则只能在「项目级」配置,不能按列、按标签细分;
- 不支持「按部门角色」自动套用权限。
该概念在知识库中的适用范围为scope: admin,前置条件是操作者为项目拥有者(project_users.owner=1)或管理员(owner=2)。
二、数据模型:一张表 + 一个 JSON 矩阵
表结构
project_permissions表由迁移 create_project_permissions_table 创建,结构非常简单:
| 字段 | 类型 | 说明 |
|---|---|---|
id | bigint 自增 | 主键 |
project_id | bigint,默认 0 | 项目 ID |
permissions | text | 完整权限矩阵的 JSON 序列化结果 |
created_at/updated_at | timestamps | 时间戳 |
迁移里先判断Schema::hasTable('project_permissions'),存在则跳过,保证升级场景幂等。
模型层
模型 ProjectPermission 中,permissions字段通过 accessor 自动完成 JSON ↔ 数组转换:
public function getPermissionsAttribute($value) { return Base::json2array($value); }11 个权限点以类常量定义(注意:数据库里的 key 是小写下划线形式):
const TASK_LIST_ADD = 'task_list_add'; // 添加列 const TASK_LIST_UPDATE = 'task_list_update'; // 修改列 const TASK_LIST_REMOVE = 'task_list_remove'; // 删除列 const TASK_LIST_SORT = 'task_list_sort'; // 列表排序 const TASK_ADD = 'task_add'; // 任务添加 const TASK_UPDATE = 'task_update'; // 任务更新 const TASK_TIME = 'task_time'; // 任务时间 const TASK_STATUS = 'task_status'; // 任务状态 const TASK_REMOVE = 'task_remove'; // 任务删除 const TASK_ARCHIVED = 'task_archived'; // 任务归档 const TASK_MOVE = 'task_move'; // 任务移动4 类主体同样以常量 + 描述映射定义:
const PERMISSIONS = [ 'project_leader' => 1, // 项目负责人 'project_member' => 2, // 项目成员 'task_leader' => 3, // 任务负责人 'task_assist' => 4, // 任务协助人 ];因此存储的 JSON 形如:{"task_add": [1, 2], "task_update": [1, 3, 4], ...}——每个权限点映射到一个「允许主体编码」数组。
三、4 类权限主体:判定依据是什么
| 主体编码 | 主体 | 源码中的判定依据 |
|---|---|---|
1project_leader | 项目拥有者 / 管理员 | ProjectController校验前$project->owner非空,即当前用户在该项目的project_users.owner为 1(负责人)或 2(管理员)。可参考 Project::isOwner() 的注释:owner=1为负责人,owner=2为管理员 |
2project_member | 项目内任意普通成员 | ProjectUser::whereProjectId(...)->whereUserid(...)->first()存在即通过 |
3task_leader | 该任务的负责人(project_task_users.owner=1) | ProjectTask::isOwner() |
4task_assist | 该任务的协作者(project_task_users.owner=0) | ProjectTask::isAssister() |
其中两个任务级判定值得展开:
- isOwner():任务自身带
owner标记时直接通过;若任务有parent_id,还会回查主任务——主任务的负责人也算子任务上的任务负责人,这是处理父子任务委托的常见做法; - isAssister():先沿
parent_id循环上溯到主任务,再查project_task_users中是否存在owner=0的记录,即协助关系认定在主任务上。
四、11 个权限点与它们的调用入口
| 权限点 | 含义 |
|---|---|
TASK_LIST_ADD | 加列(看板列) |
TASK_LIST_UPDATE | 改列名 / 颜色 |
TASK_LIST_REMOVE | 删列 |
TASK_LIST_SORT | 列排序 |
TASK_ADD | 加任务 |
TASK_UPDATE | 改任务字段 |
TASK_TIME | 改计划时间 |
TASK_STATUS | 改工作流 / 完成 |
TASK_REMOVE | 删任务 |
TASK_ARCHIVED | 归档任务 |
TASK_MOVE | 跨列 / 跨项目移动 |
这些权限点不是摆设,而是被真实挂进业务入口的。从 ProjectController 的源码结构看,调用分布大致如下:
- 列管理:加列、改列、删列、列排序分别校验
TASK_LIST_ADD/TASK_LIST_UPDATE/TASK_LIST_REMOVE/TASK_LIST_SORT(见ProjectController.php#L1051、#L1104、#L1146、#L828); - 任务增改:创建任务校验
TASK_ADD(#L2729、#L2829),修改任务字段校验TASK_UPDATE(#L2450); - 任务生命周期:删除校验
TASK_REMOVE(#L2583、#L3213),归档校验TASK_ARCHIVED(#L3171),状态流转走动态权限键(#L3027); - 移动:跨列移动与跨项目移动都校验
TASK_MOVE,且跨项目移动会双向校验——源项目要求TASK_MOVE、目标项目要求TASK_ADD(#L3511、#L3514); - 除 Web API 外,任务交接服务 ProjectTaskHandoffService 在交接前也会校验
TASK_UPDATE。
两条对外 API 记录在 routes/api-map.md 中:api/project/permission(获取权限设置)与api/project/permission/update(更新权限设置)。
五、默认权限矩阵:新建项目时是什么
文档给出的默认值描述是:新建项目时,project_member对 TASK_ADD / TASK_UPDATE / TASK_STATUS / TASK_TIME / TASK_ARCHIVED / TASK_MOVE / TASK_LIST_SORT 全开,task_leader/task_assist全开,列增删改仅拥有者 / 管理员。
而当前仓库源码中 initPermissions() 的实际默认矩阵是:
| 权限点 | 默认允许的主体 |
|---|---|
task_list_add/task_list_update/task_list_sort/task_add | 项目负责人(1) + 项目成员(2) |
task_list_remove | 仅项目负责人(1) |
task_update/task_time | 项目负责人(1) + 任务负责人(3) + 任务协助人(4) |
task_status/task_remove/task_archived/task_move | 项目负责人(1) + 任务负责人(3) |
可以看到,默认值的核心设计意图是一致的:列管理偏向项目管理者,任务内容编辑向「任务自己人」(负责人/协助人)开放,状态类操作更收敛。但注意两点差异:其一,project_member的默认开放范围在源码中仅覆盖列排序等 4 个权限点,并未对任务字段修改、状态流转全开;其二,task_assist默认只有task_update/task_time两项。知识库文档标注last_verified: v1.7.90,若你运行的版本默认值与上述不同,请以initPermissions()的当前源码为准——这是唯一会写入新项目的默认矩阵。
六、校验流程:userTaskPermission 如何逐主体判定
核心方法是 userTaskPermission(),签名是(Project $project, $action, ProjectTask $task = null)。它对 11 个权限点走同一条 switch 分支,判定顺序如下:
- 取该 action 当前允许的主体数组:
getPermission($project->id, $action)先调initPermissions()(不存在则按默认矩阵落库),再取对应 key;若 key 不存在直接抛ApiException('项目权限设置不存在'); - 项目负责人:主体数组含
1且$project->owner非空(当前用户是 owner=1 或 2)→ 通过; - 项目成员:主体数组含
2且project_users中查得到该用户 → 通过; - 任务负责人:主体数组含
3且传入了$task且$task->isOwner()→ 通过; - 任务协助人:主体数组含
4且$task->isAssister()→ 通过; - 全部不满足:把允许主体翻译成描述后倒序拼接,抛
ApiException("仅限项目负责人、任务负责人操作")这类带多语言描述的拒绝信息。
这里有一个容易误解的点:拥有者「不被矩阵限制」并不是硬编码的特权,而是矩阵默认值保证了这一点——即使project_leader主体被从某个权限点中移除,拥有者通常仍是项目成员(project_users中存在 owner=1 的记录),仍会走第 3 步project_member分支通过(前提是该主体仍在允许列表中)。从源码结构看,userTaskPermission本身并没有对 owner=1/2 做绕过。
另外注意主体判定的短路特性:四个主体按 leader → member → task_leader → task_assist 顺序检查,任一命中即返回,project_leader排在最前,保证管理角色优先。
七、在哪配:配置入口与 API
配置面板
- 桌面端:项目设置 →「权限规则」面板,用矩阵勾选的方式编辑:每个权限点 × 每类主体;
- 操作者需为项目拥有者(
owner=1)或管理员(owner=2)。
读取与更新 API
- 获取:
api/project/permission(参数project_id),实现见 ProjectController——先校验当前用户在该项目中,然后initPermissions()返回完整矩阵(不存在则先按默认值落库),因此该接口天然幂等。 - 更新:
api/project/permission/update(project_id+ 各权限点数组),控制器实现:
$permissions = Request::only([ ProjectPermission::TASK_LIST_ADD, ProjectPermission::TASK_LIST_UPDATE, ProjectPermission::TASK_LIST_REMOVE, ProjectPermission::TASK_LIST_SORT, ProjectPermission::TASK_ADD, ProjectPermission::TASK_UPDATE, ProjectPermission::TASK_TIME, ProjectPermission::TASK_STATUS, ProjectPermission::TASK_REMOVE, ProjectPermission::TASK_ARCHIVED, ProjectPermission::TASK_MOVE, ]); $projectPermission = ProjectPermission::updatePermissions($projectId, Base::newArrayRecursive('intval', $permissions));参数说明:每个权限点 key 传允许主体编码的数组,如task_add[]=1&task_add[]=2(允许项目负责人和项目成员);Request::only只放行 11 个合法 key,intval递归清洗保证值只能是主体编码。
updatePermissions() 的合并语义值得注意:
$mergedPermissions = empty($newPermissions) ? $currentPermissions : array_merge($currentPermissions, $newPermissions);即请求中没有出现的权限点保持原值不变,只有传了的才覆盖——部分更新是安全的,前端只需提交用户改动的格子。从当前仓库源码看,该接口入口只校验调用者是项目成员,文档中标注的「仅限拥有者 / 管理员配置」这一前置更可能由前端权限面板控制,调用 API 时请以你的部署版本实际行为为准。
八、设计取舍与不支持的场景
这套矩阵是典型的「粗粒度、易理解」设计,其明确不做的事与文档的 negative 清单一致:
- 不能按列、按标签做细粒度限制:权限点是项目级的,
userTaskPermission的判定参数里没有任何列(ProjectColumn)或标签(ProjectTaskTag)维度; - 不能限制拥有者 / 管理员:默认矩阵中
project_leader主体出现在全部 11 个权限点,且拥有者同时是项目成员,双重兜底; - 不支持「按部门角色」自动套用权限:主体只有 4 类静态编码,没有与
UserDepartment联动的角色映射; - 矩阵只约束任务操作:项目设置、成员管理等入口不走
userTaskPermission。
对集成方而言,这意味着如果你想做「某列只读」之类的需求,需要在上层业务逻辑自行叠加判断,而不是指望project_permissions表。
参考文件
- 权限模型与校验核心:app/Models/ProjectPermission.php
- 表结构迁移:database/migrations/2023_11_20_214409_create_project_permissions_table.php
- 任务级判定(isOwner / isAssister):app/Models/ProjectTask.php
- 项目 owner 语义(owner=1/2):app/Models/ProjectUser.php、app/Models/Project.php
- 鉴权调用点与配置 API:app/Http/Controllers/Api/ProjectController.php
- API 清单:routes/api-map.md
- 后端
- 前端
- 协同办公
- 即时通讯
- 企业应用
【免费下载链接】dootask
DooTask是一款开源在线项目任务管理工具,提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM,文件管理等工具;同时消息功能使用非对称加密技术让你的沟通更安全。
相关推荐
Phoenix GraphQL API 权限矩阵:Admin 与 Member 的角色权限全解析
Phoenix GraphQL API 权限矩阵:Admin 与 Member 的角色权限全解析 这篇技术指南聚焦 Phoenix(AI Observabili
可观测性AI 评测LLMOpsAI 应用人工智能RuoYi-Vue-fast数据权限设计:行级权限与列级权限实现
RuoYi Vue fast数据权限设计:行级权限与列级权限实现 在企业级应用开发中,数据权限控制是保障数据安全的核心环节。RuoYi Vue fast作为一款
h2ogpt权限管理系统:设计与实现权限框架
h2ogpt权限管理系统:设计与实现权限框架 在当今数字化时代,数据隐私和安全问题日益凸显。使用托管的大型语言模型(LLMs)需要将数据发送到外部服务器,这可能
AI 应用大模型RAGNLP后端语音计算机视觉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考