// +---------------------------------------------------------------------- namespace plugin\saiadmin\app\logic\system; use app\dice\helper\AdminScopeHelper; use plugin\saiadmin\app\cache\UserMenuCache; use plugin\saiadmin\app\model\system\SystemRole; use plugin\saiadmin\app\service\SystemRoleChannelService; use plugin\saiadmin\basic\think\BaseLogic; use plugin\saiadmin\exception\ApiException; use support\think\Cache; use support\think\Db; /** * 角色逻辑层(按渠道 dept_id 隔离) */ class SystemRoleLogic extends BaseLogic { public function __construct() { $this->model = new SystemRole(); } /** * 分页列表(按渠道过滤) */ public function indexList(array $where, $requestDeptId = null): array { $query = $this->search($where); $this->applyDeptScope($query, $requestDeptId); $levelArr = array_column($this->adminInfo['roleList'] ?? [], 'level'); if (!empty($levelArr)) { $maxLevel = max($levelArr); $query->where('level', '<', $maxLevel); } $query->where('id', '<>', SystemRoleChannelService::SUPER_ADMIN_ROLE_ID); return $this->getList($query); } public function add($data): bool { $data = $this->handleData($data); $deptId = AdminScopeHelper::normalizeRecordDeptId($data['dept_id'] ?? null); $data['dept_id'] = $deptId; $this->assertCodeUniqueInDept($data['code'] ?? '', $deptId, null); return $this->model->save($data); } public function edit($id, $data): bool { $model = $this->model->findOrEmpty($id); if ($model->isEmpty()) { throw new ApiException('Data not found'); } $this->assertRoleWritable($model); $data = $this->handleData($data); $deptId = AdminScopeHelper::normalizeRecordDeptId($model->dept_id ?? $data['dept_id'] ?? null); $data['dept_id'] = $deptId; $this->assertCodeUniqueInDept($data['code'] ?? '', $deptId, (int) $id); return $model->save($data); } public function destroy($ids): bool { $levelArr = array_column($this->adminInfo['roleList'] ?? [], 'level'); $maxLevel = !empty($levelArr) ? max($levelArr) : 100; $idList = is_array($ids) ? $ids : explode(',', (string) $ids); foreach ($idList as $roleId) { $roleId = (int) $roleId; if ($roleId === SystemRoleChannelService::SUPER_ADMIN_ROLE_ID) { throw new ApiException('Cannot delete super admin role'); } $role = $this->model->find($roleId); if (!$role) { continue; } $this->assertRoleWritable($role); if ((int) ($role->level ?? 0) >= $maxLevel) { throw new ApiException('Cannot operate roles with higher level than current account'); } } return $this->model->destroy($ids); } protected function handleData($data) { $levelArr = array_column($this->adminInfo['roleList'] ?? [], 'level'); if (!empty($levelArr)) { $maxLevel = max($levelArr); if (($data['level'] ?? 0) >= $maxLevel) { throw new ApiException('Cannot operate roles with higher level than current account'); } } return $data; } public function accessRole(array $where = [], $requestDeptId = null): array { $query = $this->search($where); $this->applyDeptScope($query, $requestDeptId); $levelArr = array_column($this->adminInfo['roleList'] ?? [], 'level'); if (!empty($levelArr)) { $maxLevel = max($levelArr); $query->where('level', '<', $maxLevel); } $query->where('id', '<>', SystemRoleChannelService::SUPER_ADMIN_ROLE_ID); $query->order('sort', 'desc'); return $this->getAll($query); } public function getMenuIdsByRoleIds($ids): array { if (empty($ids)) { return []; } return $this->model->where('id', 'in', $ids)->with([ 'menus' => function ($query) { $query->where('status', 1)->order('sort', 'desc'); } ])->select()->toArray(); } public function getMenuByRole($id): array { $role = $this->model->findOrEmpty($id); if ($role->isEmpty()) { throw new ApiException('Data not found'); } $this->assertRoleWritable($role); $menus = $role->menus ?: []; return [ 'id' => $id, 'menus' => $menus ]; } public function saveMenuPermission($id, $menu_ids): mixed { $role = $this->model->findOrEmpty($id); if ($role->isEmpty()) { throw new ApiException('Data not found'); } $this->assertRoleWritable($role); return $this->transaction(function () use ($id, $menu_ids) { $role = $this->model->findOrEmpty($id); if ($role) { $role->menus()->detach(); $data = array_map(function ($menu_id) use ($id) { return ['menu_id' => $menu_id, 'role_id' => $id]; }, $menu_ids); Db::name('sa_system_role_menu')->limit(100)->insertAll($data); } $cache = config('plugin.saiadmin.saithink.button_cache'); $tag = $cache['role'] . $id; Cache::tag($tag)->clear(); UserMenuCache::clearMenuCache(); return true; }); } /** * 解析并校验当前请求应操作的渠道 ID */ public function resolveRequestDeptId($requestDeptId): int { if ((int) ($this->adminInfo['id'] ?? 0) === 1) { return AdminScopeHelper::resolveConfigDeptId($this->adminInfo, $requestDeptId); } $deptLogic = new SystemDeptLogic(); $deptLogic->init($this->adminInfo); return $deptLogic->resolveAccessibleDeptId(); } /** * 列表/下拉按渠道过滤 */ protected function applyDeptScope($query, $requestDeptId = null): void { if (!$this->tableHasDeptIdColumn()) { return; } if ((int) ($this->adminInfo['id'] ?? 0) === 1) { $deptId = AdminScopeHelper::resolveConfigDeptId($this->adminInfo, $requestDeptId); $query->where('dept_id', $deptId); return; } $deptLogic = new SystemDeptLogic(); $deptLogic->init($this->adminInfo); $deptId = $deptLogic->resolveAccessibleDeptId(); if ($deptId > 0) { $query->where('dept_id', $deptId); } } /** * 校验角色属于当前可操作渠道 */ public function assertRoleWritable($role): void { if (!$this->tableHasDeptIdColumn()) { return; } $roleDeptId = AdminScopeHelper::normalizeRecordDeptId($role->dept_id ?? null); if ((int) ($role->id ?? 0) === SystemRoleChannelService::SUPER_ADMIN_ROLE_ID) { throw new ApiException('Cannot operate super admin role'); } if ((int) ($this->adminInfo['id'] ?? 0) === 1) { return; } $deptLogic = new SystemDeptLogic(); $deptLogic->init($this->adminInfo); $scopeDeptId = $deptLogic->resolveAccessibleDeptId(); if ($scopeDeptId > 0 && $roleDeptId !== $scopeDeptId) { throw new ApiException('No permission to operate this channel role'); } } protected function assertCodeUniqueInDept(string $code, int $deptId, ?int $excludeId): void { if ($code === '') { return; } $query = SystemRole::where('code', $code)->where('dept_id', $deptId); if ($excludeId !== null && $excludeId > 0) { $query->where('id', '<>', $excludeId); } if ($query->count() > 0) { throw new ApiException('Role code already exists in this channel'); } } protected function tableHasDeptIdColumn(): bool { try { $fields = Db::getFields((new SystemRole())->getTable()); return isset($fields['dept_id']); } catch (\Throwable $e) { return false; } } }