value('dept_id'); return (int) ($deptId ?: 0); } /** * @param array $creatorIds * @return array{0: array, 1: array, 2: array} * [adminId => deptId, deptId => name, deptId => "祖/父/当前"] */ protected static function loadAdminDeptMap(array $creatorIds): array { $creatorIds = array_values(array_unique(array_filter(array_map('intval', $creatorIds)))); if ($creatorIds === []) { return [[], [], []]; } $rows = AdminDept::whereIn('admin_id', $creatorIds) ->field('admin_id, dept_id') ->select() ->toArray(); $adminToDeptId = []; foreach ($rows as $row) { $adminId = (int) ($row['admin_id'] ?? 0); $deptId = (int) ($row['dept_id'] ?? 0); if ($adminId <= 0 || $deptId <= 0) { continue; } if (!isset($adminToDeptId[$adminId])) { $adminToDeptId[$adminId] = $deptId; } } if ($adminToDeptId === []) { return [[], [], []]; } $allDeptRows = Dept::field('id, pid, name')->select()->toArray(); $deptIndex = []; foreach ($allDeptRows as $row) { $id = (int) ($row['id'] ?? 0); if ($id <= 0) { continue; } $deptIndex[$id] = [ 'pid' => (int) ($row['pid'] ?? 0), 'name' => (string) ($row['name'] ?? ''), ]; } $deptNameMap = []; $deptPathMap = []; foreach (array_unique(array_values($adminToDeptId)) as $deptId) { $deptId = (int) $deptId; if ($deptId <= 0 || !isset($deptIndex[$deptId])) { continue; } $deptNameMap[$deptId] = $deptIndex[$deptId]['name']; $deptPathMap[$deptId] = self::resolveDeptPath($deptId, $deptIndex); } return [$adminToDeptId, $deptNameMap, $deptPathMap]; } /** * 从根节点到当前部门的完整链路(用 / 分隔)。 * * @param array $deptIndex */ private static function resolveDeptPath(int $deptId, array $deptIndex): string { $names = []; $guard = 0; $cursor = $deptId; while ($cursor > 0 && isset($deptIndex[$cursor]) && $guard++ < 32) { $node = $deptIndex[$cursor]; $name = trim($node['name']); if ($name !== '') { array_unshift($names, $name); } $cursor = $node['pid']; } return implode(' / ', $names); } /** * @param array> $rows * @return array> */ protected static function attachDeptInfoToRows(array $rows): array { if ($rows === []) { return $rows; } $creatorIds = array_map(static fn (array $row): int => (int) ($row['creator_id'] ?? 0), $rows); [$adminToDeptId, $deptNameMap, $deptPathMap] = self::loadAdminDeptMap($creatorIds); foreach ($rows as &$row) { $creatorId = (int) ($row['creator_id'] ?? 0); $deptId = $adminToDeptId[$creatorId] ?? 0; $row['dept_id'] = $deptId; $row['dept_name'] = $deptId > 0 ? (string) ($deptNameMap[$deptId] ?? '') : ''; $row['dept_path'] = $deptId > 0 ? (string) ($deptPathMap[$deptId] ?? '') : ''; } unset($row); return $rows; } /** * 根据 dept_id(包含其子部门)收窄可见 admin id 集合。 * 与 visibleAdminIds 取交集。 * * @param array|null $visibleAdminIds null 表示不限 * @return array|null 返回 null 表示外部条件无需附加;返回 [] 表示无可见 admin */ protected static function intersectVisibleByDept(?array $visibleAdminIds, int $deptId): ?array { if ($deptId <= 0) { return $visibleAdminIds; } $deptIds = DeptLogic::getSelfAndDescendantIds($deptId); if ($deptIds === []) { $deptIds = [$deptId]; } $adminIds = AdminDept::whereIn('dept_id', $deptIds)->column('admin_id'); $adminIds = array_values(array_unique(array_filter(array_map('intval', $adminIds), static fn (int $v): bool => $v > 0))); if ($visibleAdminIds === null) { return $adminIds; } return array_values(array_intersect($visibleAdminIds, $adminIds)); } protected static function normalizeMediaSource(string $mediaSource): string { return trim($mediaSource); } /** * 与 project.self_input_stats_view_all_roles 一致:超管或白名单角色可见全部录入人数据。 */ protected static function canViewAllSelfInputStats(array $adminInfo): bool { if ((int) ($adminInfo['root'] ?? 0) === 1) { return true; } $allow = Config::get('project.self_input_stats_view_all_roles', []); $allow = array_map('intval', is_array($allow) ? $allow : []); if ($allow === []) { return false; } $myRoles = array_map('intval', $adminInfo['role_id'] ?? []); return count(array_intersect($myRoles, $allow)) > 0; } /** * @return array|null null=全部录入人;[]=无可见;int[]=可见 creator_id 集合 */ protected static function getVisibleCreatorIds(int $adminId, array $adminInfo): ?array { if (self::canViewAllSelfInputStats($adminInfo)) { return null; } return DataScopeService::getVisibleAdminIds($adminId, $adminInfo); } protected static function assertRecordVisible(int $adminId, array $adminInfo, int $creatorId): bool { $visibleIds = self::getVisibleCreatorIds($adminId, $adminInfo); if ($visibleIds === null) { return true; } return in_array($creatorId, $visibleIds, true); } /** * 同一录入人 + 同一天 + 同一渠道唯一(不同录入人可同日同渠道各录一条)。 */ protected static function isYejiDuplicate(int $creatorId, string $yejiDate, string $mediaSource, int $excludeId = 0): bool { $query = PersonalYeji::where('creator_id', $creatorId) ->where('yeji_date', $yejiDate) ->where('media_source', $mediaSource); if ($excludeId > 0) { $query->where('id', '<>', $excludeId); } return $query->count() > 0; } /** * 同一录入人 + 同一天 + 同一渠道唯一(不同录入人可同日同渠道各录一条)。 */ protected static function isCostDuplicate(int $creatorId, string $costDate, string $mediaSource, int $excludeId = 0): bool { $query = PersonalAccountCost::where('creator_id', $creatorId) ->where('cost_date', $costDate) ->where('media_source', $mediaSource); if ($excludeId > 0) { $query->where('id', '<>', $excludeId); } return $query->count() > 0; } }