|null * } */ public static function resolve(int $adminId, array $adminInfo): array { $roleNames = self::roleNames($adminId); $kind = self::classify((int) ($adminInfo['root'] ?? 0) === 1, $roleNames, $adminInfo); $metricAdminIds = self::metricAdminIds($kind, $adminId, $adminInfo); return [ 'kind' => $kind, 'label' => self::kindLabel($kind), 'metric_admin_ids' => $metricAdminIds, ]; } public static function kindLabel(string $kind): string { return [ self::KIND_ADMIN => '全部数据', self::KIND_MANAGER => '本部门', self::KIND_GROUP_LEADER => '本小组', self::KIND_ASSISTANT => '仅本人', ][$kind] ?? '数据范围'; } /** * @param string[] $roleNames */ public static function classify(bool $isRoot, array $roleNames, array $adminInfo = []): string { if ($isRoot) { return self::KIND_ADMIN; } if (self::roleNamesMatch($roleNames, ['管理员'])) { return self::KIND_ADMIN; } if (self::roleNamesMatch($roleNames, ['经理'])) { return self::KIND_MANAGER; } if (self::roleNamesMatch($roleNames, ['诊室组长', '组长'])) { return self::KIND_GROUP_LEADER; } // 医助角色固定仅本人,不因部门负责人或 data_scope 放大到小组。 if (self::roleNamesMatch($roleNames, ['医助'])) { return self::KIND_ASSISTANT; } $scope = DataScopeService::getEffectiveScope($adminInfo); return match ($scope) { DataScopeService::SCOPE_ALL => self::KIND_ADMIN, DataScopeService::SCOPE_DEPT_AND_CHILD => self::KIND_MANAGER, DataScopeService::SCOPE_DEPT => self::KIND_GROUP_LEADER, default => self::KIND_ASSISTANT, }; } /** * @return array|null */ private static function metricAdminIds(string $kind, int $adminId, array $adminInfo = []): ?array { if ($kind === self::KIND_ADMIN) { return null; } if ($kind === self::KIND_ASSISTANT || $adminId <= 0) { return $adminId > 0 ? [$adminId] : []; } if ($kind === self::KIND_GROUP_LEADER) { $ids = self::adminsInGroup($adminId, $adminInfo); return $ids !== [] ? $ids : ($adminId > 0 ? [$adminId] : []); } $ids = self::adminsInOwnDeptTree($adminId); if ($ids === []) { return $adminId > 0 ? [$adminId] : []; } return $ids; } /** * 组长小组:只取本人最深的部门(不含一中心/二中心整棵树),并并入其担任负责人的部门。 * * @return int[] */ private static function adminsInGroup(int $adminId, array $adminInfo): array { $ownDeptIds = self::ownDeptIds($adminId); $leafDeptIds = self::leafDeptIds($ownDeptIds); $ledDeptIds = self::ledDeptIds($adminId, $adminInfo, $ownDeptIds); $groupDeptIds = array_values(array_unique(array_merge($leafDeptIds, $ledDeptIds))); $groupDeptIds = self::dropCenterRootsIfHasDeeper($groupDeptIds); if ($groupDeptIds === []) { $groupDeptIds = $leafDeptIds !== [] ? $leafDeptIds : $ownDeptIds; } $deptIds = []; foreach ($groupDeptIds as $deptId) { foreach (DeptLogic::getSelfAndDescendantIds((int) $deptId) as $id) { $id = (int) $id; if ($id > 0) { $deptIds[] = $id; } } } $deptIds = array_values(array_unique($deptIds)); if ($deptIds === []) { return $adminId > 0 ? [$adminId] : []; } $adminIds = array_values(array_unique(array_filter( array_map('intval', AdminDept::whereIn('dept_id', $deptIds)->column('admin_id')), static fn (int $id): bool => $id > 0 ))); if ($adminId > 0 && !in_array($adminId, $adminIds, true)) { $adminIds[] = $adminId; } return $adminIds; } /** * @return int[] */ private static function ownDeptIds(int $adminId): array { return array_values(array_unique(array_filter( array_map('intval', AdminDept::where('admin_id', $adminId)->column('dept_id')), static fn (int $id): bool => $id > 0 ))); } /** * 在本人所属部门里只留最深的节点,避免挂在「一中心」上就把整个中心当成小组。 * * @param int[] $ownDeptIds * @return int[] */ private static function leafDeptIds(array $ownDeptIds): array { if ($ownDeptIds === []) { return []; } $deptById = []; $rows = Dept::whereNull('delete_time')->field(['id', 'pid', 'name'])->select()->toArray(); foreach ($rows as $row) { $id = (int) ($row['id'] ?? 0); if ($id > 0) { $deptById[$id] = [ 'pid' => (int) ($row['pid'] ?? 0), 'name' => (string) ($row['name'] ?? ''), ]; } } $ownSet = array_fill_keys($ownDeptIds, true); $leaves = []; foreach ($ownDeptIds as $id) { $hasOwnDescendant = false; foreach ($ownDeptIds as $other) { if ($other === $id) { continue; } if (self::isAncestorOf($id, $other, $deptById)) { $hasOwnDescendant = true; break; } } if (!$hasOwnDescendant && isset($ownSet[$id])) { $leaves[] = $id; } } return array_values(array_unique($leaves)); } /** * @param array $deptById */ private static function isAncestorOf(int $ancestorId, int $nodeId, array $deptById): bool { $current = $nodeId; $seen = []; while ($current > 0 && isset($deptById[$current]) && !isset($seen[$current])) { $seen[$current] = true; $pid = $deptById[$current]['pid']; if ($pid === $ancestorId) { return true; } $current = $pid; } return false; } /** * 部门负责人姓名匹配当前组长时,把该部门算进小组。仅用于已判定为组长的账号。 * * @param int[] $ownDeptIds * @return int[] */ private static function ledDeptIds(int $adminId, array $adminInfo, array $ownDeptIds): array { $name = self::normalizePersonName((string) ($adminInfo['name'] ?? '')); if ($adminId <= 0 || $name === '') { return []; } $rows = Dept::whereNull('delete_time')->field(['id', 'pid', 'leader'])->select()->toArray(); $ownSet = array_fill_keys($ownDeptIds, true); $led = []; foreach ($rows as $row) { $deptId = (int) ($row['id'] ?? 0); $leaderName = self::normalizePersonName((string) ($row['leader'] ?? '')); if ($deptId <= 0 || $leaderName === '' || $leaderName !== $name) { continue; } if ($ownSet === [] || isset($ownSet[$deptId]) || self::deptUnderOwnTree($deptId, $ownDeptIds)) { $led[] = $deptId; } } return array_values(array_unique($led)); } /** * @param int[] $ownDeptIds */ private static function deptUnderOwnTree(int $deptId, array $ownDeptIds): bool { foreach ($ownDeptIds as $rootId) { $ids = DeptLogic::getSelfAndDescendantIds((int) $rootId); foreach ($ids as $id) { if ((int) $id === $deptId) { return true; } } } return false; } /** * @param int[] $deptIds * @return int[] */ private static function dropCenterRootsIfHasDeeper(array $deptIds): array { $names = []; if ($deptIds !== []) { $names = Dept::whereIn('id', $deptIds)->whereNull('delete_time')->column('name', 'id'); } $hasDeeper = false; foreach ($deptIds as $id) { $name = (string) ($names[$id] ?? ''); if ($name !== '' && mb_strpos($name, '一中心') === false && mb_strpos($name, '二中心') === false) { $hasDeeper = true; break; } } if (!$hasDeeper) { return $deptIds; } $kept = []; foreach ($deptIds as $id) { $name = (string) ($names[$id] ?? ''); if ($name !== '' && (mb_strpos($name, '一中心') !== false || mb_strpos($name, '二中心') !== false)) { continue; } $kept[] = $id; } return $kept; } private static function normalizePersonName(string $raw): string { $value = trim($raw); if ($value === '') { return ''; } $value = preg_replace('/[((][^))]*[))]/u', '', $value) ?? $value; $value = preg_replace('/[\s\x{3000}]+/u', '', $value) ?? $value; $suffixes = ['组长', '负责人', '主管', '主任', '医师', '医生', '医助', '老师']; foreach ($suffixes as $suffix) { $len = mb_strlen($suffix); while (mb_strlen($value) > $len && mb_substr($value, -$len) === $suffix) { $value = mb_substr($value, 0, mb_strlen($value) - $len); } } return trim($value); } /** * @return int[] */ private static function adminsInOwnDeptTree(int $adminId): array { $ownDeptIds = array_values(array_unique(array_filter( array_map('intval', AdminDept::where('admin_id', $adminId)->column('dept_id')), static fn (int $id): bool => $id > 0 ))); if ($ownDeptIds === []) { return $adminId > 0 ? [$adminId] : []; } $deptIds = []; foreach ($ownDeptIds as $deptId) { foreach (DeptLogic::getSelfAndDescendantIds($deptId) as $id) { $id = (int) $id; if ($id > 0) { $deptIds[] = $id; } } } $deptIds = array_values(array_unique($deptIds)); if ($deptIds === []) { return [$adminId]; } $adminIds = array_values(array_unique(array_filter( array_map('intval', AdminDept::whereIn('dept_id', $deptIds)->column('admin_id')), static fn (int $id): bool => $id > 0 ))); if ($adminId > 0 && !in_array($adminId, $adminIds, true)) { $adminIds[] = $adminId; } return $adminIds; } /** * @return string[] */ private static function roleNames(int $adminId): array { if ($adminId <= 0) { return []; } $roleIds = array_values(array_unique(array_filter( array_map('intval', AdminRole::where('admin_id', $adminId)->column('role_id')), static fn (int $id): bool => $id > 0 ))); if ($roleIds === []) { return []; } $names = SystemRole::whereIn('id', $roleIds) ->whereNull('delete_time') ->column('name'); return array_values(array_filter(array_map('strval', $names))); } /** * @param string[] $roleNames * @param string[] $needles */ private static function roleNamesMatch(array $roleNames, array $needles): bool { foreach ($roleNames as $name) { $name = trim($name); if ($name === '') { continue; } foreach ($needles as $needle) { if ($name === $needle || mb_strpos($name, $needle) !== false) { return true; } } } return false; } }