@stalfh233/omc-cli 0.4.60 → 0.4.61
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/coverage/m1-coverage-manifest-v1.json +9 -3
- package/dist/endpoints.js +3 -0
- package/dist/lanes/runtime-data.js +1 -1
- package/dist/lanes/workflow-contract.js +7 -2
- package/dist/lanes/workflow-coverage.js +1 -1
- package/dist/lanes/workflow.js +51 -1
- package/dist/recipes/workflow.js +1 -1
- package/dist/workflow-support.js +88 -2
- package/package.json +3 -3
- package/skills/omc-business-configuration.md +1 -1
- package/skills/omc-capabilities.md +3 -3
|
@@ -349,7 +349,7 @@
|
|
|
349
349
|
"capability": "W7 S29 runtime workflow work-item query (search_workitems)",
|
|
350
350
|
"status": "executable",
|
|
351
351
|
"scope": "GET /api/api/runtime/workflow/search_workitems (WorkItemSearchVO via @ModelAttribute; the server injects userId so the result is the caller's todo) returns PageVO<WorkItemVO>; per item bizObjectId/schemaCode resolved via GET /api/api/runtime/workflow/getBizObjectByWfInstanceId. Wire shape pinned from WorkflowInstanceController.searchWorkItems + WorkflowInstanceRuntimeController.handleSearchWorkItems + RuntimeController.searchWorkItems/WorkflowInstanceFacadeImpl.queryWorkItems. WorkItemSearchVO.filterWorkflowInstanceId is a NOT-equal exclusion on instanceId (BizWorkitemServiceImpl.toSearchWorkItemsCriterion:400-402), so exact instance matching is applied client-side.",
|
|
352
|
-
"excluded_or_unsupported_reason": "Live-proven 2026-09-11: startWorkflow -> workitem.list discovered workItemId 970fe802baba463d97c1d97f3695bb3a + bizObjectId c607b77af4ab4293a1f6ce314ec9327c -> approval submit -> list count 0 and record COMPLETED. Evidence in .scratch/omc-app-management/issues/06-workitem-list-capability.md.",
|
|
352
|
+
"excluded_or_unsupported_reason": "Live-proven 2026-09-11: startWorkflow -> workitem.list discovered workItemId 970fe802baba463d97c1d97f3695bb3a + bizObjectId c607b77af4ab4293a1f6ce314ec9327c -> approval submit -> list count 0 and record COMPLETED. Evidence in .scratch/omc-app-management/issues/06-workitem-list-capability.md. r4-training 2026-10-20: a stalled instance (data.submit -> PROCESSING, no next todo) exposed nothing; list_workflow_instance_activity (WorkflowMaintenanceAssembler.listActivateActivity) now backs workflow.instanceActivity with per-activity status/participants so \"where did it stop\" is a read, not a MySQL+container-log dig.",
|
|
353
353
|
"version_family": "8.6",
|
|
354
354
|
"anchor_version": "8.6.26",
|
|
355
355
|
"evidence": [
|
|
@@ -360,14 +360,20 @@
|
|
|
360
360
|
{
|
|
361
361
|
"path": ".scratch/omc-app-management/issues/06-workitem-list-capability.md",
|
|
362
362
|
"anchor": "静态取证"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"path": "packages/omc-cli/src/workflow-support.ts",
|
|
366
|
+
"anchor": "listInstanceActivities"
|
|
363
367
|
}
|
|
364
368
|
],
|
|
365
369
|
"lane": "W7-S29-greenfield",
|
|
366
370
|
"tools": [
|
|
367
|
-
"workitem.list"
|
|
371
|
+
"workitem.list",
|
|
372
|
+
"workflow.instanceActivity"
|
|
368
373
|
],
|
|
369
374
|
"scopes": [
|
|
370
|
-
"runtime workflow work-item query"
|
|
375
|
+
"runtime workflow work-item query",
|
|
376
|
+
"runtime workflow instance node-state diagnosis"
|
|
371
377
|
]
|
|
372
378
|
},
|
|
373
379
|
{
|
package/dist/endpoints.js
CHANGED
|
@@ -113,6 +113,9 @@ export const ENDPOINTS = {
|
|
|
113
113
|
workflowDelete: "/api/api/workflow/delete",
|
|
114
114
|
workflowSearchWorkItems: "/api/api/runtime/workflow/search_workitems",
|
|
115
115
|
workflowGetBizObjectByWfInstanceId: "/api/api/runtime/workflow/getBizObjectByWfInstanceId",
|
|
116
|
+
workflowListInstanceActivity: "/api/api/runtime/workflow/list_workflow_instance_activity",
|
|
117
|
+
workflowListFinishedWorkItems: "/api/api/runtime/workflow/list_finished_workitems",
|
|
118
|
+
workflowGetInstanceBaseInfo: "/api/api/runtime/workflow/get_instance_baseinfo",
|
|
116
119
|
organizationRoleList: "/api/api/organization/role/list",
|
|
117
120
|
organizationDepartmentTree: "/api/api/organization/department/tree",
|
|
118
121
|
organizationUserSearch: "/api/api/organization/user/search",
|
|
@@ -1152,7 +1152,7 @@ export function registerRuntimeDataLane(tools) {
|
|
|
1152
1152
|
code: "flow-no-next-workitem",
|
|
1153
1153
|
message: "提交成功但该实例没有生成下一节点待办,流程停在 PROCESSING。常见原因:审批参与人解析为空(角色无成员 / 成员未挂发起人部门 / 参与人函数未注册)。",
|
|
1154
1154
|
checks: [
|
|
1155
|
-
`omc call
|
|
1155
|
+
`omc call workflow.instanceActivity --args '{"workflowInstanceId":"${effectiveInstanceId}"}'(看每个节点的 status 与 participants;无 status=1 即停住)`,
|
|
1156
1156
|
"容器日志搜 GetParticipantInfo error / CustomParticipleMethod{...} -> []",
|
|
1157
1157
|
"参与人优先用参与者对象数组(角色 id);内置函数只认 FindUserByRole(org, roleCode[, order])",
|
|
1158
1158
|
],
|
|
@@ -47,7 +47,7 @@ export const WORKFLOW_WIRE = Object.freeze({
|
|
|
47
47
|
/** Workflow template object (workflow/update|publish raw body). */
|
|
48
48
|
export const workflowTemplateArg = z
|
|
49
49
|
.record(z.string(), z.unknown())
|
|
50
|
-
.describe(`workflow template object. Minimal valid shape (create via workflow.create first, then workflow.update with this, then workflow.publish): ${WORKFLOW_WIRE.templateShape}. Add approval activities between start/end (each with id/type/participant) and routes[] as needed. **参与人解析陷阱(r5-crm 实证 2026-10-10)**:内置 FindUserByRole({Originator.OU},code,...) 按部门范围找人——目标角色**不挂部门**(如全公司唯一的总监)时 ParticipantActivityRuntime.initParticipants 抛 EngineRuntimeException、流程卡 PROCESSING 无待办;跨部门/全局角色**不要写 FindUsersSpecByRoleFunction(code)**——1.1.25 运行期未注册该函数(FunctionNotFoundException,接口仍回 errCode 0),改用参与者对象数组(角色 id
|
|
50
|
+
.describe(`workflow template object. Minimal valid shape (create via workflow.create first, then workflow.update with this, then workflow.publish): ${WORKFLOW_WIRE.templateShape}. Add approval activities between start/end (each with id/type/participant) and routes[] as needed. **参与人解析陷阱(r5-crm 实证 2026-10-10)**:内置 FindUserByRole({Originator.OU},code,...) 按部门范围找人——目标角色**不挂部门**(如全公司唯一的总监)时 ParticipantActivityRuntime.initParticipants 抛 EngineRuntimeException、流程卡 PROCESSING 无待办;跨部门/全局角色**不要写 FindUsersSpecByRoleFunction(code)**——1.1.25 运行期未注册该函数(FunctionNotFoundException,接口仍回 errCode 0),改用参与者对象数组(角色 id)或给角色补上可覆盖的部门范围成员。解析为空(零成员/未挂部门/自由文本/角色码不存在)时引擎**把任务退回发起人**、流程继续,症状是「审批人成了我自己」,不是卡住;真正卡住的是参与人函数未注册(FunctionNotFoundException,无待办、PROCESSING、接口仍 errCode 0)。两者都用 workflow.instanceActivity 区分:无 status=1 节点=卡住;status=1 且参与人全是发起人=静默错派(r4-training 实证 2026-10-20)。 **START 之后必须保留发起节点**(如「填写申请单」participant={Originator},删掉/缺失则 publish 401035——r5-crm 实证 2026-10-09);流程模型的数据入口=直接 data.submit(创建+启流程+单据号生成,勿用 save+submit 两步)。 A route entry MAY carry the platform's structured branch condition ruleConditionGroup (outer OR / inner AND) plus childMatchType/utilizeElse/alternativeFlag/text — see the workflow contract's routeBranch: ${WORKFLOW_WIRE.routeBranch}. endEventHandler{bizActions,bizActionType} binds business rules — prefer workflow.bindRule instead of replacing the whole template.`);
|
|
51
51
|
const WORKFLOW_SCOPE = "greenfield workflow authoring";
|
|
52
52
|
export const WORKFLOW_LANE_CONTRACT = Object.freeze({
|
|
53
53
|
lane: LANES.workflow,
|
|
@@ -156,7 +156,11 @@ export const WORKFLOW_LANE_CONTRACT = Object.freeze({
|
|
|
156
156
|
}),
|
|
157
157
|
"workitem.list": Object.freeze({
|
|
158
158
|
scope: "runtime workflow work-item query",
|
|
159
|
-
description: "查询当前登录人的待办(`GET /api/api/runtime/workflow/search_workitems`)。WorkItemSearchVO 经 @ModelAttribute 传参,服务端以登录态注入 userId,故无需传处理人——结果天然是「我的待办」。返回每项 workItemId(id) + workflowInstanceId(instanceId) + activityName + state;并尽力经 `GET /api/api/runtime/workflow/getBizObjectByWfInstanceId` 解析 bizObjectId/schemaCode 供 data.submit 审批。`workflowInstanceId` 为客户端精确过滤(服务端 WorkItemSearchVO.filterWorkflowInstanceId 实为 notEqualsTo
|
|
159
|
+
description: "查询当前登录人的待办(`GET /api/api/runtime/workflow/search_workitems`)。WorkItemSearchVO 经 @ModelAttribute 传参,服务端以登录态注入 userId,故无需传处理人——结果天然是「我的待办」。返回每项 workItemId(id) + workflowInstanceId(instanceId) + activityName + state;并尽力经 `GET /api/api/runtime/workflow/getBizObjectByWfInstanceId` 解析 bizObjectId/schemaCode 供 data.submit 审批。`workflowInstanceId` 为客户端精确过滤(服务端 WorkItemSearchVO.filterWorkflowInstanceId 实为 notEqualsTo 排除,不能用于命中实例);带该过滤时 total 为过滤后的条数,原服务端总数在 unfilteredTotal。**想知道「流程停在哪」,用 `workflow.instanceActivity`,不要靠反复 workitem.list**——待办查询只返回在办项,实例停住时永远是空数组。",
|
|
160
|
+
}),
|
|
161
|
+
"workflow.instanceActivity": Object.freeze({
|
|
162
|
+
scope: "runtime workflow instance node-state diagnosis",
|
|
163
|
+
description: "按实例查全部节点状态(`GET /api/api/runtime/workflow/list_workflow_instance_activity`,WorkflowMaintenanceAssembler.listActivateActivity)。返回模板里每个活动节点的 activityCode/activityName/activityType + status(1=当前激活,0=已完成或未到达)+ 解析出的 participants(含姓名/id)。用于「data.submit 回 PROCESSING 但没有下一待办」的自诊断:例子 `omc call workflow.instanceActivity --args '{\"workflowInstanceId\":\"<id>\"}'`。当没有任何节点 status=1 时,readback 的 diagnosis.code=no-active-activity 并列出排查顺序(参与人为空的审批节点 → 参与人写法 → 容器日志 GetParticipantInfo error)。审批推进正常时该工具不需要——data.submit 的 readback.nextSubmit 已给出下一级参数。",
|
|
160
164
|
}),
|
|
161
165
|
}),
|
|
162
166
|
});
|
|
@@ -191,6 +195,7 @@ export function assertWorkflowContract() {
|
|
|
191
195
|
assertDescriptionCarries(WORKFLOW_LANE_CONTRACT, "workflow.validate", ["workflow/validate", "ValidateMessage", "valid"]);
|
|
192
196
|
assertDescriptionCarries(WORKFLOW_LANE_CONTRACT, "configure_workflow", ["workflow.create", "workflow.update", "workflow.publish", "workflow.bindRule", "bindRules"]);
|
|
193
197
|
assertDescriptionCarries(WORKFLOW_LANE_CONTRACT, "workitem.list", ["search_workitems", "workItemId", "workflowInstanceId", "bizObjectId", "getBizObjectByWfInstanceId", "filterWorkflowInstanceId"]);
|
|
198
|
+
assertDescriptionCarries(WORKFLOW_LANE_CONTRACT, "workflow.instanceActivity", ["list_workflow_instance_activity", "status", "participants", "no-active-activity"]);
|
|
194
199
|
if (!workflowTemplateArg.description?.includes("routes"))
|
|
195
200
|
throw new Error("workflow template arg must document the routes->rules mapping");
|
|
196
201
|
if (workflowTemplateArg.description?.includes('\\"templateType\\":\\"BusinessProcess\\"') || workflowTemplateArg.description?.includes('"templateType":"BusinessProcess"'))
|
|
@@ -26,7 +26,7 @@ export const WORKFLOW_COVERAGE = declareLaneCoverage(WORKFLOW_LANE_CONTRACT.lane
|
|
|
26
26
|
"packages/omc-cli/src/lanes/workflow.ts",
|
|
27
27
|
".scratch/omc-app-management/issues/06-workitem-list-capability.md",
|
|
28
28
|
],
|
|
29
|
-
tools: [claim("workitem.list", T["workitem.list"].scope)],
|
|
29
|
+
tools: [claim("workitem.list", T["workitem.list"].scope), claim("workflow.instanceActivity", T["workflow.instanceActivity"].scope)],
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
capability: "workflow participant resolution (org roles/departments)",
|
package/dist/lanes/workflow.js
CHANGED
|
@@ -8,7 +8,7 @@ import { registerPackageTool } from "../package-tool.js";
|
|
|
8
8
|
import { writeSummary } from "../human-summary.js";
|
|
9
9
|
import { defineTool, ToolInputError } from "../tool-types.js";
|
|
10
10
|
import { wireCall, wireRows, WireError } from "../wire.js";
|
|
11
|
-
import { resolveBizObjectIdByInstance, searchWorkItems, wireString } from "../workflow-support.js";
|
|
11
|
+
import { diagnoseInstanceActivity, getInstanceBaseInfo, listInstanceActivities, resolveBizObjectIdByInstance, searchWorkItems, wireString } from "../workflow-support.js";
|
|
12
12
|
import { WORKFLOW_EVENT_HANDLER_FIELDS, WORKFLOW_LANE_CONTRACT, workflowTemplateArg } from "./workflow-contract.js";
|
|
13
13
|
import { PLATFORM_TRIGGER_RULE_CODES } from "./rule-graph.js";
|
|
14
14
|
import { executeRuleEnableWrite } from "./rule-lifecycle.js";
|
|
@@ -2067,10 +2067,60 @@ export function registerWorkflowLane(tools) {
|
|
|
2067
2067
|
total: page.total,
|
|
2068
2068
|
totalPages: page.totalPages,
|
|
2069
2069
|
count: items.length,
|
|
2070
|
+
...(page.unfilteredTotal !== undefined ? { unfilteredTotal: page.unfilteredTotal } : {}),
|
|
2070
2071
|
items,
|
|
2071
2072
|
};
|
|
2072
2073
|
}),
|
|
2073
2074
|
}));
|
|
2075
|
+
tools.push(defineTool({
|
|
2076
|
+
name: "workflow.instanceActivity",
|
|
2077
|
+
layer: "low",
|
|
2078
|
+
kind: "read",
|
|
2079
|
+
lane: WORKFLOW_LANE,
|
|
2080
|
+
scope: WORKFLOW_TOOLS["workflow.instanceActivity"].scope,
|
|
2081
|
+
description: WORKFLOW_TOOLS["workflow.instanceActivity"].description,
|
|
2082
|
+
inputSchema: {
|
|
2083
|
+
environment: environmentArg,
|
|
2084
|
+
workflowInstanceId: z.string().min(1).describe("流程实例 id(data.submit / data.load 读回的 workflowInstanceId)"),
|
|
2085
|
+
capability: capabilityArg,
|
|
2086
|
+
},
|
|
2087
|
+
handler: async (ctx, args, self) => governedRead(ctx, self, args, async (session) => {
|
|
2088
|
+
const rows = await listInstanceActivities(session, args.workflowInstanceId);
|
|
2089
|
+
const header = await getInstanceBaseInfo(session, args.workflowInstanceId);
|
|
2090
|
+
const instanceStatus = header ? wireString(header.status) : null;
|
|
2091
|
+
const activities = rows.map((row) => {
|
|
2092
|
+
const participants = Array.isArray(row.participantors) ? row.participantors : [];
|
|
2093
|
+
return {
|
|
2094
|
+
activityCode: wireString(row.activityCode),
|
|
2095
|
+
activityName: wireString(row.activityName),
|
|
2096
|
+
activityType: row.activityType === undefined ? null : row.activityType,
|
|
2097
|
+
status: row.status === undefined ? null : row.status,
|
|
2098
|
+
participantCount: participants.length,
|
|
2099
|
+
participants: participants.slice(0, 10).map((person) => ({
|
|
2100
|
+
name: wireString(person.name) ?? wireString(person.userName) ?? null,
|
|
2101
|
+
id: wireString(person.id) ?? wireString(person.userId) ?? null,
|
|
2102
|
+
})),
|
|
2103
|
+
...(row.sheetTrigger !== undefined ? { sheetTrigger: row.sheetTrigger } : {}),
|
|
2104
|
+
};
|
|
2105
|
+
});
|
|
2106
|
+
const active = activities.filter((activity) => activity.status === 1);
|
|
2107
|
+
const reached = activities.filter((activity) => activity.status === 1 || activity.participantCount > 0);
|
|
2108
|
+
// Diagnosis, not inference: the platform's own signal is status 1 = an
|
|
2109
|
+
// activity that currently holds tasks. No active activity while the
|
|
2110
|
+
// instance is still PROCESSING is exactly the silent-stall shape.
|
|
2111
|
+
const diagnosis = diagnoseInstanceActivity({ instanceStatus, endStatus: header ? wireString(header.endStatus) : null, originator: header ? wireString(header.originator) : null, activities });
|
|
2112
|
+
return {
|
|
2113
|
+
workflowInstanceId: args.workflowInstanceId,
|
|
2114
|
+
...(instanceStatus ? { instanceStatus } : {}),
|
|
2115
|
+
...(header && wireString(header.endStatus) ? { endStatus: wireString(header.endStatus) } : {}),
|
|
2116
|
+
activityCount: activities.length,
|
|
2117
|
+
activeCount: active.length,
|
|
2118
|
+
reachedCount: reached.length,
|
|
2119
|
+
activities,
|
|
2120
|
+
diagnosis,
|
|
2121
|
+
};
|
|
2122
|
+
}),
|
|
2123
|
+
}));
|
|
2074
2124
|
tools.push(defineTool({
|
|
2075
2125
|
name: "org.role.list",
|
|
2076
2126
|
layer: "low",
|
package/dist/recipes/workflow.js
CHANGED
|
@@ -28,7 +28,7 @@ const workflowTemplateUpdate = Object.freeze({
|
|
|
28
28
|
"**{Originator}=发起人自己**:审批节点用它=提交人自审自己——提交人与审批人同账号时平台可能直接自动通过(COMPLETED,无待办;r40 实证,与节点结构有关)。需要独立审批人时绑定具体用户 id 或 `FindUserByRole({Originator.OU},\"<角色code>\",\"random\")`;验收要 PENDING 待办时先确认审批人≠提交人;",
|
|
29
29
|
"**participant 必须是平台可解析的三种形态之一(真实生产 2026-09-23 观察)**:(a) 关键字 `{Originator}`;(b) 参与者对象数组 JSON `[{\"code\":\"<角色code>\",\"corpId\":\"<corpId|other>\",\"id\":\"<roleId>\",\"name\":\"<角色名>\",\"type\":2}]`(type 2=角色,3=用户;多个用 `+` 连接);(c) 函数 `FindUserByRole({Originator.OU},\"<roleCode>\",\"random\")`。**自由文本角色名(如 `部门负责人`)能 publish 但解析不到人、不生成待办**——先用 `org.role.list` / `org.department.list` 拿 code/id 再构造。工具会在 readback 里以 `participantWarnings` 标出自由文本。",
|
|
30
30
|
"**不要从读回/种子配置回抄函数式参与人**:`workflow.get_published` 的摘要与平台种子流程(如 crm_contract_flow)会显示 `FindUsersByRoleFunction(...)` / `FindUsersSpecByRoleFunction(...)`,1.1.25 运行期**未注册这两个名字**,原样写回会在 ParticipantActivityRuntime 抛 `FunctionNotFoundException`;参与人表达式只认 `FindUserByRole(org, roleCode[, order])`(r4-training 实证 2026-10-20,`cloudpivot-workflow-AI-Platform-v1.1.25.jar` 只有 `FindUserByRoleFunction`)。",
|
|
31
|
-
"
|
|
31
|
+
"**参与人解析为空有两种后果,别混为一谈(1.1.25 实测 2026-10-20)**:① 解析成空数组(角色零成员、成员未挂发起人部门、自由文本、角色码不存在)时引擎**不报错也不卡住**,而是把任务退回**发起人**——`biz_workitem.participant` 就是发起人,接口回进程正常,唯一症状是「审批人显示成我自己」。② 参与人**函数未注册**(`FindUsersByRoleFunction` / `FindUsersSpecByRoleFunction`)才真停:`FunctionNotFoundException`、不生成待办、实例卡 PROCESSING 而接口仍回 errCode 0。要区分用 `omc call workflow.instanceActivity --args '{\"workflowInstanceId\":\"<id>\"}'`:无 status=1 节点=真停;有 status=1 且参与人全是发起人=静默错派(readback 的 diagnosis.warnings 会给 participant-fallback-to-originator)。",
|
|
32
32
|
"**审批节点必须带 sheetCode(=模型默认表单槽位,通常等于 schemaCode)——S29 契约:participant sheetCode 为空 publish 会 401026 WORKFLOW_ACTIVITY_NO_SHEET。工具已自动回填,正常无需手填**;",
|
|
33
33
|
"节点类型 START / PARTICIPANT / END;省略的既有节点由 merge 保留;",
|
|
34
34
|
"**平台 workflow.create 会自动生成含「填写申请单」发起节点的草稿(START → 填写申请单 → 审批 → END);workflow.update 是 merge,不删除既有节点——要严格三节点需在设计器删该节点,或如实接受该发起节点**",
|
package/dist/workflow-support.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ENDPOINTS } from "./endpoints.js";
|
|
2
|
-
import { wireCall } from "./wire.js";
|
|
2
|
+
import { wireCall, wireRows } from "./wire.js";
|
|
3
3
|
/**
|
|
4
4
|
* Shared workflow RUNTIME endpoint knowledge (ticket 21): merges the two
|
|
5
5
|
* parallel protocol implementations that used to live inside the runtime-data
|
|
@@ -62,7 +62,47 @@ export async function searchWorkItems(session, filter = {}) {
|
|
|
62
62
|
const items = filter.instanceId
|
|
63
63
|
? page.content.filter((item) => wireString(item.instanceId) === filter.instanceId)
|
|
64
64
|
: page.content;
|
|
65
|
-
|
|
65
|
+
// r4-training 2026-10-20: `total` is the server's count BEFORE the client-side
|
|
66
|
+
// instance filter, so a completed instance answered `total:7, count:0` — which
|
|
67
|
+
// reads as a contradiction. When the caller filtered by instance, report the
|
|
68
|
+
// filtered total and keep the raw number under unfilteredTotal.
|
|
69
|
+
return filter.instanceId
|
|
70
|
+
? { total: items.length, totalPages: page.totalPages, unfilteredTotal: page.total, items }
|
|
71
|
+
: { total: page.total, totalPages: page.totalPages, items };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Node-by-node state of one workflow instance (r4-training 2026-10-20).
|
|
75
|
+
*
|
|
76
|
+
* A stalled instance (`data.submit` → PROCESSING with no next todo) answers
|
|
77
|
+
* errCode 0 and exposes nothing through `workitem.list` (the todo query only
|
|
78
|
+
* returns pending items). `list_workflow_instance_activity` returns every
|
|
79
|
+
* template activity with `status` (1 = active, 0 = done or never reached) and
|
|
80
|
+
* the resolved participants, which is the difference between "flow advanced"
|
|
81
|
+
* and "flow silently stopped" (WorkflowMaintenanceAssembler.listActivateActivity).
|
|
82
|
+
*/
|
|
83
|
+
export async function listInstanceActivities(session, instanceId) {
|
|
84
|
+
const data = (await wireCall(session, {
|
|
85
|
+
path: ENDPOINTS.workflowListInstanceActivity,
|
|
86
|
+
query: { workflowInstanceId: instanceId },
|
|
87
|
+
})).data;
|
|
88
|
+
return wireRows(data);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Instance header (WorkflowIntanceBaseInfoVO): `status` is the run state, so a
|
|
92
|
+
* COMPLETED instance with zero active activities is NOT a stall. Best-effort —
|
|
93
|
+
* a header lookup failure must not hide the activity list.
|
|
94
|
+
*/
|
|
95
|
+
export async function getInstanceBaseInfo(session, instanceId) {
|
|
96
|
+
try {
|
|
97
|
+
const data = (await wireCall(session, {
|
|
98
|
+
path: ENDPOINTS.workflowGetInstanceBaseInfo,
|
|
99
|
+
query: { workflowInstanceId: instanceId },
|
|
100
|
+
})).data;
|
|
101
|
+
return data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
66
106
|
}
|
|
67
107
|
/**
|
|
68
108
|
* Resolve the in-flight bizObject identity for a workflow instance.
|
|
@@ -96,4 +136,50 @@ export async function getReplayToken(session) {
|
|
|
96
136
|
const token = await wireCall(session, { path: ENDPOINTS.runtimeFormReplayToken, query: {} });
|
|
97
137
|
return typeof token.data === "string" && token.data ? token.data : undefined;
|
|
98
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Turn (instance status, per-activity status) into the one-line verdict a caller
|
|
141
|
+
* needs after `data.submit` returned PROCESSING. A finished instance legitimately
|
|
142
|
+
* has no active activity; a still-running one with none has silently stalled
|
|
143
|
+
* (r4-training 2026-10-20 — the stall answers errCode 0 and shows nothing).
|
|
144
|
+
*/
|
|
145
|
+
export function diagnoseInstanceActivity(input) {
|
|
146
|
+
const active = input.activities.filter((activity) => activity.status === 1);
|
|
147
|
+
if (input.instanceStatus !== null && !/RUNNING|PROCESSING|ACTIVE/i.test(input.instanceStatus))
|
|
148
|
+
return {
|
|
149
|
+
code: "instance-finished",
|
|
150
|
+
message: `实例状态 ${input.instanceStatus},没有激活节点属正常结束${input.endStatus ? `(endStatus=${input.endStatus})` : ""}。`,
|
|
151
|
+
checks: [],
|
|
152
|
+
warnings: [],
|
|
153
|
+
};
|
|
154
|
+
if (active.length === 0) {
|
|
155
|
+
const emptyParticipants = input.activities.filter((activity) => activity.participantCount === 0).map((activity) => activity.activityCode ?? "?");
|
|
156
|
+
return {
|
|
157
|
+
code: "no-active-activity",
|
|
158
|
+
message: `该实例仍在运行,但没有任何活动节点处于激活状态(status=1)——流程已停住:可能是参与人解析为空、节点被取消,或下一节点未创建${emptyParticipants.length ? `。无参与人的节点:${emptyParticipants.join("、")}` : ""}。`,
|
|
159
|
+
checks: [
|
|
160
|
+
"看上面 participants 为空的审批节点——参与人解析为空是 1.1.25 最常见的静默停滞原因",
|
|
161
|
+
"参与人优先用参与者对象数组(角色 id);内置函数只认 FindUserByRole(org, roleCode[, order])",
|
|
162
|
+
"需要平台日志时看容器日志的 GetParticipantInfo error / CustomParticipleMethod{...} -> []",
|
|
163
|
+
],
|
|
164
|
+
warnings: [],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// 1.1.25 fallback shape (r4-training 2026-10-20 live): when a participant
|
|
168
|
+
// expression resolves to nobody (empty role, free text, unknown role code) the
|
|
169
|
+
// engine does NOT stall — it hands the task to the originator, so the only
|
|
170
|
+
// visible symptom is "the approver is me". Flag that instead of calling it a
|
|
171
|
+
// stall: a real stall (unregistered participant function / engine exception)
|
|
172
|
+
// produces no work item at all.
|
|
173
|
+
const originator = input.originator ?? null;
|
|
174
|
+
const fallbackSuspect = originator !== null &&
|
|
175
|
+
active.some((activity) => activity.participantCount > 0 && activity.participants.every((person) => person.id === originator));
|
|
176
|
+
return {
|
|
177
|
+
code: "has-active-activity",
|
|
178
|
+
message: `当前激活节点:${active.map((activity) => `${activity.activityCode}(${activity.activityName ?? ""})`).join("、")}`,
|
|
179
|
+
checks: fallbackSuspect
|
|
180
|
+
? ["若这些节点本应流转给角色或他人,现在却落在发起人身上:参与人解析为空时平台会退回发起人(1.1.25 实测),流程看似推进实为错派。核对参与人写法(参与者对象数组 / FindUserByRole(org, roleCode[, order]))与角色成员。"]
|
|
181
|
+
: [],
|
|
182
|
+
warnings: fallbackSuspect ? ["participant-fallback-to-originator"] : [],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
99
185
|
//# sourceMappingURL=workflow-support.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.61",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@stalfh233/omc-fake-platform": "0.4.
|
|
53
|
+
"@stalfh233/omc-fake-platform": "0.4.61",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.61",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|
|
@@ -150,7 +150,7 @@ rules:
|
|
|
150
150
|
|
|
151
151
|
- **两条路**:模型+表单+流程一揽子交付走**意图**(`intent` 的 `workflow` 能力,声明式一次 apply);只动流程或想逐步控制走**低层链** `workflow.create → workflow.update(权限/节点)→ workflow.publish`,标准链配方见 `omc describe workflow.update`(含多级审批/participant 三形态/自动回填说明)。流程权限细节见下节。
|
|
152
152
|
- 一个模型只能有一个流程;已有流程的模型直接 `workflow.update`,不要另建。
|
|
153
|
-
-
|
|
153
|
+
- **参与人解析失败分两种,别当成同一种(1.1.25 实测)**:① **解析为空**(角色零成员、成员未挂部门、自由文本、角色码不存在)→ 引擎把任务**退回发起人**,流程继续,症状是「审批人显示成我自己」,`data.submit` 与 `workitem.list` 都正常;② **参与人函数未注册**(`FindUsersByRoleFunction` / `FindUsersSpecByRoleFunction`,1.1.25 未内置)→ `FunctionNotFoundException`、不生成待办、实例卡 PROCESSING 而接口仍回 `errCode 0`。区分用 `omc call workflow.instanceActivity --args '{"workflowInstanceId":"<id>"}'`:无 `status:1` 节点=卡住;`status:1` 且参与人全是发起人=`diagnosis.warnings` 给 `participant-fallback-to-originator`。不要从 `workflow.get_published` 的摘要回抄函数式参与人;内置写法只有 `FindUserByRole(org, roleCode[, order])`,跨部门/全局角色优先用参与者对象数组(角色 id)。
|
|
154
154
|
|
|
155
155
|
## 流程字段权限
|
|
156
156
|
|
|
@@ -137,10 +137,10 @@ only the section for the capability you need —
|
|
|
137
137
|
|
|
138
138
|
## 流程模板 `workflow`
|
|
139
139
|
|
|
140
|
-
- **能做哪些事**:流程模板 get_draft/get_published、create、update、publish、bindRule(事件处理器规则绑定)、delete、workitem.list
|
|
141
|
-
- **工具**:`workflow.get_draft`、`workflow.get_published`、`workflow.validate`、`workflow.create`、`workflow.update`、`workflow.publish`、`workflow.bindRule`、`workflow.delete`、`configure_workflow`、`workitem.list`、`org.role.list`、`org.department.list`、`org.user.search`
|
|
140
|
+
- **能做哪些事**:流程模板 get_draft/get_published、create、update、publish、bindRule(事件处理器规则绑定)、delete、workitem.list、workflow.instanceActivity(实例节点状态诊断);参与人用组织对象。
|
|
141
|
+
- **工具**:`workflow.get_draft`、`workflow.get_published`、`workflow.validate`、`workflow.create`、`workflow.update`、`workflow.publish`、`workflow.bindRule`、`workflow.delete`、`configure_workflow`、`workitem.list`、`workflow.instanceActivity`、`org.role.list`、`org.department.list`、`org.user.search`
|
|
142
142
|
- **recipe**:`omc describe workflow.bindRule --example workflow-handler-binding`
|
|
143
|
-
- **边界 / 陷阱**:greenfield 首版;`publish`/`bindRule` 不得在 `ValidateMessage[]` 拒绝时报告成功。运行时 workitem(提交/审批)走 `data.submit
|
|
143
|
+
- **边界 / 陷阱**:greenfield 首版;`publish`/`bindRule` 不得在 `ValidateMessage[]` 拒绝时报告成功。运行时 workitem(提交/审批)走 `data.submit`;想知道实例停在哪/是否错派,用 `workflow.instanceActivity`(无 status=1=真停,参与人全是发起人=解析为空退回)。
|
|
144
144
|
|
|
145
145
|
## 表单在线 JS `onlinejs`
|
|
146
146
|
|