>, total_target: float, data_scope_limited: bool} */ public static function monthMatrix(string $yearMonth, int $adminId = 0, array $adminInfo = []): array { $tree = DeptLogic::getAllData(); $allowed = DataScopeService::getAllowedDeptIdSet($adminId, $adminInfo); $scoped = $allowed !== null; if ($scoped) { $tree = self::filterDeptTreeAllowedForest(is_array($tree) ? $tree : [], $allowed); } $rowsDb = DeptPerformanceTarget::where('year_month', $yearMonth) ->select() ->toArray(); $targets = []; foreach ($rowsDb as $r) { $targets[(int) ($r['dept_id'] ?? 0)] = $r; } $rows = self::attachTargetsToTree(is_array($tree) ? $tree : [], $targets); $total = self::sumTargetsInTree($rows); return [ 'year_month' => $yearMonth, 'rows' => $rows, 'total_target' => round($total, 2), 'data_scope_limited' => $scoped, ]; } /** * @param array $items */ public static function batchSave(string $yearMonth, array $items, int $adminId, array $adminInfo, string $adminName): bool { $allowed = DataScopeService::getAllowedDeptIdSet($adminId, $adminInfo); try { Db::transaction(function () use ($yearMonth, $items, $adminId, $adminName, $allowed): void { foreach ($items as $item) { if (!is_array($item)) { continue; } $deptId = (int) ($item['dept_id'] ?? 0); if ($deptId <= 0) { continue; } // 仅处理 data scope 内的 dept_id(界面树与 monthMatrix 一致,不再含纯展示用父级) if ($allowed !== null && !isset($allowed[$deptId])) { continue; } $dept = Dept::where('id', $deptId)->whereNull('delete_time')->find(); if (!$dept) { continue; } $deptName = trim((string) ($dept->name ?? '')); $amt = round((float) ($item['target_amount'] ?? 0), 2); $remark = mb_substr(trim((string) ($item['remark'] ?? '')), 0, 255); if ($amt <= 0) { DeptPerformanceTarget::where('dept_id', $deptId) ->where('year_month', $yearMonth) ->delete(); continue; } $row = DeptPerformanceTarget::where('dept_id', $deptId) ->where('year_month', $yearMonth) ->find(); if ($row) { $row->dept_name = $deptName; $row->target_amount = $amt; $row->remark = $remark; $row->updater_id = $adminId; $row->updater_name = $adminName; $row->save(); } else { DeptPerformanceTarget::create([ 'dept_id' => $deptId, 'dept_name' => $deptName, 'year_month' => $yearMonth, 'target_amount' => $amt, 'remark' => $remark, 'creator_id' => $adminId, 'creator_name' => $adminName, 'updater_id' => $adminId, 'updater_name' => $adminName, ]); } } }); return true; } catch (\Throwable $e) { self::setError($e->getMessage()); return false; } } /** * @param array> $nodes DeptLogic::getAllData 子树 * @param array> $targets keyed by dept_id * * @return array> */ private static function attachTargetsToTree(array $nodes, array $targets, string $prefix = ''): array { $out = []; foreach ($nodes as $n) { if (!is_array($n)) { continue; } $id = (int) ($n['id'] ?? 0); $name = trim((string) ($n['name'] ?? '')); if ($id <= 0) { continue; } $path = $prefix === '' ? $name : $prefix . ' / ' . $name; $t = $targets[$id] ?? null; $amt = $t ? round((float) $t['target_amount'], 2) : 0.0; $node = [ 'dept_id' => $id, 'dept_name' => $name, 'dept_path' => $path, 'target_id' => $t ? (int) $t['id'] : 0, 'target_amount' => $amt, 'remark' => $t ? (string) ($t['remark'] ?? '') : '', ]; $rawChildren = $n['children'] ?? []; $childList = is_array($rawChildren) && $rawChildren !== [] ? self::attachTargetsToTree($rawChildren, $targets, $path) : []; if ($childList !== []) { $node['children'] = $childList; } $out[] = $node; } return $out; } /** * @param array> $nodes */ private static function sumTargetsInTree(array $nodes): float { $s = 0.0; foreach ($nodes as $n) { $s += (float) ($n['target_amount'] ?? 0); $ch = $n['children'] ?? []; if (is_array($ch) && $ch !== []) { $s += self::sumTargetsInTree($ch); } } return $s; } /** * 本部门及以下:仅展示 allowed 内节点;无权父级不渲染,从子树中接续(不露出公司/上级中心)。 * * @param array> $nodes * @param array $allowedMap * * @return array> */ private static function filterDeptTreeAllowedForest(array $nodes, array $allowedMap): array { if ($allowedMap === []) { return []; } $out = []; foreach ($nodes as $n) { if (!is_array($n)) { continue; } $id = (int) ($n['id'] ?? 0); if ($id <= 0) { continue; } $rawChildren = $n['children'] ?? []; $kids = is_array($rawChildren) ? $rawChildren : []; if (isset($allowedMap[$id])) { $childList = []; foreach ($kids as $c) { if (!is_array($c)) { continue; } foreach (self::filterDeptTreeAllowedForest([$c], $allowedMap) as $item) { $childList[] = $item; } } $node = $n; if ($childList !== []) { $node['children'] = $childList; } else { unset($node['children']); } $out[] = $node; continue; } foreach ($kids as $c) { if (!is_array($c)) { continue; } foreach (self::filterDeptTreeAllowedForest([$c], $allowedMap) as $item) { $out[] = $item; } } } return $out; } }