@stalfh233/omc-fake-platform 0.4.87 → 0.4.90

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/state.d.ts CHANGED
@@ -380,6 +380,14 @@ export interface FakeState {
380
380
  roleIds: string[];
381
381
  deptIds?: string[];
382
382
  }>;
383
+ /** Workflow instances (biz_workflow_instance), keyed by instance id. */
384
+ workflowInstances: Map<string, {
385
+ id: string;
386
+ workflowCode: string;
387
+ schemaCode: string;
388
+ bizObjectId: string;
389
+ status: string;
390
+ }>;
383
391
  /** Workflow start permissions (h_workflow_permission): who may originate. */
384
392
  workflowPermissions: Map<string, {
385
393
  id: string;
package/dist/state.js CHANGED
@@ -64,6 +64,7 @@ export function createFakeState(options = {}) {
64
64
  orgUsers: new Map(),
65
65
  orgRoles: new Map(),
66
66
  orgMembers: new Map(),
67
+ workflowInstances: new Map(),
67
68
  workflowPermissions: new Map(),
68
69
  enforceWorkflowStartPermission: false,
69
70
  workflows: new Map(),
package/dist/workflow.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { businessRefusal, malformedEnvelope, NOT_HANDLED } from "./state.js";
2
- const PENDING_STATES = new Set(["NOTSTARTED", "INPROGRESS"]);
2
+ // DRAFT: a portal draft-save start item is state DRAFT (live r15 readback) and
3
+ // still appears in the caller's todo face until submitted.
4
+ const PENDING_STATES = new Set(["NOTSTARTED", "INPROGRESS", "DRAFT"]);
3
5
  const ACTIVITY_TYPES = new Set(["START", "END", "CONNECTION", "PARTICIPANT", "SUB_INSTANCE", "CIRCULATE", "SYSTEM_ACTIVITY", "AGENT_ACTIVITY"]);
4
6
  const POINT = /^-?\d+(?:\.\d+)?,\s-?\d+(?:\.\d+)?$/;
5
7
  /**
@@ -102,13 +104,25 @@ export function startWorkflowDraft(state, input) {
102
104
  const sequence = state.workItems.size + 1;
103
105
  const instanceId = `wf-${sequence}`;
104
106
  const startId = `wi-${sequence}`;
107
+ // Live 1.1.25: saveWorkflow creates a DRAFT instance row plus a DRAFT start
108
+ // work item (read back from biz_workflow_instance/biz_workitem on the r15
109
+ // scratch model). The DRAFT state is load-bearing: the instance-list endpoint
110
+ // hides DRAFT rows (see the route above), so a purge that only reads that list
111
+ // misses exactly this case.
112
+ state.workflowInstances.set(instanceId, {
113
+ id: instanceId,
114
+ workflowCode: input.workflowCode,
115
+ schemaCode: input.schemaCode,
116
+ bizObjectId: input.bizObjectId,
117
+ status: "DRAFT",
118
+ });
105
119
  state.workItems.set(startId, {
106
120
  id: startId,
107
121
  instanceId,
108
122
  activityCode: "fill",
109
123
  activityName: "填写申请单",
110
124
  participant: "tester",
111
- state: "INPROGRESS",
125
+ state: "DRAFT",
112
126
  workflowCode: input.workflowCode,
113
127
  schemaCode: input.schemaCode,
114
128
  bizObjectId: input.bizObjectId,
@@ -372,6 +386,44 @@ export const workflowRoute = (ctx, input) => {
372
386
  content: rows.slice(start, start + size).map(toWorkItemVO),
373
387
  };
374
388
  }
389
+ case "/api/api/runtime/workflow/list_instances": {
390
+ // WorkflowInstanceController.list_instances — the instance list a purge
391
+ // must empty before the model cascade can run (r14: a surviving instance's
392
+ // work item outlives its template and NPEs the global todo search).
393
+ //
394
+ // Live-faithful detail (r15, decompiled 8.6.26): the controller always
395
+ // sets currentUserId, and BizWorkflowinstanceServiceImpl adds
396
+ // `state != DRAFT` for any non-empty user — so DRAFT instances are NEVER
397
+ // in this list. A purge must find them through their work items.
398
+ let rows = [...ctx.state.workflowInstances.values()].filter((row) => String(row.status ?? "").toUpperCase() !== "DRAFT");
399
+ if (q.workflowCode)
400
+ rows = rows.filter((row) => row.workflowCode === q.workflowCode);
401
+ const size = Number(q.size ?? 10);
402
+ const page = Number(q.page ?? 0);
403
+ const start = page * size;
404
+ return {
405
+ totalElements: rows.length,
406
+ totalPages: size > 0 ? Math.ceil(rows.length / size) : 0,
407
+ content: rows.slice(start, start + size),
408
+ };
409
+ }
410
+ case "/api/api/runtime/workflow/delete_instance": {
411
+ // WorkflowInstanceController.delete_instance: only the instance id is
412
+ // required (the user comes from the session), so an admin session can erase
413
+ // an instance started by someone else.
414
+ const id = String(q.workflowInstanceId ?? "");
415
+ if (!id)
416
+ return businessRefusal(40000, "流程实例id为空");
417
+ const instance = ctx.state.workflowInstances.get(id);
418
+ // Live 1.1.25 (r15): deleting an instance whose business row is already
419
+ // gone makes the engine NPE while rendering the "workflow removed"
420
+ // notice (`bizObject should not be null`) → 50000, and the instance row
421
+ // stays. Mirror it so purge's stale-instance path is exercised offline.
422
+ if (instance && !ctx.state.dataRows.has(String(instance.bizObjectId ?? "")))
423
+ return businessRefusal(50000, "服务异常");
424
+ const existed = ctx.state.workflowInstances.delete(id);
425
+ return existed;
426
+ }
375
427
  case "/api/api/runtime/workflow/list_workitems": {
376
428
  // The JPA-spec face (WorkflowInstanceController.listWorkItems): it queri
377
429
  // the work-item table directly instead of resolving each hit's template,
@@ -406,8 +458,9 @@ export const workflowRoute = (ctx, input) => {
406
458
  { id: "role-finance", name: "财务负责人", code: "SYS260515031207258", roleType: "SYS", corpId: "corp-1" },
407
459
  ];
408
460
  case "/api/api/organization/department/tree": {
409
- // Platform semantics (1.1.25): no deptIds → roots; deptIds=<id> → that
410
- // department's WHOLE SUBTREE, not one level.
461
+ // Platform semantics (re-verified r15, 1.1.25): no deptIds → roots ONLY;
462
+ // deptIds=<id> → DIRECT CHILDREN only. The old "whole subtree" fake made
463
+ // the one-level walker look correct, hiding third-level departments.
411
464
  const all = [
412
465
  { id: "dept-1", name: "总所", code: "1", corpId: "corp-1", parentId: null },
413
466
  { id: "dept-2", name: "业务部", code: "1_1", corpId: "corp-1", parentId: "dept-1" },
@@ -416,18 +469,7 @@ export const workflowRoute = (ctx, input) => {
416
469
  const requested = String(q.deptIds ?? "");
417
470
  if (requested === "")
418
471
  return { departmentList: all.filter((dept) => dept.parentId === null) };
419
- const subtree = [];
420
- const queue = [requested];
421
- while (queue.length > 0) {
422
- const current = queue.shift();
423
- for (const dept of all) {
424
- if (dept.parentId !== current || subtree.some((seen) => seen.id === dept.id))
425
- continue;
426
- subtree.push(dept);
427
- queue.push(dept.id);
428
- }
429
- }
430
- return { departmentList: subtree };
472
+ return { departmentList: all.filter((dept) => dept.parentId === requested) };
431
473
  }
432
474
  case "/api/api/organization/user/search": {
433
475
  // UserController.search: @RequestBody {wd,page,size} → PageVO<UserVO>.
@@ -645,7 +687,10 @@ export const workflowRoute = (ctx, input) => {
645
687
  const members = [...ctx.state.orgMembers.values()].filter((member) => member.roleId === roleId);
646
688
  const page = Number(q.page ?? 0);
647
689
  const size = Number(q.size ?? 500);
648
- return { totalElements: members.length, totalPages: size > 0 ? Math.ceil(members.length / size) : 0, content: members.slice(page * size, page * size + size).map((member) => ({ id: member.userId, name: member.name, unitType: member.unitType, departmentId: "dept-1" })) };
690
+ // Live 1.1.25 row: departmentId is null, the department rides
691
+ // parentDepartmentName[0].deptId, and ouScope is what role-participant
692
+ // resolution filters on (r15).
693
+ return { totalElements: members.length, totalPages: size > 0 ? Math.ceil(members.length / size) : 0, content: members.slice(page * size, page * size + size).map((member) => ({ id: member.userId, name: member.name, unitType: member.unitType, departmentId: null, departmentName: "总所", parentDepartmentName: [{ deptId: "dept-1", deptPath: "总所", deptName: "总所" }], ouScope: member.ouScope })) };
649
694
  }
650
695
  case "/api/api/organization/role/get_ouscope": {
651
696
  const member = ctx.state.orgMembers.get(`${String(q.roleId ?? "")}#${String(q.userId ?? "")}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stalfh233/omc-fake-platform",
3
- "version": "0.4.87",
3
+ "version": "0.4.90",
4
4
  "description": "OMC 的内存假 CloudPivot 平台(wire 接缝的第二个适配器)+ HTTP 假服务器——@stalfh233/omc-cli 的测试与 bench 基准基座",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "build": "tsc --project tsconfig.build.json"
19
19
  },
20
20
  "dependencies": {
21
- "omc-core": "0.4.87"
21
+ "omc-core": "0.4.90"
22
22
  },
23
23
  "engines": {
24
24
  "node": ">=22.13"