weaver-e9-assistant-cli 1.1.5 → 1.1.11
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-token.js +4 -4
- package/dist/cache-store.js +73 -0
- package/dist/cli.js +714 -57
- package/dist/client.js +1 -1
- package/dist/cube.js +16 -38
- package/dist/formula.js +246 -0
- package/dist/hrm.js +33 -19
- package/dist/memory.js +968 -0
- package/dist/nodemap.js +232 -242
- package/dist/subordinate.js +86 -35
- package/dist/teamreport.js +77 -0
- package/dist/tododetail.js +516 -0
- package/dist/todosubmit.js +619 -26
- package/dist/util.js +21 -0
- package/dist/wfcodeblock.js +761 -0
- package/dist/wfcreate.js +29 -1
- package/dist/wfform.js +286 -67
- package/dist/wfstart.js +516 -81
- package/dist/wfsummary.js +1 -1
- package/dist/wfupload.js +48 -4
- package/dist/workflow.js +4 -2
- package/dist/workplan.js +21 -6
- package/package.json +2 -2
package/dist/wfsummary.js
CHANGED
|
@@ -84,6 +84,6 @@ export async function getFlowSummary(cfg, requestId) {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
function htmltypeLabel(ht) {
|
|
87
|
-
const m = { 1: '文本', 2: '多行文本', 3: '浏览选择', 4: '
|
|
87
|
+
const m = { 1: '文本', 2: '多行文本', 3: '浏览选择', 4: '复选框', 5: '选择框', 6: '附件' };
|
|
88
88
|
return m[ht] ?? `ht=${ht}`;
|
|
89
89
|
}
|
package/dist/wfupload.js
CHANGED
|
@@ -17,6 +17,24 @@ export function guessListType(filePath) {
|
|
|
17
17
|
const ext = (path.extname(filePath).slice(1) || '').toLowerCase();
|
|
18
18
|
return IMG_EXT.has(ext) ? 'img' : 'list';
|
|
19
19
|
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 字段的上传类型:E9 前端 `2===field.detailtype ? "img" : "list"`。
|
|
23
|
+
* 不能只看扩展名——.png 传到「附件」字段也应走 list。
|
|
24
|
+
*/
|
|
25
|
+
export function fieldListType(field) {
|
|
26
|
+
const detailtype = field?.detailtype ?? field?.raw?.detailtype;
|
|
27
|
+
return Number(detailtype) === 2 ? 'img' : 'list';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 写进表单字段的上传结果:
|
|
32
|
+
* - 附件字段(list)→ fileid(浏览器实测 hidden input 值为文档 id)
|
|
33
|
+
* - 图片字段(img) → imagefileid(缺失回退 fileid)
|
|
34
|
+
*/
|
|
35
|
+
export function uploadFieldValue(up, listType) {
|
|
36
|
+
return listType === 'img' ? (up.imagefileid || up.fileid) : up.fileid;
|
|
37
|
+
}
|
|
20
38
|
/**
|
|
21
39
|
* 上传本地文件到表单字段。返回 imagefileid(填进 field<id> 的值)。
|
|
22
40
|
* listType: 'img'(图片字段) / 'list'(附件字段),不传按扩展名猜。
|
|
@@ -25,7 +43,19 @@ export async function uploadFile(cfg, opts) {
|
|
|
25
43
|
const { workflowId, fieldId, filePath } = opts;
|
|
26
44
|
const listType = opts.listType ?? guessListType(filePath);
|
|
27
45
|
const category = opts.category ?? '21';
|
|
28
|
-
|
|
46
|
+
// 上传接口要求 loginuserid 与当前操作人一致;cookie-only 环境没有 userid 参数时从 loginuuids 取
|
|
47
|
+
const cookieUid = (() => {
|
|
48
|
+
const m = String(cfg.cookie ?? '').match(/loginuuids=([^;]*)/i);
|
|
49
|
+
if (!m)
|
|
50
|
+
return '';
|
|
51
|
+
try {
|
|
52
|
+
return decodeURIComponent(m[1]).split(',')[0].trim();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
})();
|
|
58
|
+
const loginuserid = opts.loginuserid ?? cookieUid ?? '1';
|
|
29
59
|
if (!fs.existsSync(filePath))
|
|
30
60
|
throw new Error(`文件不存在: ${filePath}`);
|
|
31
61
|
const buf = fs.readFileSync(filePath);
|
|
@@ -38,7 +68,9 @@ export async function uploadFile(cfg, opts) {
|
|
|
38
68
|
// 直接用 fetch + aiAgentToken(不走 E9Api,因 multipart 需自定义 body)
|
|
39
69
|
const sess = (await import('./auth-token.js')).AuthTokenSession.fromProfile(cfg);
|
|
40
70
|
const base = cfg.baseUrl.replace(/\/+$/, '');
|
|
41
|
-
|
|
71
|
+
// 字段 fileattr.docCategory 可能是完整的 ",,2";调用方也可能只传 "2",统一补前缀
|
|
72
|
+
const docCategory = String(category).startsWith(',') ? String(category) : `,,${category}`;
|
|
73
|
+
const url = `${base}/api/doc/upload/uploadFile2Doc?category=${encodeURIComponent(docCategory)}&workflowid=${workflowId}&listType=${listType}&wfTestStr=&uploadSrc=wfform&loginuserid=${loginuserid}`;
|
|
42
74
|
const fd = new FormData();
|
|
43
75
|
fd.append('name', filename);
|
|
44
76
|
fd.append('isFirstUploadFile', '9');
|
|
@@ -47,12 +79,24 @@ export async function uploadFile(cfg, opts) {
|
|
|
47
79
|
fd.append('secretLevel', '');
|
|
48
80
|
fd.append('ts', String(Date.now()));
|
|
49
81
|
fd.append('file', new Blob([buf], { type: mime }), filename);
|
|
50
|
-
|
|
82
|
+
// token 优先;cookie-only / token 失效时回退 Cookie(手工导 cookie 的环境上传附件依赖此路径)
|
|
83
|
+
const buildHeaders = (useToken) => useToken && sess.token
|
|
84
|
+
? { aiAgentToken: sess.token }
|
|
85
|
+
: { ...(cfg.cookie ? { Cookie: cfg.cookie } : {}), 'X-Requested-With': 'XMLHttpRequest' };
|
|
86
|
+
let r = await fetch(url, {
|
|
51
87
|
method: 'POST',
|
|
52
|
-
headers:
|
|
88
|
+
headers: buildHeaders(!!sess.token),
|
|
53
89
|
body: fd,
|
|
54
90
|
signal: AbortSignal.timeout(60000),
|
|
55
91
|
});
|
|
92
|
+
if (!r.ok && sess.token && cfg.cookie) {
|
|
93
|
+
r = await fetch(url, {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers: buildHeaders(false),
|
|
96
|
+
body: fd,
|
|
97
|
+
signal: AbortSignal.timeout(60000),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
56
100
|
const t = await r.text();
|
|
57
101
|
let j = null;
|
|
58
102
|
try {
|
package/dist/workflow.js
CHANGED
|
@@ -222,6 +222,8 @@ export async function getWfList(cfg, opts) {
|
|
|
222
222
|
const expectedPages = Math.ceil((Number(total) || 0) / effectivePageSize);
|
|
223
223
|
if (expectedPages > 500)
|
|
224
224
|
throw new Error(`负向筛选需读取 ${expectedPages} 页,超过 500 页安全上限;请先增加其它筛选条件。`);
|
|
225
|
+
// ⚠ 不能并发翻页:同一 dataKey 的 table/datas 并发请求在服务端会偶发返回空页,
|
|
226
|
+
// 导致「收不全 → 误报失败」。必须串行。
|
|
225
227
|
for (let current = 2; current <= expectedPages; current++) {
|
|
226
228
|
const next = await api.post('/api/ec/dev/table/datas', {
|
|
227
229
|
dataKey, current: String(current), sortParams: parseSortSpec(opts.sort), pageSize: '100',
|
|
@@ -243,8 +245,8 @@ export async function getWfList(cfg, opts) {
|
|
|
243
245
|
const filteredTotal = opts.excludeCreatorId ? items.length : total;
|
|
244
246
|
if (opts.totalOnly)
|
|
245
247
|
items = [];
|
|
246
|
-
//
|
|
247
|
-
if (category === 'todo' && !opts.totalOnly) {
|
|
248
|
+
// 待办才取当前处理人(已办/我发起无未操作者概念);--no-operators 可跳过(该接口在服务端较慢)
|
|
249
|
+
if (category === 'todo' && !opts.totalOnly && opts.withOperators !== false) {
|
|
248
250
|
try {
|
|
249
251
|
const datasPayload = JSON.stringify(rows.map((r) => ({
|
|
250
252
|
requestid: r?.requestid ?? r?.randomField2 ?? '',
|
package/dist/workplan.js
CHANGED
|
@@ -170,19 +170,34 @@ export async function searchWorkplan(cfg, opts = {}) {
|
|
|
170
170
|
if (!dataKey)
|
|
171
171
|
throw new Error(`workplan search 未拿到 dataKey,getlist 响应: ${(r1.text ?? '').slice(0, 200)}`);
|
|
172
172
|
const page = opts.page ?? 1;
|
|
173
|
+
// ★ 保持串行:首条请求要完成 TLS 握手(TLS 1.3 实测约 2.3s;CLI 入口已默认压到 1.2),
|
|
174
|
+
// 后续请求复用同一连接仅约 50~150ms。并发发 datas+counts 会新开连接、再付一次握手
|
|
175
|
+
// (实测三段串行 2.6s → 并发 4.9s),故不并行。
|
|
173
176
|
const r2 = await api.post('/api/ec/dev/table/datas', { dataKey, current: String(page), sortParams: '[]', pageSize: '10' });
|
|
174
177
|
const columns = extractColumns(r2.json);
|
|
175
178
|
const rows = extractRows(r2.json);
|
|
176
179
|
const items = rows.map((r) => normalizeSearchRow(r));
|
|
177
180
|
const pageSize = pickByPaths(r2.json, ['pageSize', 'data.pageSize']);
|
|
178
|
-
|
|
181
|
+
const effectivePageSize = typeof pageSize === 'number' && pageSize > 0 ? pageSize : 10;
|
|
182
|
+
let total = extractTotal(r2.json);
|
|
179
183
|
let countsResp = null;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
// 省掉 counts 请求的两种情况:
|
|
185
|
+
// 1) datas 自带总数;
|
|
186
|
+
// 2) 首页且未填满一页 → 总数就是本页条数(不可能还有下一页);
|
|
187
|
+
// 3) 调用方显式 withCount=false。
|
|
188
|
+
const needCounts = total == null
|
|
189
|
+
&& !(page === 1 && items.length < effectivePageSize)
|
|
190
|
+
&& opts.withCount !== false;
|
|
191
|
+
if (needCounts) {
|
|
192
|
+
try {
|
|
193
|
+
const r3 = await api.post('/api/ec/dev/table/counts', { dataKey });
|
|
194
|
+
countsResp = r3.json;
|
|
195
|
+
total = extractTotal(r3.json);
|
|
196
|
+
}
|
|
197
|
+
catch { /* counts 失败不阻断 */ }
|
|
184
198
|
}
|
|
185
|
-
|
|
199
|
+
if (total == null && page === 1 && items.length < effectivePageSize)
|
|
200
|
+
total = items.length;
|
|
186
201
|
return { dataKey, page, total, pageSize: typeof pageSize === 'number' ? pageSize : null, items, raw: { getlist: r1.json, datas: r2.json, counts: countsResp } };
|
|
187
202
|
}
|
|
188
203
|
export function normalizeSearchRow(row) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weaver-e9-assistant-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"description": "泛微 e-cology E9 HTTP 能力 CLI 封装(待办、微博、新建流程、邮件、公文、日程、考勤、建模台账)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"start": "node dist/cli.js",
|
|
20
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 && node --check dist/hrm.js && node --check dist/supervise.js && node --check dist/wfagent.js && node --check dist/nodemap.js",
|
|
21
|
+
"build": "node --check dist/cache-store.js && node --check dist/formula.js && node --check dist/teamreport.js && 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/tododetail.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 && node --check dist/hrm.js && node --check dist/supervise.js && node --check dist/wfagent.js && node --check dist/nodemap.js && node --check dist/wfcodeblock.js",
|
|
22
22
|
"test": "node --test \"tests/**/*.test.js\"",
|
|
23
23
|
"prepack": "npm test",
|
|
24
24
|
"release": "node scripts/release.js",
|