weaver-e9-assistant-cli 1.1.4 → 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/README.md +2 -0
- package/dist/auth-token.js +4 -4
- package/dist/cache-store.js +73 -0
- package/dist/cli.js +1508 -69
- 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/kq.js +65 -0
- package/dist/memory.js +968 -0
- package/dist/nodemap.js +232 -0
- package/dist/odoc.js +7 -2
- package/dist/parse.js +4 -0
- package/dist/subordinate.js +111 -35
- package/dist/supervise.js +152 -0
- package/dist/teamreport.js +77 -0
- package/dist/todobatch.js +20 -9
- package/dist/tododetail.js +516 -0
- package/dist/todosubmit.js +657 -17
- package/dist/util.js +21 -0
- package/dist/wfagent.js +538 -0
- package/dist/wfcodeblock.js +761 -0
- package/dist/wfcreate.js +29 -1
- package/dist/wfform.js +286 -67
- package/dist/wfstart.js +518 -79
- 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/util.js
CHANGED
|
@@ -17,6 +17,27 @@ export function nowTimeStr() {
|
|
|
17
17
|
export function sleep(ms) {
|
|
18
18
|
return new Promise((r) => setTimeout(r, ms));
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* 并发受限的 map:保持返回顺序与 items 一致,最多 limit 个任务同时执行。
|
|
22
|
+
* 用于组织树/多页数据等「彼此独立但数量可能很大」的请求,避免串行排队拖慢,
|
|
23
|
+
* 也避免一次性打满服务端。
|
|
24
|
+
*/
|
|
25
|
+
export async function mapLimit(items, limit, fn) {
|
|
26
|
+
const list = Array.from(items ?? []);
|
|
27
|
+
const results = new Array(list.length);
|
|
28
|
+
const max = Math.max(1, Number(limit) || 1);
|
|
29
|
+
let next = 0;
|
|
30
|
+
const worker = async () => {
|
|
31
|
+
while (true) {
|
|
32
|
+
const i = next++;
|
|
33
|
+
if (i >= list.length)
|
|
34
|
+
return;
|
|
35
|
+
results[i] = await fn(list[i], i);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
await Promise.all(Array.from({ length: Math.min(max, list.length) }, worker));
|
|
39
|
+
return results;
|
|
40
|
+
}
|
|
20
41
|
/** 非空字符串化:null/undefined/'' → undefined,否则 String(v)。 */
|
|
21
42
|
export function stringify(v) {
|
|
22
43
|
if (v === undefined || v === null || v === '')
|
package/dist/wfagent.js
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E9 流程代理(workflow agent)模块。
|
|
3
|
+
*
|
|
4
|
+
* 链路(HAR 抓包实证 2026-10-14 `workflow流程代理.har` + 实跑校准):
|
|
5
|
+
* 1) POST /api/workflow/agent/getList body{agented,agentFlag,+过滤项,current} → sessionkey(**恒顶层**)
|
|
6
|
+
* 2) POST /api/ec/dev/table/datas body{dataKey=sessionkey,current,sortParams,pageSize} → columns + datas
|
|
7
|
+
* 3) POST /api/ec/dev/table/counts body{dataKey} → {count}
|
|
8
|
+
* 4) POST /api/workflow/agent/getSearchCondition body{agented,agentFlag,menuIds,menuPathIds} → 查询条件定义(含 SELECT options)
|
|
9
|
+
* 5) POST /api/workflow/agent/getAddAgentInfo body{symbol:'addAgent'} → {formValue,fields}(取默认被代理人)
|
|
10
|
+
* 6) POST /api/workflow/agent/agentTemplateList body{name,workflowid,agenterid,current} → sessionkey → 同 2/3 步
|
|
11
|
+
* 7) POST /api/workflow/agent/verifyAgentOverlap body{保存体+method} → {overlapIds,overlapCount}
|
|
12
|
+
* 8) POST /api/workflow/agent/saveAgentTemplate body{保存体+templatename} → {api_status:true}
|
|
13
|
+
* 9) POST /api/workflow/agent/addAgentSave body{保存体+method} → {agentresult:'success'}
|
|
14
|
+
* 10) POST /api/workflow/agent/getAgentDetail body{agentid,symbol:'edit'} → {agentid,datas,formValue,wfInfo,...}(编辑取详情)
|
|
15
|
+
* 11) POST /api/workflow/agent/saveAgentDetail body{详情回吐体} → {agentresult:'success'}(编辑保存)
|
|
16
|
+
* 12) POST /api/workflow/agent/takeBackAgent body{symbol:'it',agentid} / {symbol:'pt',agentid,bagentuid} → {flag:true}(收回)
|
|
17
|
+
* 13) POST /api/workflow/agent/getBatchEditInfo body 空 → {fields}(批量编辑弹窗字段:仅起止日期/时间)
|
|
18
|
+
* 14) POST /api/workflow/agent/saveBatchEditInfo body{method:'editBatch',agentids,起止日期/时间} → {agentresult:'success'}(批量改期)
|
|
19
|
+
*
|
|
20
|
+
* 保存链路顺序(抓包实证):saveAgentTemplate(可选)→ verifyAgentOverlap → addAgentSave;
|
|
21
|
+
* 编辑同理:verifyAgentOverlap → saveAgentDetail。
|
|
22
|
+
*
|
|
23
|
+
* 三视图 agented × agentFlag(2026-10-14 实跑核实总数 2 / 67 / 20):
|
|
24
|
+
* mine agented=0 agentFlag=0 我设置的代理
|
|
25
|
+
* all agented=0 agentFlag=1 全部代理设置(多一列 beagenterid)
|
|
26
|
+
* records agented=1 agentFlag=0 代理办理记录(代理产生的流程,行带 requestname/requestid)
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ 未实现(E9 有这些接口、但**未抓到包**,不猜参数):
|
|
29
|
+
* deleteAgentTemplate(模板删除)、takeBackPromptMsg(收回前提示)。
|
|
30
|
+
* ⚠️ **一行 = 一个流程**:`workflow_agent` 表带 `workflowid` 列(表结构实证),
|
|
31
|
+
* 抓包里的 `rangetype=567,531`(2 个流程)也确实落成 2 行。
|
|
32
|
+
* 因此 `agentrange=2`(全部流程)会**为每个流程各建一条**——2026-10-14 实测本环境一次建出 466 条!
|
|
33
|
+
* 只想试一条务必用 `--workflow <单个流程id>`。
|
|
34
|
+
*
|
|
35
|
+
* 四个开关语义(2026-10-14 用 `workflow_agent` 表结构校准,非推测):
|
|
36
|
+
* isCreateAgenter → iscreateagenter 「是否代理创建权限」
|
|
37
|
+
* isProxyDeal → isproxydeal 「流转中的待办事宜」
|
|
38
|
+
* isPendThing → ispending 「是否要把待办事宜转给代理人」
|
|
39
|
+
* isSysCreateAgenter → isSysCreateAgenter「系统创建代理标记」
|
|
40
|
+
* 默认值 1 照抄抓包样本,可用命令行覆盖。
|
|
41
|
+
*/
|
|
42
|
+
import { endpointFor } from './config.js';
|
|
43
|
+
import { createApiFromProfile } from './client.js';
|
|
44
|
+
import { extractRows, extractColumns, extractTotal, pickByPaths, stripHtml } from './parse.js';
|
|
45
|
+
|
|
46
|
+
/** 流程代理菜单 id(抓包实证:menuIds=1,86)。 */
|
|
47
|
+
const WFAGENT_MENU = { menuIds: '1,86', menuPathIds: '1,86' };
|
|
48
|
+
|
|
49
|
+
/** 视图 → agented/agentFlag(实跑核实)。 */
|
|
50
|
+
export const WFAGENT_VIEWS = {
|
|
51
|
+
mine: { agented: '0', agentFlag: '0', title: '我设置的代理' },
|
|
52
|
+
all: { agented: '0', agentFlag: '1', title: '全部代理设置' },
|
|
53
|
+
records: { agented: '1', agentFlag: '0', title: '代理办理记录' },
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** 代理状态筛选(getSearchCondition 的 options 实证)。 */
|
|
57
|
+
export const WFAGENT_STATUS = {
|
|
58
|
+
'全部': '0', '未结束': '1', '未开始': '2', '代理中': '3', '收回中': '5', '已结束': '4',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** 视图名 → 视图定义;接受 mine/all/records 或中文别名,未知返回 undefined。 */
|
|
62
|
+
export function resolveView(spec) {
|
|
63
|
+
const s = String(spec ?? '').trim();
|
|
64
|
+
if (!s)
|
|
65
|
+
return 'mine';
|
|
66
|
+
const low = s.toLowerCase();
|
|
67
|
+
if (WFAGENT_VIEWS[low])
|
|
68
|
+
return low;
|
|
69
|
+
if (/^[012]$/.test(s))
|
|
70
|
+
return undefined; // 视图不是一个数字,别误判
|
|
71
|
+
if (/代理办理|办理记录|记录|日志/.test(s))
|
|
72
|
+
return 'records';
|
|
73
|
+
if (/全部|所有/.test(s))
|
|
74
|
+
return 'all';
|
|
75
|
+
if (/我的|我设置|自己/.test(s))
|
|
76
|
+
return 'mine';
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 状态名 → agentstatus 值;接受中文名或数字直传,空返回 ''。
|
|
82
|
+
* ⚠️ 空串(不传 agentstatus)时服务端返回的是**全部**(含已结束),**不是** OA 表单默认的「未结束」(实测)。
|
|
83
|
+
*/
|
|
84
|
+
export function resolveStatus(spec) {
|
|
85
|
+
const s = String(spec ?? '').trim();
|
|
86
|
+
if (!s)
|
|
87
|
+
return '';
|
|
88
|
+
if (/^\d+$/.test(s))
|
|
89
|
+
return s;
|
|
90
|
+
const key = Object.keys(WFAGENT_STATUS).find((k) => k === s || k.includes(s));
|
|
91
|
+
return key ? WFAGENT_STATUS[key] : undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 组装保存体(verifyAgentOverlap / addAgentSave 通用)。**纯函数**,便于单测。
|
|
96
|
+
* agentrange:1=选择(配合 rangetype 流程列表)/ 2=全部(getAddAgentInfo.fields.agentrange.options 实证);
|
|
97
|
+
* 未指定流程 → 全部。
|
|
98
|
+
* 必填缺失/范围为空时抛错(写操作宁可不出手)。
|
|
99
|
+
*/
|
|
100
|
+
export function buildAgentBody(opts = {}) {
|
|
101
|
+
const agentUid = String(opts.agentUid ?? '').trim();
|
|
102
|
+
const beAgentUid = String(opts.beAgentUid ?? '').trim();
|
|
103
|
+
const beginDate = String(opts.beginDate ?? '').trim();
|
|
104
|
+
const endDate = String(opts.endDate ?? '').trim();
|
|
105
|
+
if (!agentUid)
|
|
106
|
+
throw new Error('缺少代理人(--agent)。');
|
|
107
|
+
if (!beAgentUid)
|
|
108
|
+
throw new Error('缺少被代理人(--be-agent)。');
|
|
109
|
+
if (!beginDate || !endDate)
|
|
110
|
+
throw new Error('缺少代理起止日期(--begin / --end,YYYY-MM-DD)。');
|
|
111
|
+
const workflowIds = String(opts.workflowIds ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
112
|
+
const wantAll = opts.range === undefined || opts.range === null || opts.range === ''
|
|
113
|
+
? workflowIds.length === 0
|
|
114
|
+
: /^(全部|所有|all)$/i.test(String(opts.range));
|
|
115
|
+
const agentrange = wantAll ? '2' : '1';
|
|
116
|
+
if (agentrange === '1' && workflowIds.length === 0)
|
|
117
|
+
throw new Error('agentrange=选择 但没有流程范围:请给 --workflow <流程id,id> 或改用 --range 全部。');
|
|
118
|
+
// 四个开关:默认 1(照抄抓包样本);语义未校准,见文件头。
|
|
119
|
+
const sw = (v) => (v === undefined || v === null || v === '' ? '1' : String(v));
|
|
120
|
+
return {
|
|
121
|
+
method: 'add',
|
|
122
|
+
beginDate,
|
|
123
|
+
endDate,
|
|
124
|
+
isCreateAgenter: sw(opts.isCreateAgenter),
|
|
125
|
+
isProxyDeal: sw(opts.isProxyDeal),
|
|
126
|
+
rangetype: agentrange === '1' ? workflowIds.join(',') : '',
|
|
127
|
+
bagentuid: beAgentUid,
|
|
128
|
+
agentuid: agentUid,
|
|
129
|
+
beginTime: String(opts.beginTime ?? ''),
|
|
130
|
+
endTime: String(opts.endTime ?? ''),
|
|
131
|
+
isSysCreateAgenter: sw(opts.isSysCreateAgenter),
|
|
132
|
+
isPendThing: sw(opts.isPendThing),
|
|
133
|
+
agentrange,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 代理列表。getList → table/datas → table/counts(同待办 table 链路)。
|
|
139
|
+
* opts: { view, status, agent, beAgent, wftype, workflowPath, beginFrom/beginTo, endFrom/endTo, dept, subcom, page, pageSize }
|
|
140
|
+
*/
|
|
141
|
+
export async function getAgentList(cfg, opts = {}) {
|
|
142
|
+
const api = await createApiFromProfile(cfg);
|
|
143
|
+
const view = WFAGENT_VIEWS[opts.view ?? 'mine'] ?? WFAGENT_VIEWS.mine;
|
|
144
|
+
const page = Number(opts.page ?? '1') || 1;
|
|
145
|
+
const body = {
|
|
146
|
+
bagentuid: opts.beAgent ?? '',
|
|
147
|
+
agentuid: opts.agent ?? '',
|
|
148
|
+
wftype: opts.wftype ?? '',
|
|
149
|
+
wfidbytype: opts.workflowPath ?? '',
|
|
150
|
+
// selectXXXDate:0=全部 / 1=今天 / 2=本周 / … / 6=指定日期范围(getSearchCondition 的 options 实证)
|
|
151
|
+
// ⚠️ 给了显式起止日期必须用 6(曾误用 1=今天,会静默筛成"今天")
|
|
152
|
+
selectBeginDate: opts.beginFrom ? '6' : '0',
|
|
153
|
+
begindatefrom: opts.beginFrom ?? '',
|
|
154
|
+
begindateto: opts.beginTo ?? opts.beginFrom ?? '',
|
|
155
|
+
selectEndDate: opts.endFrom ? '6' : '0',
|
|
156
|
+
enddatefrom: opts.endFrom ?? '',
|
|
157
|
+
enddateto: opts.endTo ?? opts.endFrom ?? '',
|
|
158
|
+
agentstatus: opts.status ?? '',
|
|
159
|
+
aiddepartmentid: opts.dept ?? '',
|
|
160
|
+
aidsubcompanyid: opts.subcom ?? '',
|
|
161
|
+
current: String(page),
|
|
162
|
+
agented: view.agented,
|
|
163
|
+
agentFlag: view.agentFlag,
|
|
164
|
+
};
|
|
165
|
+
const r1 = await api.post('/api/workflow/agent/getList', body);
|
|
166
|
+
const sessionkey = pickByPaths(r1.json, ['sessionkey', 'data.sessionkey']);
|
|
167
|
+
if (!sessionkey)
|
|
168
|
+
throw new Error(`未拿到代理列表 sessionkey(getList 响应:${(r1.text ?? '').slice(0, 200)})`);
|
|
169
|
+
const r2 = await api.post('/api/ec/dev/table/datas', { dataKey: sessionkey, current: String(page), sortParams: '[]', pageSize: String(opts.pageSize ?? '10') });
|
|
170
|
+
const columns = extractColumns(r2.json);
|
|
171
|
+
const items = extractRows(r2.json).map((row) => normalizeAgentRow(row, columns));
|
|
172
|
+
let total = null;
|
|
173
|
+
let countsRaw = null;
|
|
174
|
+
try {
|
|
175
|
+
const r3 = await api.post('/api/ec/dev/table/counts', { dataKey: sessionkey });
|
|
176
|
+
countsRaw = r3.json;
|
|
177
|
+
total = extractTotal(r3.json);
|
|
178
|
+
}
|
|
179
|
+
catch { /* counts 失败不阻断 */ }
|
|
180
|
+
let pageSize = pickByPaths(r2.json, ['pageSize', 'data.pageSize']);
|
|
181
|
+
if (typeof pageSize !== 'number' && /^\d+$/.test(String(pageSize ?? '')))
|
|
182
|
+
pageSize = parseInt(String(pageSize), 10);
|
|
183
|
+
return { view: opts.view ?? 'mine', viewTitle: view.title, page, total, pageSize: pageSize ?? null, items, sessionkey, columns, raw: { getList: r1.json, datas: r2.json, counts: countsRaw } };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 规范化一行代理数据(可读值优先取 `<field>span`,回退原值)。
|
|
188
|
+
* mine/all 视图给的是代理配置(代理人/被代理人/流程路径/起止/状态);
|
|
189
|
+
* records 视图给的是代理产生的流程(标题/当前节点/经办人)。
|
|
190
|
+
*/
|
|
191
|
+
export function normalizeAgentRow(row, columns) {
|
|
192
|
+
if (!row || typeof row !== 'object')
|
|
193
|
+
return {};
|
|
194
|
+
void columns;
|
|
195
|
+
const get = (keys) => pickByPaths(row, keys);
|
|
196
|
+
const spanOr = (k, extra = []) => stripHtml(get([`${k}span`, k, ...extra]));
|
|
197
|
+
return {
|
|
198
|
+
// 代理配置(mine / all)
|
|
199
|
+
agentId: String(get(['randomField0', 'agentid']) ?? ''),
|
|
200
|
+
agent: spanOr('agenterid', ['agentname']),
|
|
201
|
+
agentUid: String(get(['agenterid']) ?? ''),
|
|
202
|
+
beAgent: spanOr('beagenterid', ['bagentuid']),
|
|
203
|
+
beAgentUid: String(get(['beagenterid', 'randomField1', 'bagentuid']) ?? ''),
|
|
204
|
+
operator: spanOr('operatorid', ['operator']),
|
|
205
|
+
workflowType: spanOr('workflowtype'),
|
|
206
|
+
workflowName: spanOr('workflowname'),
|
|
207
|
+
beginDate: spanOr('beginDate'),
|
|
208
|
+
endDate: spanOr('endDate'),
|
|
209
|
+
status: spanOr('agenttype'),
|
|
210
|
+
// 原始状态码:1=有效(配合起止日期再渲染成「未开始」/「代理中」)/ 0=已结束。
|
|
211
|
+
// ⚠️ 收回(@see takeBackAgent)是**把 agenttype 置 0**,行**不会从列表消失**——核验必须看这个码。
|
|
212
|
+
statusCode: String(get(['agenttype']) ?? ''),
|
|
213
|
+
// 代理办理记录(records)
|
|
214
|
+
requestId: String(get(['randomFieldId', 'randomField2']) ?? ''),
|
|
215
|
+
title: stripHtml(get(['requestnamespan'])) || String(get(['requestname']) ?? ''),
|
|
216
|
+
node: stripHtml(get(['currentnodeidspan', 'currentnodespan'])) || String(get(['currentnodeid']) ?? ''),
|
|
217
|
+
handler: spanOr('userid', ['username']),
|
|
218
|
+
principal: spanOr('agentorbyagentid'),
|
|
219
|
+
creator: spanOr('creater'),
|
|
220
|
+
createTime: spanOr('createdate', ['createtime']),
|
|
221
|
+
// ⚠️ 只认 workflowid:mine/all 视图的 randomField0 是**代理记录 id**,不能当流程 id 用
|
|
222
|
+
workflowId: String(get(['workflowid']) ?? ''),
|
|
223
|
+
raw: row,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** 规范化一行代理模板(列:name/workflowrange/agenterid/randomFieldId,抓包实证)。 */
|
|
228
|
+
export function normalizeTemplateRow(row) {
|
|
229
|
+
if (!row || typeof row !== 'object')
|
|
230
|
+
return {};
|
|
231
|
+
const get = (keys) => pickByPaths(row, keys);
|
|
232
|
+
const spanOr = (k, extra = []) => stripHtml(get([`${k}span`, k, ...extra]));
|
|
233
|
+
return {
|
|
234
|
+
templateId: String(get(['randomFieldId']) ?? ''),
|
|
235
|
+
name: stripHtml(get(['namespan'])) || String(get(['name']) ?? ''),
|
|
236
|
+
workflowRange: spanOr('workflowrange') || '',
|
|
237
|
+
agent: spanOr('agenterid'),
|
|
238
|
+
agentUid: String(get(['agenterid']) ?? ''),
|
|
239
|
+
raw: row,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** 代理列表查询条件定义。POST /api/workflow/agent/getSearchCondition */
|
|
244
|
+
export async function getAgentConditions(cfg, opts = {}) {
|
|
245
|
+
const view = WFAGENT_VIEWS[opts.view ?? 'mine'] ?? WFAGENT_VIEWS.mine;
|
|
246
|
+
const api = await createApiFromProfile(cfg);
|
|
247
|
+
const r = await api.post('/api/workflow/agent/getSearchCondition', { agented: view.agented, agentFlag: view.agentFlag, ...WFAGENT_MENU });
|
|
248
|
+
const j = r.json ?? {};
|
|
249
|
+
const groups = (j.condition ?? []).map((g) => ({
|
|
250
|
+
title: String(g.title ?? ''),
|
|
251
|
+
items: (g.items ?? []).map((it) => ({
|
|
252
|
+
label: String(it.label ?? ''),
|
|
253
|
+
domkey: Array.isArray(it.domkey) ? it.domkey.join(',') : String(it.domkey ?? ''),
|
|
254
|
+
type: String(it.conditionType ?? ''),
|
|
255
|
+
options: (it.options ?? []).map((o) => ({ key: String(o.key ?? ''), name: String(o.showname ?? '') })),
|
|
256
|
+
})),
|
|
257
|
+
}));
|
|
258
|
+
return { groups, raw: j };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** 新建代理表单初始值。POST /api/workflow/agent/getAddAgentInfo(取默认被代理人 = 当前登录人)。 */
|
|
262
|
+
export async function getAddAgentInfo(cfg) {
|
|
263
|
+
const api = await createApiFromProfile(cfg);
|
|
264
|
+
const r = await api.post('/api/workflow/agent/getAddAgentInfo', { symbol: 'addAgent' });
|
|
265
|
+
const j = r.json ?? {};
|
|
266
|
+
const fv = j.formValue ?? {};
|
|
267
|
+
const field = (k) => j.fields?.[k] ?? {};
|
|
268
|
+
return {
|
|
269
|
+
defaultBeAgentUid: String(fv.bagentuid?.value ?? ''),
|
|
270
|
+
defaultBeAgent: String(fv.bagentuid?.valueSpan ?? ''),
|
|
271
|
+
// agentrange 选项(1=选择 / 2=全部)
|
|
272
|
+
rangeOptions: (field('agentrange').options ?? []).map((o) => ({ key: String(o.key ?? ''), name: String(o.showname ?? '') })),
|
|
273
|
+
raw: j,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** 代理模板列表(agentTemplateList → 同 3 步链路)。 */
|
|
278
|
+
export async function listAgentTemplates(cfg, opts = {}) {
|
|
279
|
+
const api = await createApiFromProfile(cfg);
|
|
280
|
+
const page = Number(opts.page ?? '1') || 1;
|
|
281
|
+
const r1 = await api.post('/api/workflow/agent/agentTemplateList', {
|
|
282
|
+
name: opts.name ?? '', workflowid: opts.workflowId ?? '', agenterid: opts.agentUid ?? '', current: String(page),
|
|
283
|
+
});
|
|
284
|
+
const sessionkey = pickByPaths(r1.json, ['sessionkey', 'data.sessionkey']);
|
|
285
|
+
if (!sessionkey)
|
|
286
|
+
throw new Error(`未拿到代理模板 sessionkey(agentTemplateList 响应:${(r1.text ?? '').slice(0, 200)})`);
|
|
287
|
+
const r2 = await api.post('/api/ec/dev/table/datas', { dataKey: sessionkey, current: String(page), sortParams: '[]', pageSize: String(opts.pageSize ?? '10') });
|
|
288
|
+
const columns = extractColumns(r2.json);
|
|
289
|
+
const items = extractRows(r2.json).map((row) => normalizeTemplateRow(row));
|
|
290
|
+
let total = null;
|
|
291
|
+
try {
|
|
292
|
+
total = extractTotal((await api.post('/api/ec/dev/table/counts', { dataKey: sessionkey })).json);
|
|
293
|
+
}
|
|
294
|
+
catch { /* counts 失败不阻断 */ }
|
|
295
|
+
return { page, total, items, columns, sessionkey, raw: { agentTemplateList: r1.json, datas: r2.json } };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** 重叠校验(保存前)。POST /api/workflow/agent/verifyAgentOverlap → {overlapIds, overlapCount} */
|
|
299
|
+
export async function verifyOverlap(cfg, body) {
|
|
300
|
+
const api = await createApiFromProfile(cfg);
|
|
301
|
+
const r = await api.post('/api/workflow/agent/verifyAgentOverlap', body);
|
|
302
|
+
const j = r.json ?? {};
|
|
303
|
+
return { overlapIds: String(j.overlapIds ?? ''), overlapCount: Number(j.overlapCount ?? 0) || 0, raw: j };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** 存为模板(可选步骤)。POST /api/workflow/agent/saveAgentTemplate → {api_status:true} */
|
|
307
|
+
export async function saveAgentTemplate(cfg, body, templateName) {
|
|
308
|
+
const api = await createApiFromProfile(cfg);
|
|
309
|
+
const r = await api.post('/api/workflow/agent/saveAgentTemplate', { ...body, templatename: String(templateName ?? '') });
|
|
310
|
+
return { ok: r.json?.api_status === true, raw: r.json };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 收回代理。POST /api/workflow/agent/takeBackAgent → {flag:true}
|
|
315
|
+
*
|
|
316
|
+
* 参数(2026-10-14 用户在 OA 点「收回此条代理」抓包实证):
|
|
317
|
+
* `symbol=it`(**必带**,就是它把单条收回与其它语义区分开)+ `agentid`(代理记录 id)+ `backRunning`(1=连流转中的一并收回)。
|
|
318
|
+
* ⚠️ 只传 `agentid` 会返回 `{flag:true}` 但**毫无效果**——失败静默,务必回读核验。
|
|
319
|
+
*
|
|
320
|
+
* 一次一条(UI 也是逐条发),多条循环发;每条都带 `symbol=it`。
|
|
321
|
+
* 与 doSupervise 同款闸门:未传 opts.send 只回显不执行(由 CLI 负责打印预览)。
|
|
322
|
+
*/
|
|
323
|
+
export async function takeBackAgent(cfg, opts = {}) {
|
|
324
|
+
const ep = endpointFor(cfg);
|
|
325
|
+
const agentIds = String(opts.agentIds ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
326
|
+
if (!agentIds.length)
|
|
327
|
+
throw new Error('takeBackAgent 需要代理记录 id(--agent-ids "69,70",即列表里的「代理id」)。');
|
|
328
|
+
const backRunning = opts.backRunning === undefined || opts.backRunning === '' ? '1' : String(opts.backRunning);
|
|
329
|
+
if (!opts.send)
|
|
330
|
+
return { sent: false, endpoint: { baseUrl: ep.baseUrl }, agentIds, backRunning };
|
|
331
|
+
const api = await createApiFromProfile(cfg);
|
|
332
|
+
const results = [];
|
|
333
|
+
for (let i = 0; i < agentIds.length; i++) {
|
|
334
|
+
const agentId = agentIds[i];
|
|
335
|
+
if (opts.onProgress)
|
|
336
|
+
opts.onProgress(i + 1, agentIds.length, agentId);
|
|
337
|
+
let flag = false;
|
|
338
|
+
let raw = null;
|
|
339
|
+
try {
|
|
340
|
+
const r = await api.post('/api/workflow/agent/takeBackAgent', { symbol: 'it', agentid: agentId, backRunning });
|
|
341
|
+
raw = r.json;
|
|
342
|
+
flag = r.json?.flag === true;
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
raw = { error: e.message };
|
|
346
|
+
}
|
|
347
|
+
results.push({ agentId, flag, raw });
|
|
348
|
+
}
|
|
349
|
+
return { sent: true, endpoint: { baseUrl: ep.baseUrl }, agentIds, backRunning, results, okCount: results.filter((r) => r.flag).length };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 取代理详情(编辑用)。POST /api/workflow/agent/getAgentDetail(body `agentid` + `symbol=edit`)
|
|
354
|
+
* → `{agentid, datas:[{...各行字段...}], formValue:{各顶层字段的当前值}, wfInfo, fields, isE9Rule, hrmBrowserParam}`。
|
|
355
|
+
* ⚠️ 编辑不是"重新填一张表",而是**取详情 → 改字段 → 原样回吐**(见 buildAgentEditBody)。
|
|
356
|
+
*/
|
|
357
|
+
export async function getAgentDetail(cfg, agentId) {
|
|
358
|
+
const id = String(agentId ?? '').trim();
|
|
359
|
+
if (!id)
|
|
360
|
+
throw new Error('取代理详情需要代理记录 id(列表里的「代理id」)。');
|
|
361
|
+
const api = await createApiFromProfile(cfg);
|
|
362
|
+
const r = await api.post('/api/workflow/agent/getAgentDetail', { agentid: id, symbol: 'edit' });
|
|
363
|
+
return parseAgentDetail(r.json ?? {}, id);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** 纯函数:把 getAgentDetail 响应归一化为 `{agentId, current, rows, raw}`(便于单测与复用)。 */
|
|
367
|
+
export function parseAgentDetail(json, fallbackId = '') {
|
|
368
|
+
const j = json ?? {};
|
|
369
|
+
const fv = j.formValue ?? {};
|
|
370
|
+
const rows = Array.isArray(j.datas) ? j.datas : [];
|
|
371
|
+
if (!rows.length)
|
|
372
|
+
throw new Error(`代理详情为空(agentid=${fallbackId || j.agentid})——该条可能已不存在。`);
|
|
373
|
+
return {
|
|
374
|
+
agentId: String(j.agentid ?? fallbackId),
|
|
375
|
+
current: {
|
|
376
|
+
beginDate: String(fv.beginDate?.value ?? ''),
|
|
377
|
+
endDate: String(fv.endDate?.value ?? ''),
|
|
378
|
+
beginTime: String(fv.beginTime?.value ?? ''),
|
|
379
|
+
endTime: String(fv.endTime?.value ?? ''),
|
|
380
|
+
beAgentUid: String(fv.bagentuid?.value ?? ''),
|
|
381
|
+
beAgent: String(fv.bagentuid?.valueSpan ?? ''),
|
|
382
|
+
workflowId: String(fv.workflowid?.value ?? ''),
|
|
383
|
+
workflowName: String(fv.workflowid?.valueSpan ?? j.wfInfo?.workflowname ?? ''),
|
|
384
|
+
},
|
|
385
|
+
rows,
|
|
386
|
+
raw: j,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* 组装编辑保存体(**纯函数**)。以详情里的当前值为底、套用覆盖项,再把每一行**原样**回吐为 `row_<i>_<字段>`。
|
|
392
|
+
* 配方已用抓包 body 逐字段比对验证(22/22 吻合)。
|
|
393
|
+
* overrides 可给:beginDate / endDate / beginTime / endTime / 四个开关。
|
|
394
|
+
*/
|
|
395
|
+
export function buildAgentEditBody(detail, overrides = {}) {
|
|
396
|
+
const cur = detail?.current ?? {};
|
|
397
|
+
const rows = detail?.rows ?? [];
|
|
398
|
+
const pick = (v, fallback) => (v === undefined || v === null || v === '' ? fallback : v);
|
|
399
|
+
const body = {
|
|
400
|
+
agentid: String(detail?.agentId ?? ''),
|
|
401
|
+
symbol: 'edit',
|
|
402
|
+
beginDate: pick(overrides.beginDate, cur.beginDate ?? ''),
|
|
403
|
+
endDate: pick(overrides.endDate, cur.endDate ?? ''),
|
|
404
|
+
bagentuid: String(cur.beAgentUid ?? ''),
|
|
405
|
+
beginTime: pick(overrides.beginTime, cur.beginTime ?? ''),
|
|
406
|
+
endTime: pick(overrides.endTime, cur.endTime ?? ''),
|
|
407
|
+
workflowid: String(cur.workflowId ?? ''),
|
|
408
|
+
rownum: String(rows.length || 1),
|
|
409
|
+
ismodifyCondition: 'false',
|
|
410
|
+
};
|
|
411
|
+
rows.forEach((row, i) => {
|
|
412
|
+
for (const [k, v] of Object.entries(row ?? {}))
|
|
413
|
+
body[`row_${i}_${k}`] = String(v ?? '');
|
|
414
|
+
});
|
|
415
|
+
const switches = {
|
|
416
|
+
isCreateAgenter: overrides.isCreateAgenter,
|
|
417
|
+
isProxyDeal: overrides.isProxyDeal,
|
|
418
|
+
isSysCreateAgenter: overrides.isSysCreateAgenter,
|
|
419
|
+
isPendThing: overrides.isPendThing,
|
|
420
|
+
};
|
|
421
|
+
for (const [k, v] of Object.entries(switches))
|
|
422
|
+
if (v !== undefined && v !== null && v !== '')
|
|
423
|
+
body[`row_0_${k}`] = String(v);
|
|
424
|
+
return body;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* 编辑代理保存。链路(抓包实证顺序):`verifyAgentOverlap` → `saveAgentDetail` → `{agentresult:'success'}`。
|
|
429
|
+
* 与 doSupervise 同款闸门:未传 opts.send 只做重叠校验 + 回显(由 CLI 打印改前→改后)。
|
|
430
|
+
*/
|
|
431
|
+
export async function editAgent(cfg, opts = {}) {
|
|
432
|
+
const ep = endpointFor(cfg);
|
|
433
|
+
const agentId = String(opts.agentId ?? '').trim();
|
|
434
|
+
if (!agentId)
|
|
435
|
+
throw new Error('编辑代理需要代理记录 id(--agent-id)。');
|
|
436
|
+
const detail = opts.detail ?? await getAgentDetail(cfg, agentId);
|
|
437
|
+
const body = buildAgentEditBody(detail, opts);
|
|
438
|
+
const overlap = await verifyOverlap(cfg, body);
|
|
439
|
+
if (!opts.send)
|
|
440
|
+
return { sent: false, endpoint: { baseUrl: ep.baseUrl }, detail, body, overlap };
|
|
441
|
+
const api = await createApiFromProfile(cfg);
|
|
442
|
+
const r = await api.post('/api/workflow/agent/saveAgentDetail', body);
|
|
443
|
+
return { sent: true, endpoint: { baseUrl: ep.baseUrl }, detail, body, overlap, result: String(r.json?.agentresult ?? ''), raw: r.json };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** 批量编辑弹窗的字段定义。POST /api/workflow/agent/getBatchEditInfo(**无 body**)→ `{fields:{...}}`(实测只有起止日期/时间 4 个)。 */
|
|
447
|
+
export async function getBatchEditInfo(cfg) {
|
|
448
|
+
const api = await createApiFromProfile(cfg);
|
|
449
|
+
const r = await api.post('/api/workflow/agent/getBatchEditInfo', {});
|
|
450
|
+
const j = r.json ?? {};
|
|
451
|
+
return { fields: Object.keys(j.fields ?? {}), raw: j };
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* 组装批量编辑保存体(**纯函数**)。
|
|
456
|
+
*
|
|
457
|
+
* ⚠️ 与**单条**编辑完全不同:批量编辑**只改起止日期/时间**(弹窗只有这 4 个字段,`getBatchEditInfo` 实证),
|
|
458
|
+
* **不需要**回吐 `row_0_*` 详情;body 就 6 个字段:`method=editBatch` + `agentids` + 起止日期/时间。
|
|
459
|
+
* ⚠️ 这 4 个字段是**整体覆盖**,且**日期留空会被填成 `1900-01-01`**(2026-10-15 实测)——
|
|
460
|
+
* 于是 `1900-01-01 ~ 未来日期` 直接覆盖"今天",代理**立刻变成生效中**。
|
|
461
|
+
* 故本函数**强制要求 `--begin` 与 `--end` 同时给**(时间可留空=全天),避免静默把开始日期打成 1900。
|
|
462
|
+
*/
|
|
463
|
+
export function buildBatchEditBody(opts = {}) {
|
|
464
|
+
const ids = String(opts.agentIds ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
465
|
+
if (ids.length < 2)
|
|
466
|
+
throw new Error('批量编辑至少需要 2 条代理(--agent-ids "537,538");只改一条请用 --agent-id 走单条编辑。');
|
|
467
|
+
const beginDate = String(opts.beginDate ?? '').trim();
|
|
468
|
+
const endDate = String(opts.endDate ?? '').trim();
|
|
469
|
+
if (!beginDate || !endDate)
|
|
470
|
+
throw new Error('批量编辑是**整体覆盖**,且日期留空会被填成 1900-01-01(代理会立刻生效)——'
|
|
471
|
+
+ '必须同时给 --begin 与 --end。只想改结束日期请用单条编辑(--agent-id)。');
|
|
472
|
+
return {
|
|
473
|
+
method: 'editBatch',
|
|
474
|
+
agentids: ids.join(','),
|
|
475
|
+
beginDate: String(opts.beginDate ?? ''),
|
|
476
|
+
endDate: String(opts.endDate ?? ''),
|
|
477
|
+
beginTime: String(opts.beginTime ?? ''),
|
|
478
|
+
endTime: String(opts.endTime ?? ''),
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/** 批量编辑保存。链路同单条:`verifyAgentOverlap`(method=editBatch)→ `saveBatchEditInfo`。 */
|
|
483
|
+
export async function editAgentBatch(cfg, opts = {}) {
|
|
484
|
+
const ep = endpointFor(cfg);
|
|
485
|
+
const body = buildBatchEditBody(opts);
|
|
486
|
+
const overlap = await verifyOverlap(cfg, body);
|
|
487
|
+
if (!opts.send)
|
|
488
|
+
return { sent: false, endpoint: { baseUrl: ep.baseUrl }, body, overlap };
|
|
489
|
+
const api = await createApiFromProfile(cfg);
|
|
490
|
+
const r = await api.post('/api/workflow/agent/saveBatchEditInfo', body);
|
|
491
|
+
return { sent: true, endpoint: { baseUrl: ep.baseUrl }, body, overlap, result: String(r.json?.agentresult ?? ''), raw: r.json };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* 收回**该被代理人名下的全部**代理(界面按钮「收回全部代理」)。
|
|
496
|
+
*
|
|
497
|
+
* POST /api/workflow/agent/takeBackAgent,body `symbol=pt` + **`agentid`(锚点)** + `bagentuid` + `backRunning`
|
|
498
|
+
* (2026-10-14 用户在 OA 点「收回全部代理」抓包实证;**`symbol=pt` 与单条的 `symbol=it` 是两种语义**)。
|
|
499
|
+
* ⚠️ **`agentid` 必须带**(2026-10-14 实验证实):只传 `symbol=pt`+`bagentuid` 时接口回 `{"flag":true}`,
|
|
500
|
+
* 但**毫无效果**(生效中的代理一条没少);补上任意一条属于该被代理人的 `agentid` 后立刻生效。
|
|
501
|
+
* 界面语义是「点哪行就用哪行的 id 当锚点」,故本函数在未指定 `opts.agentId` 时**自动取该被代理人名下
|
|
502
|
+
* 第一条生效中的代理**作为锚点;一条都没有则不发请求(没有可收回的东西)。
|
|
503
|
+
* ⚠️ 同样是「标记 agenttype=0」,行不会从列表消失。
|
|
504
|
+
*/
|
|
505
|
+
export async function takeBackAllAgent(cfg, opts = {}) {
|
|
506
|
+
const ep = endpointFor(cfg);
|
|
507
|
+
const beAgentUid = String(opts.beAgentUid ?? '').trim();
|
|
508
|
+
if (!beAgentUid)
|
|
509
|
+
throw new Error('收回全部需要被代理人 id(--be-agent <姓名|id>)。');
|
|
510
|
+
const backRunning = opts.backRunning === undefined || opts.backRunning === '' ? '1' : String(opts.backRunning);
|
|
511
|
+
let agentId = String(opts.agentId ?? '').trim();
|
|
512
|
+
if (!opts.send)
|
|
513
|
+
return { sent: false, endpoint: { baseUrl: ep.baseUrl }, beAgentUid, backRunning, agentId: agentId || null };
|
|
514
|
+
if (!agentId) {
|
|
515
|
+
// 找锚点:该被代理人名下第一条生效中的代理(界面就是拿点击行的 id)
|
|
516
|
+
const anchor = await getAgentList(cfg, { view: 'all', beAgent: beAgentUid, status: '1', page: 1 });
|
|
517
|
+
agentId = String(anchor.items?.[0]?.agentId ?? '');
|
|
518
|
+
if (!agentId)
|
|
519
|
+
return { sent: true, endpoint: { baseUrl: ep.baseUrl }, beAgentUid, backRunning, agentId: null, flag: false, noop: true };
|
|
520
|
+
}
|
|
521
|
+
const api = await createApiFromProfile(cfg);
|
|
522
|
+
const r = await api.post('/api/workflow/agent/takeBackAgent', { symbol: 'pt', agentid: agentId, bagentuid: beAgentUid, backRunning });
|
|
523
|
+
return { sent: true, endpoint: { baseUrl: ep.baseUrl }, beAgentUid, agentId, backRunning, flag: r.json?.flag === true, raw: r.json };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* 新建代理。POST /api/workflow/agent/addAgentSave → {agentresult:'success'}
|
|
528
|
+
* 与 doSupervise 同款闸门:未传 opts.send 只回显不落库(由 CLI 负责打印预览)。
|
|
529
|
+
*/
|
|
530
|
+
export async function addAgent(cfg, opts = {}) {
|
|
531
|
+
const ep = endpointFor(cfg);
|
|
532
|
+
const body = opts.body ?? buildAgentBody(opts);
|
|
533
|
+
if (!opts.send)
|
|
534
|
+
return { sent: false, endpoint: { baseUrl: ep.baseUrl }, body };
|
|
535
|
+
const api = await createApiFromProfile(cfg);
|
|
536
|
+
const r = await api.post('/api/workflow/agent/addAgentSave', body);
|
|
537
|
+
return { sent: true, endpoint: { baseUrl: ep.baseUrl }, body, result: String(r.json?.agentresult ?? ''), raw: r.json };
|
|
538
|
+
}
|