weaver-e9-assistant-cli 1.0.0 → 1.1.1
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.
- package/dist/auth.js +18 -8
- package/dist/blog.js +65 -2
- package/dist/cli.js +332 -56
- package/dist/client.js +26 -5
- package/dist/cube.js +121 -18
- package/dist/email.js +4 -1
- package/dist/subordinate.js +419 -0
- package/dist/todobatch.js +7 -0
- package/dist/wfuser.js +37 -0
- package/dist/workplan.js +37 -0
- package/package.json +27 -25
package/dist/todobatch.js
CHANGED
|
@@ -41,10 +41,13 @@ function pickRandomFields(row) {
|
|
|
41
41
|
|
|
42
42
|
/** 拉待办列表并取出与 requestIds 匹配的原始行(含 randomField)+ dataKey。跨页匹配直到全部命中或到末尾。 */
|
|
43
43
|
async function loadRowsFor(cfg, requestIds) {
|
|
44
|
+
// 修复 #6:逐页定位待办是最耗时环节,实时打印进度,避免长时间无输出被误判卡死。
|
|
44
45
|
const wanted = new Set(requestIds.map(String));
|
|
45
46
|
const found = new Map();
|
|
46
47
|
let dataKey = '';
|
|
48
|
+
console.error(` [批量] 正在待办列表定位 ${requestIds.length} 条流程(按请求顺序逐页搜索)...`);
|
|
47
49
|
for (let current = 1; current <= 10; current++) {
|
|
50
|
+
console.error(` [批量] 读取待办第 ${current} 页...`);
|
|
48
51
|
const list = await getWfList(cfg, { category: 'todo', page: current, pageSize: '100' });
|
|
49
52
|
dataKey = list.dataKey || dataKey;
|
|
50
53
|
const rows = extractRows(list.raw?.datas);
|
|
@@ -53,6 +56,7 @@ async function loadRowsFor(cfg, requestIds) {
|
|
|
53
56
|
if (wanted.has(rid) && !found.has(rid))
|
|
54
57
|
found.set(rid, row);
|
|
55
58
|
}
|
|
59
|
+
console.error(` [批量] 第 ${current} 页已定位 ${found.size}/${requestIds.length} 条`);
|
|
56
60
|
if (rows.length < 100 || found.size === wanted.size)
|
|
57
61
|
break;
|
|
58
62
|
if (!rows.length)
|
|
@@ -68,6 +72,7 @@ async function loadRowsFor(cfg, requestIds) {
|
|
|
68
72
|
|
|
69
73
|
/** 批量门禁(抓包实证):checks(dataKey+randomDatas) + getSecondAuthConfig4List(requestIds=rid_uid+__weaver_request_url__=base64)。 */
|
|
70
74
|
async function runGate(api, checkId, rows, dataKey) {
|
|
75
|
+
console.error(` [批量] 执行门禁检查(${checkId})...`);
|
|
71
76
|
const check = await api.post('/api/ec/dev/table/checks', {
|
|
72
77
|
dataKey,
|
|
73
78
|
checkId,
|
|
@@ -92,6 +97,7 @@ export async function batchSubmit(cfg, opts) {
|
|
|
92
97
|
const api = await createApiFromProfile(cfg);
|
|
93
98
|
const { dataKey, rows } = await loadRowsFor(cfg, requestIds);
|
|
94
99
|
const gate = await runGate(api, 'batchSubmit', rows, dataKey);
|
|
100
|
+
console.error(` [批量] 提交 ${requestIds.length} 条流程(requestBatchSubmit)...`);
|
|
95
101
|
const result = await api.post('/api/workflow/reqform/requestBatchSubmit', {
|
|
96
102
|
multiSubIds: requestIds.join(',') + ',',
|
|
97
103
|
remark: String(opts.remark ?? 'OK'),
|
|
@@ -99,6 +105,7 @@ export async function batchSubmit(cfg, opts) {
|
|
|
99
105
|
belongtoUserids: userIds.join(',') + ',',
|
|
100
106
|
needBatchSubmit: '1',
|
|
101
107
|
});
|
|
108
|
+
console.error(` [批量] 提交响应已返回(status=${result?.status ?? '?'})`);
|
|
102
109
|
return { requestIds, userIds, gate, result };
|
|
103
110
|
}
|
|
104
111
|
|
package/dist/wfuser.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* aiAgentToken 鉴权。返回 data.{userid, username, deptid, deptname, subcompanyname, jobs}。
|
|
6
6
|
*/
|
|
7
7
|
import { AuthTokenSession } from './auth-token.js';
|
|
8
|
+
import { createApiFromProfile } from './client.js';
|
|
8
9
|
import { todayStr } from './util.js';
|
|
9
10
|
/** 取当前登录用户信息。 */
|
|
10
11
|
export async function getCurrentUser(cfg) {
|
|
@@ -28,6 +29,42 @@ export async function getCurrentUser(cfg) {
|
|
|
28
29
|
jobs: String(cur.jobs ?? ''),
|
|
29
30
|
};
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* 当前用户是否系统管理员。GET /api/ec/dev/app/getUserInfo?__random__=<ts> → isAdmin===true。
|
|
34
|
+
* 返回 { isAdmin: true|false|undefined }——undefined 表示无法确认(未取到 isAdmin 字段),
|
|
35
|
+
* 调用方按「非管理员」fail-closed 处理(越权防护默认不放行)。
|
|
36
|
+
* 鉴权:token 通道优先;token no-op(200 但无 isAdmin)且配了 cookie → 强制 cookie 头重发逃生。
|
|
37
|
+
*/
|
|
38
|
+
export async function getCurrentUserIsAdmin(cfg) {
|
|
39
|
+
const api = await createApiFromProfile(cfg);
|
|
40
|
+
let r = await api.get('/api/ec/dev/app/getUserInfo', { __random__: Date.now() });
|
|
41
|
+
let isAdmin = pickIsAdmin(r?.json);
|
|
42
|
+
if (isAdmin === undefined && cfg.cookie) {
|
|
43
|
+
r = await api.get('/api/ec/dev/app/getUserInfo', { __random__: Date.now() }, { forceCookie: true });
|
|
44
|
+
isAdmin = pickIsAdmin(r?.json);
|
|
45
|
+
}
|
|
46
|
+
return { isAdmin, raw: r?.json ?? null };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 深度扫描找 isAdmin 字段(键名大小写不敏感,取值 true/'true'/1 → true)。 */
|
|
50
|
+
function pickIsAdmin(obj) {
|
|
51
|
+
if (obj == null)
|
|
52
|
+
return undefined;
|
|
53
|
+
if (typeof obj === 'object') {
|
|
54
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
55
|
+
if (String(k).toLowerCase() === 'isadmin') {
|
|
56
|
+
return v === true || v === 'true' || v === '1' || v === 1;
|
|
57
|
+
}
|
|
58
|
+
if (v && typeof v === 'object') {
|
|
59
|
+
const r = pickIsAdmin(v);
|
|
60
|
+
if (r !== undefined)
|
|
61
|
+
return r;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
31
68
|
// 人员类标签(→ userid 或 username)
|
|
32
69
|
const PERSON_LABEL = /申请人|发起人|续签人|经办人|经手人|创建人|人员|姓名|联系人|负责人|填报人/;
|
|
33
70
|
// 部门类标签(→ deptid 或 deptname)
|
package/dist/workplan.js
CHANGED
|
@@ -389,6 +389,43 @@ export async function importWorkplanExcel(cfg, opts = {}) {
|
|
|
389
389
|
const result = parseWorkplanImportResult(response.json);
|
|
390
390
|
return { imported: result.success, filePath, fileid: String(fileid), ...result };
|
|
391
391
|
}
|
|
392
|
+
// ─── 导出日程(Excel)────────────────────────────────────
|
|
393
|
+
/**
|
|
394
|
+
* 导出日程为 xls。走 cookie(导出接口无 aiAgentToken),GET 直接返回附件流。
|
|
395
|
+
* endpoint:exportExcelList=日程清单 / exportExcel4Html=日程一览表。
|
|
396
|
+
* 参数:bywhat(1日/2周/3月)、selectDate、objType、objIds、workPlanType。
|
|
397
|
+
*/
|
|
398
|
+
export async function exportWorkplanExcel(cfg, opts = {}) {
|
|
399
|
+
const endpoint = opts.type === 'html' ? 'exportExcel4Html' : 'exportExcelList';
|
|
400
|
+
const params = new URLSearchParams({
|
|
401
|
+
bywhat: opts.bywhat ?? '3',
|
|
402
|
+
selectDate: opts.selectDate ?? todayStr(),
|
|
403
|
+
objType: opts.objType ?? '1',
|
|
404
|
+
objIds: opts.objIds ?? '',
|
|
405
|
+
workPlanType: opts.workPlanType ?? '',
|
|
406
|
+
_t: String(Date.now()),
|
|
407
|
+
});
|
|
408
|
+
const api = await createApiFromProfile(cfg);
|
|
409
|
+
const headers = await api.getHeaders(undefined, { forceCookie: true });
|
|
410
|
+
delete headers['Content-Type'];
|
|
411
|
+
const url = `${api.baseUrl}/api/workplan/calendar/${endpoint}?${params.toString()}`;
|
|
412
|
+
const response = await fetch(url, { headers, signal: AbortSignal.timeout(60000) });
|
|
413
|
+
if (!response.ok)
|
|
414
|
+
throw new Error(`日程导出失败:HTTP ${response.status}`);
|
|
415
|
+
const buf = Buffer.from(await response.arrayBuffer());
|
|
416
|
+
const disposition = response.headers.get('content-disposition') || '';
|
|
417
|
+
const m = disposition.match(/filename\*?=(?:UTF-8''|")?([^";]+)/i);
|
|
418
|
+
let filename = m ? m[1].trim() : `日程导出-${opts.selectDate ?? todayStr()}.xls`;
|
|
419
|
+
if (filename.startsWith('"') && filename.endsWith('"'))
|
|
420
|
+
filename = filename.slice(1, -1);
|
|
421
|
+
try { filename = decodeURIComponent(filename); } catch { /* 保留原样 */ }
|
|
422
|
+
const outDir = path.resolve(opts.outDir || path.join(os.homedir(), 'Downloads'));
|
|
423
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
424
|
+
const outPath = nextAvailablePath(outDir, filename);
|
|
425
|
+
fs.writeFileSync(outPath, buf);
|
|
426
|
+
return { downloaded: true, outPath, filename, size: buf.length, endpoint };
|
|
427
|
+
}
|
|
428
|
+
|
|
392
429
|
// ─── 工具函数 ────────────────────────────────────────────
|
|
393
430
|
function stringify(v) { if (v === undefined || v === null || v === '')
|
|
394
431
|
return undefined; return String(v); }
|
package/package.json
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "weaver-e9-assistant-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "泛微 e-cology E9 HTTP 能力 CLI 封装(待办、微博、新建流程、邮件、公文、日程、考勤、建模台账)",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"e9": "dist/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"engines": {
|
|
10
|
-
"node": ">=18"
|
|
11
|
-
},
|
|
12
|
-
"optionalDependencies": {
|
|
13
|
-
"playwright": "^1.
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"start": "node dist/cli.js",
|
|
20
|
-
"e9": "node dist/cli.js",
|
|
21
|
-
"build": "node --check dist/cli.js && node --check dist/cube.js && node --check dist/workplan.js && node --check dist/fileutil.js && node --check dist/kq.js && node --check dist/links.js && node --check dist/report.js && node --check dist/workflow.js && node --check dist/parse.js && node --check dist/todosubmit.js && node --check dist/wfsummary.js && node --check dist/wfstart.js && node --check dist/submissionplan.js && node --check dist/submissionstate.js && node --check dist/auth-token.js && node --check dist/todobatch.js && node --check dist/pageopen.js && node --check dist/createcontext.js && node --check dist/querystate.js && node --check dist/wfops.js",
|
|
22
|
-
"test": "node --test tests
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "weaver-e9-assistant-cli",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "泛微 e-cology E9 HTTP 能力 CLI 封装(待办、微博、新建流程、邮件、公文、日程、考勤、建模台账)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"e9": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"optionalDependencies": {
|
|
13
|
+
"playwright": "^1.63.0"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node dist/cli.js",
|
|
20
|
+
"e9": "node dist/cli.js",
|
|
21
|
+
"build": "node --check dist/cli.js && node --check dist/cube.js && node --check dist/workplan.js && node --check dist/fileutil.js && node --check dist/kq.js && node --check dist/links.js && node --check dist/report.js && node --check dist/workflow.js && node --check dist/parse.js && node --check dist/todosubmit.js && node --check dist/wfsummary.js && node --check dist/wfstart.js && node --check dist/submissionplan.js && node --check dist/submissionstate.js && node --check dist/auth-token.js && node --check dist/todobatch.js && node --check dist/pageopen.js && node --check dist/createcontext.js && node --check dist/querystate.js && node --check dist/wfops.js && node --check dist/subordinate.js",
|
|
22
|
+
"test": "node --test \"tests/**/*.test.js\"",
|
|
23
|
+
"prepack": "npm test",
|
|
24
|
+
"release": "node scripts/release.js",
|
|
25
|
+
"dev": "node dist/cli.js"
|
|
26
|
+
}
|
|
27
|
+
}
|