@dreamor/atlas-cli 0.7.16 → 0.7.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -39,3 +39,43 @@ export function aggregateByAxis(rows, axis) {
|
|
|
39
39
|
export function filterByStaff(entries, staffId) {
|
|
40
40
|
return entries.filter((p) => p.staffId === staffId || p.staffName?.includes(staffId));
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* 走查 summaryByTeam 返回的树结构,提取指定项目的实际工时条目。
|
|
44
|
+
*
|
|
45
|
+
* 树中同一员工可能出现在 manager 层(汇总下属)和 leaf 层(个人),
|
|
46
|
+
* 采取走最深层的策略:越深的节点 detail 越精确。
|
|
47
|
+
*/
|
|
48
|
+
export function flattenTree(nodes, targetPid) {
|
|
49
|
+
const byStaffId = new Map();
|
|
50
|
+
function walk(items, depth) {
|
|
51
|
+
for (const raw of items ?? []) {
|
|
52
|
+
const node = raw;
|
|
53
|
+
const details = (node.detail ?? []);
|
|
54
|
+
let projectManpower = 0;
|
|
55
|
+
for (const d of details) {
|
|
56
|
+
if (String(d.projectId) === targetPid) {
|
|
57
|
+
projectManpower += Number(d.manpower ?? 0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (projectManpower > 0) {
|
|
61
|
+
const sid = String(node.staffId ?? '');
|
|
62
|
+
const existing = byStaffId.get(sid);
|
|
63
|
+
if (!existing || depth > existing.depth) {
|
|
64
|
+
byStaffId.set(sid, {
|
|
65
|
+
depth,
|
|
66
|
+
entry: {
|
|
67
|
+
staffId: sid,
|
|
68
|
+
staffName: String(node.realname ?? ''),
|
|
69
|
+
manpower: Math.round(projectManpower * 100) / 100,
|
|
70
|
+
departmentName: String(node.department ?? ''),
|
|
71
|
+
role: String(node.role ?? ''),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
walk(node.children, depth + 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
walk(nodes, 0);
|
|
80
|
+
return [...byStaffId.values()].map((v) => v.entry);
|
|
81
|
+
}
|
|
@@ -4,15 +4,25 @@ import { enforceOutputLimit } from '../../util/output-limit.js';
|
|
|
4
4
|
import { SessionExpiredError } from '../../util/errors.js';
|
|
5
5
|
import { expandMonths, expandMonthsDefault } from '../../util/months.js';
|
|
6
6
|
import { resolveSecureExportPath, secureWriteFile } from '../../util/secure-fs.js';
|
|
7
|
-
import { annotateWithMonth, aggregateByAxis, filterByStaff, } from './_logic.js';
|
|
7
|
+
import { annotateWithMonth, aggregateByAxis, filterByStaff, flattenTree, } from './_logic.js';
|
|
8
8
|
import { resolveProjectId } from '../../util/env.js';
|
|
9
|
+
import { readBanmaIdentity } from '../../auth/session.js';
|
|
9
10
|
function getProjectId(opts) {
|
|
10
11
|
return resolveProjectId(opts.projectId);
|
|
11
12
|
}
|
|
12
13
|
async function fetchActual(pid, month) {
|
|
13
14
|
const client = createClient();
|
|
14
|
-
const
|
|
15
|
-
|
|
15
|
+
const identity = await readBanmaIdentity();
|
|
16
|
+
const loginStaffId = identity?.staffId ?? '';
|
|
17
|
+
// B4: summaryByTeam 请求体需用 projectIds 数组 + 额外字段
|
|
18
|
+
// 使用 projectIds 时 detail 只返回该项目的数据
|
|
19
|
+
const raw = await client.post('/yuntu-service/manpower/weekly/summaryByTeam.json', { month, staffIds: [], projectIds: [pid], isConfirm: false, loginStaffId });
|
|
20
|
+
const tree = (Array.isArray(raw) ? raw : []);
|
|
21
|
+
// 根节点为当前用户,children 为团队成员
|
|
22
|
+
if (tree.length > 0 && tree[0].children) {
|
|
23
|
+
return flattenTree(tree[0].children, pid);
|
|
24
|
+
}
|
|
25
|
+
return [];
|
|
16
26
|
}
|
|
17
27
|
export async function showCmd(staffId, opts) {
|
|
18
28
|
const pid = getProjectId(opts);
|
|
@@ -5,6 +5,8 @@ import { expandMonths, expandMonthsDefault } from '../../util/months.js';
|
|
|
5
5
|
import { monthTsToKey } from '../../util/time.js';
|
|
6
6
|
import { groupByAxis, mergeBaselineActual, } from './_logic.js';
|
|
7
7
|
import { resolveProjectId, resolveProjectInfo } from '../../util/env.js';
|
|
8
|
+
import { readBanmaIdentity } from '../../auth/session.js';
|
|
9
|
+
import { flattenTree as flattenActualTree } from '../actual/_logic.js';
|
|
8
10
|
function getProjectId(opts) {
|
|
9
11
|
return resolveProjectId(opts.projectId);
|
|
10
12
|
}
|
|
@@ -38,9 +40,21 @@ export async function compareCmd(opts) {
|
|
|
38
40
|
}
|
|
39
41
|
// 指数退避:避免连续触发限流(B2)
|
|
40
42
|
try {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
const identity = await readBanmaIdentity();
|
|
44
|
+
const loginStaffId = identity?.staffId ?? '';
|
|
45
|
+
const raw = await client.post('/yuntu-service/manpower/weekly/summaryByTeam.json', {
|
|
46
|
+
month: m, staffIds: [], projectIds: [pid], isConfirm: false, loginStaffId,
|
|
47
|
+
});
|
|
48
|
+
const tree = (Array.isArray(raw) ? raw : []);
|
|
49
|
+
const results = [];
|
|
50
|
+
if (tree.length > 0 && tree[0].children) {
|
|
51
|
+
const entries = flattenActualTree(tree[0].children, pid);
|
|
52
|
+
for (const p of entries) {
|
|
53
|
+
results.push({ month: m, manpower: p.manpower ?? 0, role: p.role, departmentName: p.departmentName });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const ac of results) {
|
|
57
|
+
allAc.push(ac);
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
60
|
catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ATLAS_VERSION = '0.7.
|
|
1
|
+
export const ATLAS_VERSION = '0.7.17';
|