diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 1260120ca..a111faabc 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -2897,7 +2897,7 @@ class DialogController extends AbstractController } // $dialog = WebSocketDialog::checkDialog($dialog_id); - // 有群主(主或副)时,仅群主/副群主可邀请;无群主时,任意成员可邀请 + // 有群主时,仅群主/群管理员可邀请;无群主时,任意成员可邀请 if ($dialog->owner_id > 0 && !$dialog->isOwner($user->userid)) { throw new \App\Exceptions\ApiException('仅限群主或群管理员操作'); } @@ -3013,7 +3013,7 @@ class DialogController extends AbstractController } /** - * 任命副群主(仅主群主可操作) + * 任命群管理员(仅群主可操作) * * @apiParam {Number} dialog_id 群对话ID * @apiParam {Number} userid 要任命的群成员 userid @@ -3028,7 +3028,7 @@ class DialogController extends AbstractController return Base::retError('请选择有效的成员'); } - $dialog = WebSocketDialog::checkDialog($dialog_id, true); // checkOwner=true:仅主群主 + $dialog = WebSocketDialog::checkDialog($dialog_id, true); // checkOwner=true:仅群主 $dialog->checkGroup('user'); // 仅普通群 $member = WebSocketDialogUser::where('dialog_id', $dialog->id) @@ -3054,10 +3054,10 @@ class DialogController extends AbstractController } /** - * 罢免副群主(仅主群主可操作) + * 罢免群管理员(仅群主可操作) * * @apiParam {Number} dialog_id 群对话ID - * @apiParam {Number} userid 要罢免的副群主 userid + * @apiParam {Number} userid 要罢免的群管理员 userid */ public function group__deldeputy() { diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index 5424cf018..1da4ddbe3 100755 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -381,7 +381,7 @@ class ProjectController extends AbstractController * * @apiParam {Number} project_id 项目ID * @apiParam {Number[]} userid 成员userid数组(最终完整列表) - * @apiParam {Number[]} [deputy_userid] 副负责人userid数组(可选,仅主负责人有效;必须是 userid 子集) + * @apiParam {Number[]} [deputy_userid] 项目管理员userid数组(可选,仅负责人有效;必须是 userid 子集) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) @@ -408,7 +408,7 @@ class ProjectController extends AbstractController // $project = Project::userProject($project_id, true, true); // - // 仅主负责人可设置副负责人;副负责人/其他角色提交 deputy_userid 一律忽略 + // 仅负责人可设置项目管理员;项目管理员/其他角色提交 deputy_userid 一律忽略 $isPrimary = (int)$project->owner === ProjectUser::OWNER_PRIMARY; $applyDeputy = $isPrimary && $deputy_userid !== null; // @@ -434,7 +434,7 @@ class ProjectController extends AbstractController $row->exitProject(); } // - // 副负责人 diff(仅主负责人有效) + // 项目管理员 diff(仅负责人有效) if ($applyDeputy) { $currentDeputies = ProjectUser::whereProjectId($project->id) ->where('owner', ProjectUser::OWNER_DEPUTY) @@ -624,11 +624,11 @@ class ProjectController extends AbstractController } // AbstractModel::transaction(function() use ($owner_userid, $project) { - // 仅清除原主 owner=1(副 owner=2 保留) + // 仅清除原负责人 owner=1(项目管理员 owner=2 保留) ProjectUser::whereProjectId($project->id) ->whereOwner(ProjectUser::OWNER_PRIMARY) ->change(['owner' => 0]); - // 设新主 owner=1(如新主原本是副,从 2 升为 1) + // 设新负责人 owner=1(如新负责人原本是项目管理员,从 2 升为 1) ProjectUser::updateInsert([ 'project_id' => $project->id, 'userid' => $owner_userid, @@ -648,7 +648,7 @@ class ProjectController extends AbstractController $project->addLog("移交项目给", ['userid' => $owner_userid]); }); // - // pushMsg 带 deputy_userids,前端可直接更新副列表无需重拉 + // pushMsg 带 deputy_userids,前端可直接更新项目管理员列表无需重拉 $project->pushMsg('detail', [ 'owner_userid' => $project->fresh()->owner_userid, 'deputy_userids' => $project->fresh()->deputy_userids, @@ -657,7 +657,7 @@ class ProjectController extends AbstractController } /** - * @api {post} api/project/adddeputy 任命副负责人(仅主负责人可操作) + * @api {post} api/project/adddeputy 任命项目管理员(仅负责人可操作) * * @apiDescription 需要token身份 * @apiVersion 1.0.0 @@ -707,7 +707,7 @@ class ProjectController extends AbstractController } /** - * @api {post} api/project/deldeputy 罢免副负责人(仅主负责人可操作) + * @api {post} api/project/deldeputy 罢免项目管理员(仅负责人可操作) * * @apiDescription 需要token身份 * @apiVersion 1.0.0 @@ -715,7 +715,7 @@ class ProjectController extends AbstractController * @apiName deldeputy * * @apiParam {Number} project_id 项目ID - * @apiParam {Number} userid 要罢免的副负责人 userid + * @apiParam {Number} userid 要罢免的项目管理员 userid */ public function deldeputy() { @@ -1994,7 +1994,7 @@ class ProjectController extends AbstractController // 项目可见性 $projectOwnerids = ProjectUser::whereProjectId($task->project_id) ->whereIn('owner', [ProjectUser::OWNER_PRIMARY, ProjectUser::OWNER_DEPUTY]) - ->pluck('userid')->map(fn($v) => (int)$v)->toArray(); // 项目负责人(主+副) + ->pluck('userid')->map(fn($v) => (int)$v)->toArray(); // 项目负责人(含项目管理员) if ($task->visibility != 1 && !in_array($user->userid, $projectOwnerids)) { $taskUserids = ProjectTaskUser::whereTaskId($task_id)->pluck('userid')->toArray(); //任务负责人、协助人 $subTaskUserids = ProjectTaskUser::whereTaskPid($task_id)->pluck('userid')->toArray(); //子任务负责人、协助人 @@ -2504,7 +2504,7 @@ class ProjectController extends AbstractController } else { $projectOwner = ProjectUser::whereProjectId($task->project_id) ->whereIn('owner', [ProjectUser::OWNER_PRIMARY, ProjectUser::OWNER_DEPUTY]) - ->pluck('userid')->toArray(); // 项目负责人(主+副) + ->pluck('userid')->toArray(); // 项目负责人(含项目管理员) $taskOwnerAndAssists = ProjectTaskUser::select(['userid', 'owner'])->whereTaskId($data['id'])->pluck('userid')->toArray(); $visibleIds = array_merge($projectOwner, $taskOwnerAndAssists); $data['is_visible'] = in_array($user->userid, $visibleIds) ? 1 : 0; diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index ede4bd66a..c7aaed184 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -2146,7 +2146,7 @@ class UsersController extends AbstractController } /** - * @api {post} api/users/department/adddeputy 任命副负责人(限管理员) + * @api {post} api/users/department/adddeputy 任命部门管理员(限管理员) * * @apiDescription 需要token身份 * @apiVersion 1.0.0 @@ -2154,7 +2154,7 @@ class UsersController extends AbstractController * @apiName department__adddeputy * * @apiParam {Number} id 部门 id - * @apiParam {Number} userid 副负责人 userid + * @apiParam {Number} userid 部门管理员 userid * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) @@ -2178,7 +2178,7 @@ class UsersController extends AbstractController } /** - * @api {post} api/users/department/deldeputy 罢免副负责人(限管理员) + * @api {post} api/users/department/deldeputy 罢免部门管理员(限管理员) * * @apiDescription 需要token身份 * @apiVersion 1.0.0 @@ -2186,7 +2186,7 @@ class UsersController extends AbstractController * @apiName department__deldeputy * * @apiParam {Number} id 部门 id - * @apiParam {Number} userid 要罢免的副负责人 userid + * @apiParam {Number} userid 要罢免的部门管理员 userid */ public function department__deldeputy() { diff --git a/app/Models/Project.php b/app/Models/Project.php index c983801ab..bf4b891c9 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -94,7 +94,7 @@ class Project extends AbstractModel } /** - * 副负责人 userid 列表 + * 项目管理员 userid 列表 * @return array */ public function getDeputyUseridsAttribute(): array @@ -110,7 +110,7 @@ class Project extends AbstractModel } /** - * 是否主负责人(与 project_users.owner=1 一致) + * 是否项目负责人(与 project_users.owner=1 一致) */ public function isPrimaryOwner($userid): bool { @@ -124,7 +124,7 @@ class Project extends AbstractModel } /** - * 是否副负责人(与 project_users.owner=2 一致) + * 是否项目管理员(与 project_users.owner=2 一致) */ public function isDeputyOwner($userid): bool { @@ -138,7 +138,7 @@ class Project extends AbstractModel } /** - * 是否负责人(主或副) + * 是否负责人(含项目管理员) */ public function isOwner($userid): bool { @@ -693,8 +693,8 @@ class Project extends AbstractModel * 获取项目信息(用于判断会员是否存在项目内) * @param int $project_id * @param null|bool $archived true:仅限未归档, false:仅限已归档, null:不限制 - * @param null|bool|string $mustOwner true:主或副都可(主+副共享操作); - * 'primary':仅主(转让/删除/任命副等主独占操作); + * @param null|bool|string $mustOwner true:负责人或项目管理员都可(共享操作); + * 'primary':仅负责人(转让/删除/任命项目管理员等独占操作); * false:仅限非负责人;null:不限制 * @return self */ diff --git a/app/Models/ProjectTask.php b/app/Models/ProjectTask.php index 3fe5d7634..3c99a9552 100644 --- a/app/Models/ProjectTask.php +++ b/app/Models/ProjectTask.php @@ -1993,7 +1993,7 @@ class ProjectTask extends AbstractModel // $projectOwnerids = ProjectUser::whereProjectId($this->project_id) ->whereIn('owner', [ProjectUser::OWNER_PRIMARY, ProjectUser::OWNER_DEPUTY]) - ->pluck('userid')->toArray(); // 项目负责人(主+副) + ->pluck('userid')->toArray(); // 项目负责人(含项目管理员) // $array = []; if (empty($userids)) { diff --git a/app/Models/ProjectUser.php b/app/Models/ProjectUser.php index 9cb830be4..b69bb5795 100644 --- a/app/Models/ProjectUser.php +++ b/app/Models/ProjectUser.php @@ -39,13 +39,13 @@ class ProjectUser extends AbstractModel { /** @var int 普通成员编码 */ const OWNER_MEMBER = 0; - /** @var int 主负责人编码 */ + /** @var int 项目负责人编码 */ const OWNER_PRIMARY = 1; - /** @var int 副负责人编码 */ + /** @var int 项目管理员编码 */ const OWNER_DEPUTY = 2; /** - * 是否主负责人(owner=1) + * 是否项目负责人(owner=1) */ public function isPrimaryOwner(): bool { @@ -53,7 +53,7 @@ class ProjectUser extends AbstractModel } /** - * 是否副负责人(owner=2) + * 是否项目管理员(owner=2) */ public function isDeputyOwner(): bool { @@ -61,7 +61,7 @@ class ProjectUser extends AbstractModel } /** - * 是否负责人(主或副) + * 是否负责人(含项目管理员) */ public function isOwner(): bool { @@ -91,8 +91,8 @@ class ProjectUser extends AbstractModel foreach ($list as $item) { $row = self::whereProjectId($item->project_id)->whereUserid($newUserid)->first(); if ($row) { - // 已存在:仅当离职用户是主(owner=1)时把接收人升为主; - // 离职用户是副(owner=2)时不传副给接收人(spec:副不替补) + // 已存在:仅当离职用户是项目负责人(owner=1)时把接收人升为项目负责人; + // 离职用户是项目管理员(owner=2)时不传项目管理员身份给接收人(spec:项目管理员不替补) if ((int)$item->owner === self::OWNER_PRIMARY) { $row->owner = self::OWNER_PRIMARY; } @@ -100,7 +100,7 @@ class ProjectUser extends AbstractModel $row->save(); $item->delete(); } else { - // 不存在:转移时如果离职用户是副,降级为普通成员(不带副身份过户给接收人) + // 不存在:转移时如果离职用户是项目管理员,降级为普通成员(不带项目管理员身份过户给接收人) if ((int)$item->owner === self::OWNER_DEPUTY) { $item->owner = self::OWNER_MEMBER; } diff --git a/app/Models/UserDepartment.php b/app/Models/UserDepartment.php index 40c2445e8..be1296248 100644 --- a/app/Models/UserDepartment.php +++ b/app/Models/UserDepartment.php @@ -55,7 +55,7 @@ class UserDepartment extends AbstractModel } /** - * 副负责人 userid 列表 + * 部门管理员 userid 列表 * @return array */ public function getDeputyUseridsAttribute(): array @@ -71,7 +71,7 @@ class UserDepartment extends AbstractModel } /** - * 是否主负责人(与 owner_userid 一致) + * 是否部门负责人(与 owner_userid 一致) */ public function isPrimaryOwner($userid): bool { @@ -82,7 +82,7 @@ class UserDepartment extends AbstractModel } /** - * 是否副负责人(在 user_department_owners 表里) + * 是否部门管理员(在 user_department_owners 表里) */ public function isDeputyOwner($userid): bool { @@ -96,7 +96,7 @@ class UserDepartment extends AbstractModel } /** - * 是否负责人(主或副) + * 是否负责人(含部门管理员) */ public function isOwner($userid): bool { @@ -127,7 +127,7 @@ class UserDepartment extends AbstractModel $dialog->owner_id = $this->owner_userid; if ($dialog->save()) { $dialog->joinGroup($this->owner_userid, 0, true); - // 同步 role:原主 role=0、新主 role=1(副 role=2 保留不动) + // 同步 role:原负责人 role=0、新负责人 role=1(部门管理员 role=2 保留不动) if ($oldOwnerId > 0 && $oldOwnerId !== (int)$this->owner_userid) { WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $oldOwnerId) @@ -156,9 +156,9 @@ class UserDepartment extends AbstractModel $dialog->group_type = 'department'; if ($dialog->save()) { $dialog->joinGroup($this->owner_userid, 0, true); - // 同步 role:原主 role=0、新主 role=1、原副 role=0 - // 原副清零:避免 dialog_users.role=2 与 user_department_owners 不一致 - // (副关系不带过来,须通过 addDeputy 显式重新任命) + // 同步 role:原负责人 role=0、新负责人 role=1、原部门管理员 role=0 + // 原部门管理员清零:避免 dialog_users.role=2 与 user_department_owners 不一致 + // (部门管理员关系不带过来,须通过 addDeputy 显式重新任命) if ($oldOwnerId > 0 && $oldOwnerId !== (int)$this->owner_userid) { WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $oldOwnerId) @@ -214,10 +214,10 @@ class UserDepartment extends AbstractModel } /** - * 任命副负责人 - * - 副自动加入 users.department(成为部门成员,与主对齐) - * - 副自动加入部门群 + 设 role=2 - * - 幂等(已是副不报错) + * 任命部门管理员 + * - 部门管理员自动加入 users.department(成为部门成员,与负责人对齐) + * - 部门管理员自动加入部门群 + 设 role=2 + * - 幂等(已是部门管理员不报错) * * @param int $userid * @return void @@ -237,13 +237,13 @@ class UserDepartment extends AbstractModel } AbstractModel::transaction(function () use ($userid, $user) { - // 写副表(unique key 自动幂等) + // 写部门管理员表(unique key 自动幂等) \DB::table('user_department_owners')->insertOrIgnore([ 'department_id' => $this->id, 'userid' => $userid, ]); - // 加入 users.department(成为部门成员,与主对齐) + // 加入 users.department(成为部门成员,与负责人对齐) $userDeptIds = $user->department; // accessor 返回数组 if (!in_array($this->id, $userDeptIds)) { $userDeptIds = array_merge($userDeptIds, [$this->id]); @@ -251,7 +251,7 @@ class UserDepartment extends AbstractModel $user->save(); } - // 加副入部门群 + 设 role=2 + // 加部门管理员入部门群 + 设 role=2 if ($this->dialog_id > 0) { $dialog = WebSocketDialog::find($this->dialog_id); if ($dialog) { @@ -270,9 +270,9 @@ class UserDepartment extends AbstractModel } /** - * 罢免副负责人 - * - 删副表记录 - * - 从 users.department 移除该部门 ID(与主"离开部门"对齐) + * 罢免部门管理员 + * - 删部门管理员表记录 + * - 从 users.department 移除该部门 ID(与负责人"离开部门"对齐) * - 退出部门群(成员关系=群关系一致) * - 幂等 * @@ -341,7 +341,7 @@ class UserDepartment extends AbstractModel // 解散群组 $dialog = WebSocketDialog::find($this->dialog_id); $dialog?->deleteDialog(); - // 清理副负责人记录(防悬挂) + // 清理部门管理员记录(防悬挂) \DB::table('user_department_owners')->where('department_id', $this->id)->delete(); // $this->delete(); @@ -355,7 +355,7 @@ class UserDepartment extends AbstractModel */ public static function transfer($originalUserid, $newUserid) { - // 主转让(保持现有逻辑) + // 部门负责人转让(保持现有逻辑) self::whereOwnerUserid($originalUserid)->chunkById(100, function ($list) use ($originalUserid, $newUserid) { /** @var self $item */ foreach ($list as $item) { @@ -364,7 +364,7 @@ class UserDepartment extends AbstractModel ]); } }); - // 副离职清理(新增):直接删除离职用户的所有副记录 + // 部门管理员离职清理(新增):直接删除离职用户的所有部门管理员记录 // 不需要清群 role —— UserTransfer::exitDialog 会把人踢出所有群,role 随成员关系一起消失 \DB::table('user_department_owners') ->where('userid', $originalUserid) diff --git a/app/Models/WebSocketDialog.php b/app/Models/WebSocketDialog.php index 3f1b0e20c..b9ab7911a 100644 --- a/app/Models/WebSocketDialog.php +++ b/app/Models/WebSocketDialog.php @@ -520,7 +520,7 @@ class WebSocketDialog extends AbstractModel foreach ($list as $item) { if ($checkDelete) { if ($type === 'remove') { - // 移出时:如果是全员群仅允许管理员操作,其他群主/副群主/邀请人可以操作 + // 移出时:如果是全员群仅允许管理员操作,其他群主/群管理员/邀请人可以操作 if ($this->group_type === 'all') { User::auth("admin"); } else { @@ -529,12 +529,12 @@ class WebSocketDialog extends AbstractModel if ($actor <= 0) { throw new ApiException('只有群主或邀请人可以移出成员'); } - // 主群主、副群主、邀请人可移出 + // 群主、群管理员、邀请人可移出 $allowedActor = $this->isOwner($actor) || $actor === (int)$item->inviter; if (!$allowedActor) { throw new ApiException('只有群主或邀请人可以移出成员'); } - // 副群主不能移出主群主或其他副群主 + // 群管理员不能移出群主或其他群管理员 if ($this->isDeputyOwner($actor)) { $targetIsOwner = $this->isPrimaryOwner($item->userid) || $this->isDeputyOwner($item->userid); if ($targetIsOwner) { @@ -659,7 +659,7 @@ class WebSocketDialog extends AbstractModel } /** - * 是否主群主(与 owner_id 一致) + * 是否群主(与 owner_id 一致) */ public function isPrimaryOwner($userid): bool { @@ -667,7 +667,7 @@ class WebSocketDialog extends AbstractModel } /** - * 是否副群主(仅 web_socket_dialog_users.role=2) + * 是否群管理员(仅 web_socket_dialog_users.role=2) */ public function isDeputyOwner($userid): bool { @@ -681,7 +681,7 @@ class WebSocketDialog extends AbstractModel } /** - * 是否群主(主或副) + * 是否群主(含群管理员) */ public function isOwner($userid): bool { @@ -689,7 +689,7 @@ class WebSocketDialog extends AbstractModel } /** - * 副群主 userid 列表 + * 群管理员 userid 列表 * * @return array */ diff --git a/database/migrations/2026_04_30_000001_add_role_to_web_socket_dialog_users.php b/database/migrations/2026_04_30_000001_add_role_to_web_socket_dialog_users.php index ce0d91c44..877775e21 100644 --- a/database/migrations/2026_04_30_000001_add_role_to_web_socket_dialog_users.php +++ b/database/migrations/2026_04_30_000001_add_role_to_web_socket_dialog_users.php @@ -16,7 +16,7 @@ class AddRoleToWebSocketDialogUsers extends Migration Schema::table('web_socket_dialog_users', function (Blueprint $table) { if (!Schema::hasColumn('web_socket_dialog_users', 'role')) { $table->tinyInteger('role')->default(0)->after('userid') - ->comment('0=普通成员 1=主群主 2=副群主'); + ->comment('0=普通成员 1=群主 2=群管理员'); $table->index(['dialog_id', 'role'], 'idx_dialog_role'); } }); diff --git a/database/migrations/2026_05_01_000001_create_user_department_owners_table.php b/database/migrations/2026_05_01_000001_create_user_department_owners_table.php index b3bbfc3eb..9d6ff5c6e 100644 --- a/database/migrations/2026_05_01_000001_create_user_department_owners_table.php +++ b/database/migrations/2026_05_01_000001_create_user_department_owners_table.php @@ -17,7 +17,7 @@ class CreateUserDepartmentOwnersTable extends Migration Schema::create('user_department_owners', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('department_id')->comment('部门ID'); - $table->unsignedBigInteger('userid')->comment('副负责人 userid'); + $table->unsignedBigInteger('userid')->comment('部门管理员 userid'); $table->timestamp('created_at')->useCurrent(); $table->unique(['department_id', 'userid'], 'uniq_dept_user'); $table->index('userid', 'idx_userid'); diff --git a/resources/assets/js/pages/manage/components/ProjectPanel.vue b/resources/assets/js/pages/manage/components/ProjectPanel.vue index 0eef480a8..e11e4d200 100644 --- a/resources/assets/js/pages/manage/components/ProjectPanel.vue +++ b/resources/assets/js/pages/manage/components/ProjectPanel.vue @@ -774,7 +774,7 @@ export default { }, deputyWaitDemote() { - // 所有从副负责人列表中移出的人(即使同时被踢出项目,也在罢免段显示,避免操作隐身) + // 所有从项目管理员列表中移出的人(即使同时被踢出项目,也在罢免段显示,避免操作隐身) const {deputy_userids = [], deputy_useridbak = []} = this.userData; return deputy_useridbak.filter(id => !deputy_userids.includes(id)); }, @@ -822,7 +822,7 @@ export default { }, memberRowUncancelable() { - // 项目成员行:主+当前副选择(响应式)都不可移除 + // 项目成员行:负责人 + 当前项目管理员选择(响应式)都不可移除 if (!this.projectData) return []; const deputies = (this.userData && Array.isArray(this.userData.deputy_userids)) ? this.userData.deputy_userids @@ -834,13 +834,13 @@ export default { }, deputyRowUncancelable() { - // 副负责人行:防御性锁定主(理论上主不会出现在该 v-model 里) + // 项目管理员行:防御性锁定负责人(理论上负责人不会出现在该 v-model 里) if (!this.projectData) return []; return [this.projectData.owner_userid]; }, deputyRowDisabledChoice() { - // 副负责人候选:排除主(不能任命主为副) + // 项目管理员候选:排除负责人(不能任命负责人为项目管理员) if (!this.projectData) return []; return [this.projectData.owner_userid]; }, @@ -1169,7 +1169,7 @@ export default { this.handleColumnDebounce(100); }, 'userData.deputy_userids'(newDeputies) { - // 副负责人必须是项目成员:副行新增时自动并入成员行(罢免时不联动移除) + // 项目管理员必须是项目成员:项目管理员行新增时自动并入成员行(罢免时不联动移除) if (!Array.isArray(newDeputies) || !Array.isArray(this.userData.userids)) { return; } @@ -1507,7 +1507,7 @@ export default { onUser() { this.userLoad++; - // 副负责人必须是项目成员:把 deputy 并入 userid 列表(前端归一化) + // 项目管理员必须是项目成员:把 deputy 并入 userid 列表(前端归一化) const baseUserids = (this.userData.userids || []).slice(); const deputyUserids = (this.userData.deputy_userids || []).slice(); const mergedUserids = Array.from(new Set([...baseUserids, ...deputyUserids])); @@ -1516,7 +1516,7 @@ export default { project_id: this.projectId, userid: mergedUserids, }; - // 仅主负责人发送 deputy_userid;副负责人/其他角色不发送(后端也会忽略) + // 仅项目负责人发送 deputy_userid;项目管理员/其他角色不发送(后端也会忽略) if (this.canManageDeputy) { payload.deputy_userid = deputyUserids; } diff --git a/resources/assets/js/pages/manage/components/TeamManagement.vue b/resources/assets/js/pages/manage/components/TeamManagement.vue index 66956eabb..3282af2a4 100644 --- a/resources/assets/js/pages/manage/components/TeamManagement.vue +++ b/resources/assets/js/pages/manage/components/TeamManagement.vue @@ -833,7 +833,7 @@ export default { }, deputyDisabledChoice() { - // 主负责人不能同时是副;已是副的不需要再选 + // 部门负责人不能同时是部门管理员;已是部门管理员的不需要再选 return [ ...(this.departmentData.owner_userid || []), ]; @@ -1088,8 +1088,8 @@ export default { }); $A.messageSuccess(res.msg); - // 副列表同步(编辑/新建都支持) - // 编辑场景:从 departmentList 取旧副;新建场景:从刚返回的列表反查刚创建的部门 + // 部门管理员列表同步(编辑/新建都支持) + // 编辑场景:从 departmentList 取旧部门管理员;新建场景:从刚返回的列表反查刚创建的部门 let targetId = this.departmentData.id; let oldDeputies = []; if (targetId > 0) { diff --git a/tests/Feature/MultiOwnerDepartmentTest.php b/tests/Feature/MultiOwnerDepartmentTest.php index 876f51f16..f5b0f9582 100644 --- a/tests/Feature/MultiOwnerDepartmentTest.php +++ b/tests/Feature/MultiOwnerDepartmentTest.php @@ -122,7 +122,7 @@ class MultiOwnerDepartmentTest extends TestCase $member = $this->makeUser('d3_m@test.local'); $dept = $this->makeDepartment($owner->userid); - // 手动插入副记录(addDeputy 在 Task 5 才实现) + // 手动插入部门管理员记录(addDeputy 在 Task 5 才实现) DB::table('user_department_owners')->insert([ 'department_id' => $dept->id, 'userid' => $deputy->userid, @@ -184,7 +184,7 @@ class MultiOwnerDepartmentTest extends TestCase $deputy = $this->makeUser('d4b_dep@test.local'); $dept = $this->makeDepartment($oldOwner->userid); - // 加 deputy 入群 + 副记录 + role=2(pushMsg=false 跳过 Swoole) + // 加 deputy 入群 + 部门管理员记录 + role=2(pushMsg=false 跳过 Swoole) $dialog = WebSocketDialog::find($dept->dialog_id); $dialog->joinGroup($deputy->userid, 0, null, false); DB::table('user_department_owners')->insert([ @@ -204,9 +204,9 @@ class MultiOwnerDepartmentTest extends TestCase 'owner_userid' => $newOwner->userid, ]); - // 副表保留 + // 部门管理员表保留 $this->assertContains($deputy->userid, $dept->fresh()->deputy_userids); - // 副 role 保留 + // 部门管理员 role 保留 $depRole = WebSocketDialogUser::where('dialog_id', $dept->dialog_id) ->where('userid', $deputy->userid)->value('role'); $this->assertEquals(2, (int)$depRole); @@ -222,11 +222,11 @@ class MultiOwnerDepartmentTest extends TestCase $dept = $dept->fresh(); $this->assertContains($deputy->userid, $dept->deputy_userids); - // 副已入群 + // 部门管理员已入群 $exists = WebSocketDialogUser::where('dialog_id', $dept->dialog_id) ->where('userid', $deputy->userid)->exists(); $this->assertTrue($exists); - // 副 role=2 + // 部门管理员 role=2 $role = WebSocketDialogUser::where('dialog_id', $dept->dialog_id) ->where('userid', $deputy->userid)->value('role'); $this->assertEquals(2, (int)$role); @@ -272,23 +272,23 @@ class MultiOwnerDepartmentTest extends TestCase $dept = $this->makeDepartment($owner->userid); $this->simulateAddDeputy($dept, $deputy->userid); - // 任命后副应该入 users.department 并加入部门群 - $this->assertContains($dept->id, User::find($deputy->userid)->department, '任命副后应加入 users.department'); + // 任命后部门管理员应该入 users.department 并加入部门群 + $this->assertContains($dept->id, User::find($deputy->userid)->department, '任命部门管理员后应加入 users.department'); $this->assertTrue( WebSocketDialogUser::where('dialog_id', $dept->dialog_id)->where('userid', $deputy->userid)->exists(), - '任命副后应加入部门群' + '任命部门管理员后应加入部门群' ); $this->simulateDelDeputy($dept, $deputy->userid); $dept = $dept->fresh(); $this->assertNotContains($deputy->userid, $dept->deputy_userids); - // 罢免后从 users.department 移除(与主"离开部门"对齐) - $this->assertNotContains($dept->id, User::find($deputy->userid)->department, '罢免副后应从 users.department 移除'); + // 罢免后从 users.department 移除(与负责人"离开部门"对齐) + $this->assertNotContains($dept->id, User::find($deputy->userid)->department, '罢免部门管理员后应从 users.department 移除'); // 退出部门群(成员关系=群关系一致) $exists = WebSocketDialogUser::where('dialog_id', $dept->dialog_id) ->where('userid', $deputy->userid)->exists(); - $this->assertFalse($exists, '罢免副后应退出部门群(成员关系=群关系)'); + $this->assertFalse($exists, '罢免部门管理员后应退出部门群(成员关系=群关系)'); } public function test_delDeputy_idempotent_for_non_deputy() @@ -297,7 +297,7 @@ class MultiOwnerDepartmentTest extends TestCase $member = $this->makeUser('d6b_m@test.local'); $dept = $this->makeDepartment($owner->userid); - // member 不是副,调 delDeputy 不应抛错 + // member 不是部门管理员,调 delDeputy 不应抛错 $this->simulateDelDeputy($dept, $member->userid); $this->assertTrue(true); } @@ -326,15 +326,15 @@ class MultiOwnerDepartmentTest extends TestCase UserDepartment::transfer($departing->userid, $receiver->userid); - // 离职的副记录已删 + // 离职的部门管理员记录已删 $this->assertNotContains($departing->userid, $dept->fresh()->deputy_userids); - // receiver 没有继承副身份 + // receiver 没有继承部门管理员身份 $this->assertNotContains($receiver->userid, $dept->fresh()->deputy_userids); } public function test_user_transfer_inherits_departing_primary() { - // 主转让仍要把主权位传给接收人(保留现有行为) + // 部门负责人转让仍要把负责人身份传给接收人(保留现有行为) $departing = $this->makeUser('d7c_dep@test.local'); $receiver = $this->makeUser('d7c_rec@test.local'); $dept = $this->makeDepartment($departing->userid); @@ -346,7 +346,7 @@ class MultiOwnerDepartmentTest extends TestCase public function test_deleteDepartment_recursively_cleans_child_deputies() { - // 父部门 + 子部门各有副,删父部门时副记录应级联清理 + // 父部门 + 子部门各有部门管理员,删父部门时部门管理员记录应级联清理 $owner = $this->makeUser('d7d_o@test.local'); $deputyParent = $this->makeUser('d7d_dp@test.local'); $deputyChild = $this->makeUser('d7d_dc@test.local'); @@ -365,7 +365,7 @@ class MultiOwnerDepartmentTest extends TestCase $parentId = $parent->id; $childId = $child->id; - $parent->deleteDepartment(); // 递归删子部门 + 清各自副 + $parent->deleteDepartment(); // 递归删子部门 + 清各自部门管理员 $this->assertEquals(0, DB::table('user_department_owners')->where('department_id', $parentId)->count()); $this->assertEquals(0, DB::table('user_department_owners')->where('department_id', $childId)->count()); diff --git a/tests/Feature/MultiOwnerGroupTest.php b/tests/Feature/MultiOwnerGroupTest.php index 4082b4ea9..d094ebca1 100644 --- a/tests/Feature/MultiOwnerGroupTest.php +++ b/tests/Feature/MultiOwnerGroupTest.php @@ -48,7 +48,7 @@ class MultiOwnerGroupTest extends TestCase $member = $this->makeUser('m2@test.local'); $dialog = $this->makeGroup($owner->userid, [$deputy->userid, $member->userid]); - // 手工把 deputy 设为副群主 + // 手工把 deputy 设为群管理员 WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $deputy->userid) ->update(['role' => 2]); @@ -111,13 +111,13 @@ class MultiOwnerGroupTest extends TestCase ->where('userid', $deputy->userid) ->update(['role' => 2]); - // 模拟副群主退群(pushMsg=false 跳过 Swoole 推送,仅验证 DB 状态) + // 模拟群管理员退群(pushMsg=false 跳过 Swoole 推送,仅验证 DB 状态) $dialog->exitGroup($deputy->userid, 'exit', false, false); $exists = WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $deputy->userid) ->exists(); - $this->assertFalse($exists, '退群后副群主记录应被删除'); + $this->assertFalse($exists, '退群后群管理员记录应被删除'); $this->assertNotContains($deputy->userid, $dialog->fresh()->deputy_ids); } @@ -358,13 +358,13 @@ class MultiOwnerGroupTest extends TestCase return ['allowed' => false, 'error' => '目标用户不在群内']; } - // 主群主、副群主、邀请人可移出 + // 群主、群管理员、邀请人可移出 $allowedActor = $dialog->isOwner($actorId) || $actorId === (int)$item->inviter; if (!$allowedActor) { return ['allowed' => false, 'error' => '只有群主或邀请人可以移出成员']; } - // 副群主不能移出主群主或其他副群主 + // 群管理员不能移出群主或其他群管理员 if ($dialog->isDeputyOwner($actorId)) { $targetIsOwner = $dialog->isPrimaryOwner($targetId) || $dialog->isDeputyOwner($targetId); if ($targetIsOwner) { @@ -389,9 +389,9 @@ class MultiOwnerGroupTest extends TestCase WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $deputy->userid)->update(['role' => 2]); - // 验证权限逻辑:副群主可移出普通成员 + // 验证权限逻辑:群管理员可移出普通成员 $result = $this->simulateRemovePermission($dialog, $deputy->userid, $member->userid); - $this->assertTrue($result['allowed'], '副群主应能移出普通成员,错误:' . ($result['error'] ?? '')); + $this->assertTrue($result['allowed'], '群管理员应能移出普通成员,错误:' . ($result['error'] ?? '')); // 验证实际移出操作(checkDelete=false 绕过 auth,直接测试 DB 状态) $dialog->exitGroup($member->userid, 'remove', false, false); @@ -408,9 +408,9 @@ class MultiOwnerGroupTest extends TestCase WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $deputy->userid)->update(['role' => 2]); - // 验证权限逻辑:副群主不可移出主群主 + // 验证权限逻辑:群管理员不可移出群主 $result = $this->simulateRemovePermission($dialog, $deputy->userid, $owner->userid); - $this->assertFalse($result['allowed'], '副群主不应能移出主群主'); + $this->assertFalse($result['allowed'], '群管理员不应能移出群主'); $this->assertNotNull($result['error']); } @@ -424,9 +424,9 @@ class MultiOwnerGroupTest extends TestCase ->whereIn('userid', [$deputy1->userid, $deputy2->userid]) ->update(['role' => 2]); - // 验证权限逻辑:副群主不可移出其他副群主 + // 验证权限逻辑:群管理员不可移出其他群管理员 $result = $this->simulateRemovePermission($dialog, $deputy1->userid, $deputy2->userid); - $this->assertFalse($result['allowed'], '副群主不应能移出其他副群主'); + $this->assertFalse($result['allowed'], '群管理员不应能移出其他群管理员'); $this->assertEquals('群管理员不能移出群主或其他群管理员', $result['error']); } @@ -466,11 +466,11 @@ class MultiOwnerGroupTest extends TestCase } /** - * 验证离职移交时副群主角色被正确清除。 + * 验证离职移交时群管理员角色被正确清除。 * * UserTransfer::exitDialog() 对离职用户调用 exitGroup($original_userid, 'remove', false, false), * exitGroup 内部直接 hard-delete web_socket_dialog_users 记录($item->delete()), - * 因此副群主的 role 随记录一起消失,无需额外逻辑。 + * 因此群管理员的 role 随记录一起消失,无需额外逻辑。 * * 本测试直接调用 exitDialog()(通过 UserTransfer 实例),绕过 start() 中的项目/任务/文件迁移, * 以确保在无 Swoole 推送的 PHPUnit 环境中可以正常运行。 @@ -482,13 +482,13 @@ class MultiOwnerGroupTest extends TestCase $receiver = $this->makeUser('rec11@test.local'); $dialog = $this->makeGroup($owner->userid, [$departing->userid, $receiver->userid]); - // 将离职用户设为副群主 + // 将离职用户设为群管理员 WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $departing->userid) ->update(['role' => 2]); // 验证前置条件 - $this->assertContains($departing->userid, $dialog->fresh()->deputy_ids, '前置条件:离职用户应是副群主'); + $this->assertContains($departing->userid, $dialog->fresh()->deputy_ids, '前置条件:离职用户应是群管理员'); // 通过 UserTransfer 触发 exitDialog(使用正确字段名 original_userid / new_userid) $transfer = \App\Models\UserTransfer::createInstance([ @@ -500,14 +500,14 @@ class MultiOwnerGroupTest extends TestCase $freshDialog = $dialog->fresh(); - // 离职用户不应再出现在副群主列表中 - $this->assertNotContains($departing->userid, $freshDialog->deputy_ids, '离职用户不应留在副群主列表'); + // 离职用户不应再出现在群管理员列表中 + $this->assertNotContains($departing->userid, $freshDialog->deputy_ids, '离职用户不应留在群管理员列表'); // 离职用户的成员记录应已删除 $exists = WebSocketDialogUser::where('dialog_id', $dialog->id) ->where('userid', $departing->userid) ->exists(); $this->assertFalse($exists, 'exitDialog 后离职用户的成员记录应被删除'); - // 接收方不应自动继承副群主角色 - $this->assertNotContains($receiver->userid, $freshDialog->deputy_ids, '接收方不应自动继承副群主角色'); + // 接收方不应自动继承群管理员角色 + $this->assertNotContains($receiver->userid, $freshDialog->deputy_ids, '接收方不应自动继承群管理员角色'); } } diff --git a/tests/Feature/MultiOwnerProjectTest.php b/tests/Feature/MultiOwnerProjectTest.php index 9930583f3..e1d43bace 100644 --- a/tests/Feature/MultiOwnerProjectTest.php +++ b/tests/Feature/MultiOwnerProjectTest.php @@ -117,17 +117,17 @@ class MultiOwnerProjectTest extends TestCase } /** - * 模拟合并后的 ProjectController::user() 端点:同步成员 + 副负责人。 + * 模拟合并后的 ProjectController::user() 端点:同步成员 + 项目管理员。 * * @param Project $project 项目实例 * @param int $callerUserid 调用方 userid(用于权限判断) - * @param int[] $userids 最终成员完整列表(必须包含主负责人) - * @param int[]|null $deputyUserids 最终副负责人完整列表;null 表示不设置(沿用既有副) + * @param int[] $userids 最终成员完整列表(必须包含项目负责人) + * @param int[]|null $deputyUserids 最终项目管理员完整列表;null 表示不设置(沿用既有项目管理员) * @return int[] 被移除的成员 userids */ private function simulateMemberSync(Project $project, int $callerUserid, array $userids, ?array $deputyUserids): array { - // 鉴权:调用方必须是主或副 + // 鉴权:调用方必须是项目负责人或项目管理员 $callerRow = ProjectUser::where('project_id', $project->id) ->where('userid', $callerUserid)->first(); if (!$callerRow || !in_array((int)$callerRow->owner, [ProjectUser::OWNER_PRIMARY, ProjectUser::OWNER_DEPUTY], true)) { @@ -474,12 +474,12 @@ class MultiOwnerProjectTest extends TestCase \App\Models\ProjectUser::transfer($departing->userid, $receiver->userid); - // 离职的副已不在 project_users + // 离职的项目管理员已不在 project_users $this->assertFalse( ProjectUser::where('project_id', $project->id) ->where('userid', $departing->userid)->exists() ); - // receiver 没有继承副身份 + // receiver 没有继承项目管理员身份 $row = ProjectUser::where('project_id', $project->id)->where('userid', $receiver->userid)->first(); $this->assertNotEquals(2, (int)$row->owner); $this->assertNotContains($receiver->userid, $project->fresh()->deputy_userids);