feat(dialog): 系统设置支持禁止其他人员设置/取消聊天待办

新增系统级开关 todo_set_permission(open=允许默认 / close=禁止)。
开关为禁止时,仅本人、群主/群管理员、项目负责人/项目管理员、任务负责人
可设置或取消聊天消息待办,其他人由后端拦截;默认允许,保持现有行为。

- SystemController::setting 接入开关读写(白名单 + 默认 open)
- WebSocketDialog::checkTodoOwnerPermission 角色判断(复用 isOwner 等)
- WebSocketDialogMsg::toggleTodoMsg 内权限闸门:close 且影响到他人且
  非放行角色时 retError;仅影响自己始终放行;open 时行为零变化
- SystemSetting.vue「消息相关」新增「待办设置权限」开关 UI
- 国际化文案(original-api.txt / original-web.txt)
- TodoSetPermissionTest 覆盖角色判断、闸门决策及真实拦截路径(8 用例)

任务 #124。系统后台 admin 不特殊放行;「完成待办」不在本次范围。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
kuaifan
2026-05-31 15:12:46 +00:00
parent e57736bcc1
commit aa2e0acaba
7 changed files with 300 additions and 0 deletions

View File

@@ -710,6 +710,42 @@ class WebSocketDialog extends AbstractModel
return $this->isPrimaryOwner($userid) || $this->isDeputyOwner($userid);
}
/**
* 是否有权限设置/取消本会话内「他人」的待办
* 放行:群主/群管理员、关联项目负责人/项目管理员、关联任务负责人(及任务所属项目负责人/管理员)
*
* @param int $userid
* @return bool
*/
public function checkTodoOwnerPermission($userid): bool
{
$userid = intval($userid);
if ($userid <= 0) {
return false;
}
// 群主 / 群管理员
if ($this->isOwner($userid)) {
return true;
}
// 关联项目(项目群)负责人 / 项目管理员
$project = Project::whereDialogId($this->id)->first();
if ($project && $project->isOwner($userid)) {
return true;
}
// 关联任务(任务群)负责人,及任务所属项目负责人 / 管理员
$task = ProjectTask::whereDialogId($this->id)->first();
if ($task) {
if (ProjectTaskUser::whereTaskId($task->id)->whereUserid($userid)->whereOwner(1)->exists()) {
return true;
}
$taskProject = Project::find($task->project_id);
if ($taskProject && $taskProject->isOwner($userid)) {
return true;
}
}
return false;
}
/**
* 群管理员 userid 列表
*