@gadmin2n/schematics 0.0.142 → 0.0.144

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.
Files changed (140) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +5 -23
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/lib/page-helpers.ts +4 -2
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/page-manage.ts +36 -1
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/agenda.seed.ts +14 -14
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/bootstrap.ts +18 -13
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/canvas.seed.ts +14 -14
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +1 -1
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +67 -404
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +16 -40
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflows.ts +12 -124
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.module.ts +3 -0
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +0 -21
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.controller.ts +3 -10
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/codeRuntime.module.ts +1 -2
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-callback.controller.spec.ts +48 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-callback.controller.ts +96 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.controller.spec.ts +1 -59
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.controller.ts +8 -43
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.dto.ts +0 -23
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction.service.ts +2 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-wait.handler.ts +2 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/externalInteraction.module.ts +7 -3
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.controller.ts +2 -2
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.service.spec.ts +18 -123
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.service.ts +4 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.controller.ts +3 -10
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/httpRequest.module.ts +1 -2
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.controller.ts +0 -10
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.dto.ts +12 -4
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.errors.ts +1 -1
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.external-decision.spec.ts +42 -209
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.service.ts +108 -225
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/humanApproval.module.ts +0 -2
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.controller.ts +27 -22
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.dto.ts +9 -1
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/runtime-token.guard.spec.ts +30 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/runtime-token.guard.ts +8 -15
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +0 -2
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/runtime-connectivity-probe.service.spec.ts +14 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/runtime-connectivity-probe.service.ts +37 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.spec.ts +41 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.ts +53 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.module.ts +7 -2
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/workflow-runtime-health.controller.ts +21 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.spec.ts +2 -2
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-callable.spec.ts +16 -88
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-callable.ts +23 -59
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +125 -39
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +95 -28
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-handle-validator.ts +0 -20
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.spec.ts +23 -26
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.ts +6 -52
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +2 -2
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +24 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +26 -21
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/widget-catalog.service.spec.ts +0 -1
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/widget-catalog.service.ts +0 -1
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowRuntimeConfig/workflow-runtime-config.service.spec.ts +32 -0
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowRuntimeConfig/workflow-runtime-config.service.ts +41 -0
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowRuntimeConfig/workflowRuntimeConfig.module.ts +10 -0
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/server/tsconfig.build.json +10 -1
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +16 -12
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +1 -1
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/PageHeader.tsx +1 -1
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/inspectorActions.ts +19 -19
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/contexts/color-mode/index.tsx +1 -1
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/header.tsx +15 -11
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +0 -6
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/AddDataModal.tsx +3 -3
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/AddPageModal.tsx +1 -1
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DeleteDataConfirm.tsx +1 -1
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/EditDataModal.tsx +1 -1
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +1 -0
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +85 -95
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +85 -95
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +2 -2
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +21 -2
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/game/list.tsx +21 -2
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/human-approval/inbox.tsx +14 -66
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +4 -4
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +3 -3
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +3 -3
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.test.tsx +29 -0
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.tsx +4 -2
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/EnhancedFlowRenderer.tsx +4 -6
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.test.tsx +3 -0
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.tsx +3 -1
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +4 -6
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NewNodeDefaults.test.tsx +23 -92
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeOutputSummary.tsx +200 -0
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +61 -136
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +96 -195
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeTypeBadge.tsx +36 -0
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/CreateNodeInstanceModal.tsx +5 -10
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +47 -22
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/create.tsx +7 -5
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/edit.tsx +8 -6
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-config.css +35 -0
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/default-widgets.tsx +1 -6
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApproverSourceEditor.tsx +1 -4
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CodeConfigEditor.tsx +1 -1
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.tsx +1 -1
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalWaitConfigEditor.tsx +82 -259
  104. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HttpRequestConfigEditor.tsx +1 -2
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.test.tsx +7 -6
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.tsx +77 -71
  107. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.test.tsx +10 -57
  108. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.tsx +8 -273
  109. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +54 -0
  110. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +110 -20
  111. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +4 -34
  112. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveOutputs.test.ts +3 -10
  113. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveOutputs.ts +2 -37
  114. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/list.tsx +26 -2
  115. package/package.json +1 -1
  116. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.controller.ts +0 -89
  117. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.dto.ts +0 -68
  118. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.service.spec.ts +0 -177
  119. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.service.ts +0 -283
  120. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approvalChannel.module.ts +0 -10
  121. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/ApprovalChannelModal.test.tsx +0 -84
  122. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/ApprovalChannelModal.tsx +0 -141
  123. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/approval-channel.types.ts +0 -16
  124. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/index.tsx +0 -210
  125. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApprovalRequestTriggerConfigEditor.tsx +0 -39
  126. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubmitApprovalResultConfigEditor.tsx +0 -104
  127. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubmitExternalResultConfigEditor.tsx +0 -98
  128. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/AssignRolesModal.tsx +0 -0
  129. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/CreatePageModal.tsx +0 -0
  130. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/EditPageModal.tsx +0 -0
  131. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/PageDetailDrawer.tsx +0 -0
  132. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/PageFormModal.tsx +0 -0
  133. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/CreateModal.tsx +0 -0
  134. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/EditModal.tsx +0 -0
  135. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/ResourceDetailDrawer.tsx +0 -0
  136. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/modal.tsx +0 -0
  137. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/CreateModal.tsx +0 -0
  138. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/EditModal.tsx +0 -0
  139. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/RoleDetailDrawer.tsx +0 -0
  140. /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/modal.tsx +0 -0
@@ -20,25 +20,26 @@ import { seedNodeTypes } from './workflow-node-types';
20
20
  * 业务数据 seed 函数仍来自 ./workflow-{node-types,workflows,event-trigger}.ts
21
21
  * (文件较大,保持独立但只被本入口 import,不再由 seed/index.ts 直接调用)。
22
22
  *
23
- * 前置:seedBootstrap 已建好 admin 与 SYSTEM_ADMIN。
23
+ * 前置:seedBootstrap 已建好 SYSTEM_ADMIN。
24
24
  */
25
25
  export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
26
26
  const role = await prisma.role.findUniqueOrThrow({
27
27
  where: { name: 'SYSTEM_ADMIN' },
28
28
  });
29
- const admin = await prisma.page.findUniqueOrThrow({
30
- where: { code: 'admin' },
31
- });
32
29
 
33
- // ── 父分组 page ────────────────────────────────────────────────────────────
34
- const workflowPage = await upsertPage(prisma, {
35
- code: 'workflow',
36
- name: 'workflow',
37
- zhName: 'Workflow',
38
- enName: 'Workflow',
39
- sortOrder: 1030,
40
- parentId: admin.id,
41
- });
30
+ // ── 父分组 page(与 admin 平级的顶级菜单) ─────────────────────────────────
31
+ const workflowPage = await upsertPage(
32
+ prisma,
33
+ {
34
+ code: 'workflow',
35
+ name: 'workflow',
36
+ zhName: 'Workflow',
37
+ enName: 'Workflow',
38
+ sortOrder: 1030,
39
+ },
40
+ // 从「系统管理」子菜单提升为顶级菜单:同步 parentId/排序到已有 DB
41
+ { parentId: null, sortOrder: 1030 },
42
+ );
42
43
  console.log(`[workflow] Page: ${workflowPage.code} (id=${workflowPage.id})`);
43
44
 
44
45
  // ── leaf pages + resource 绑定 ─────────────────────────────────────────────
@@ -82,20 +83,11 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
82
83
  resourceCode: 'temporalTenantConfig',
83
84
  resourceDescription: 'Temporal tenant config management',
84
85
  },
85
- {
86
- code: 'workflow_approval_channels',
87
- zhName: '审批通道',
88
- enName: 'Approval Channels',
89
- sortOrder: 5,
90
- resourceCode: 'approvalChannel',
91
- resourceDescription:
92
- 'Reusable dispatch and callback workflows for external approvals',
93
- },
94
86
  {
95
87
  code: 'approval_tasks',
96
88
  zhName: '审批任务',
97
89
  enName: 'Approval Tasks',
98
- sortOrder: 6,
90
+ sortOrder: 5,
99
91
  resourceCode: 'humanApproval',
100
92
  resourceDescription: 'Read and decide assigned Human Approval tasks',
101
93
  },
@@ -185,10 +177,7 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
185
177
  const connectionsPage = allPages.find(
186
178
  (page) => page.code === 'workflow_connections',
187
179
  );
188
- const approvalChannelsPage = allPages.find(
189
- (page) => page.code === 'workflow_approval_channels',
190
- );
191
- if (nodeTemplatePage && connectionsPage && approvalChannelsPage) {
180
+ if (nodeTemplatePage && connectionsPage) {
192
181
  const authorRoles = await prisma.rolePages.findMany({
193
182
  where: { pageId: nodeTemplatePage.id },
194
183
  select: { roleId: true },
@@ -197,10 +186,6 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
197
186
  prisma,
198
187
  connectionsPage.id,
199
188
  );
200
- const approvalChannelResourceIds = await getPageResourceIds(
201
- prisma,
202
- approvalChannelsPage.id,
203
- );
204
189
  for (const { roleId } of authorRoles) {
205
190
  await grantRoleAccess(
206
191
  prisma,
@@ -211,15 +196,6 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
211
196
  console.log(
212
197
  `[workflow] Grant: role#${roleId} → workflow_connections (inherits Node Template access)`,
213
198
  );
214
- await grantRoleAccess(
215
- prisma,
216
- roleId,
217
- approvalChannelsPage.id,
218
- approvalChannelResourceIds,
219
- );
220
- console.log(
221
- `[workflow] Grant: role#${roleId} → workflow_approval_channels (inherits Node Template access)`,
222
- );
223
199
  }
224
200
  }
225
201
 
@@ -40,26 +40,18 @@ const WORKFLOWS = [
40
40
  position: { x: 850, y: 200 },
41
41
  config: {},
42
42
  },
43
- {
44
- id: 'action_4',
45
- type: 'send_notification',
46
- label: '发送通知',
47
- position: { x: 1100, y: 200 },
48
- config: {},
49
- },
50
43
  ],
51
44
  edges: [
52
45
  { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
53
46
  { id: 'e-action_1-action_2', source: 'action_1', target: 'action_2' },
54
47
  { id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
55
- { id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
56
48
  ],
57
49
  },
58
50
  },
59
51
  {
60
52
  code: 'user_reg_approval',
61
53
  name: '新用户注册审批',
62
- description: '新用户注册后触发审批流程,管理员审批通过后发送欢迎邮件',
54
+ description: '新用户注册后触发审批流程,管理员审批通过后激活用户',
63
55
  status: 'PUBLISHED' as const,
64
56
  isEnabled: true,
65
57
  creator: 'admin',
@@ -71,13 +63,6 @@ const WORKFLOWS = [
71
63
  label: 'Webhook 接收注册',
72
64
  position: { x: 100, y: 200 },
73
65
  },
74
- {
75
- id: 'action_1',
76
- type: 'set_variable',
77
- label: '提取用户信息',
78
- position: { x: 350, y: 200 },
79
- config: {},
80
- },
81
66
  {
82
67
  id: 'approval_1',
83
68
  type: 'approval',
@@ -99,26 +84,11 @@ const WORKFLOWS = [
99
84
  position: { x: 1100, y: 100 },
100
85
  config: {},
101
86
  },
102
- {
103
- id: 'action_3',
104
- type: 'send_notification',
105
- label: '发送欢迎邮件',
106
- position: { x: 1350, y: 100 },
107
- config: {},
108
- },
109
- {
110
- id: 'action_4',
111
- type: 'send_notification',
112
- label: '发送拒绝通知',
113
- position: { x: 1100, y: 300 },
114
- config: {},
115
- },
116
87
  ],
117
88
  edges: [
118
- { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
119
89
  {
120
- id: 'e-action_1-approval_1',
121
- source: 'action_1',
90
+ id: 'e-trigger_1-approval_1',
91
+ source: 'trigger_1',
122
92
  target: 'approval_1',
123
93
  },
124
94
  {
@@ -132,20 +102,13 @@ const WORKFLOWS = [
132
102
  target: 'action_2',
133
103
  label: 'true',
134
104
  },
135
- {
136
- id: 'e-condition_1-action_4',
137
- source: 'condition_1',
138
- target: 'action_4',
139
- label: 'false',
140
- },
141
- { id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
142
105
  ],
143
106
  },
144
107
  },
145
108
  {
146
109
  code: 'error_monitor_alert',
147
110
  name: '异常监控告警',
148
- description: '监听系统事件,当错误率超过阈值时触发告警通知',
111
+ description: '监听系统事件并记录异常监控结果',
149
112
  status: 'PUBLISHED' as const,
150
113
  isEnabled: true,
151
114
  creator: 'system',
@@ -172,13 +135,6 @@ const WORKFLOWS = [
172
135
  position: { x: 600, y: 200 },
173
136
  config: {},
174
137
  },
175
- {
176
- id: 'action_2',
177
- type: 'send_notification',
178
- label: '发送告警',
179
- position: { x: 850, y: 100 },
180
- config: {},
181
- },
182
138
  {
183
139
  id: 'action_3',
184
140
  type: 'code',
@@ -194,12 +150,6 @@ const WORKFLOWS = [
194
150
  source: 'action_1',
195
151
  target: 'condition_1',
196
152
  },
197
- {
198
- id: 'e-condition_1-action_2',
199
- source: 'condition_1',
200
- target: 'action_2',
201
- label: 'true',
202
- },
203
153
  {
204
154
  id: 'e-condition_1-action_3',
205
155
  source: 'condition_1',
@@ -212,7 +162,7 @@ const WORKFLOWS = [
212
162
  {
213
163
  code: 'batch_data_export',
214
164
  name: '批量数据导出',
215
- description: '手动触发导出指定表的数据为CSV文件并发送邮件',
165
+ description: '手动触发导出指定表的数据为 CSV 文件',
216
166
  status: 'DRAFT' as const,
217
167
  isEnabled: false,
218
168
  creator: 'admin',
@@ -245,19 +195,11 @@ const WORKFLOWS = [
245
195
  position: { x: 850, y: 200 },
246
196
  config: {},
247
197
  },
248
- {
249
- id: 'action_3',
250
- type: 'send_notification',
251
- label: '发送邮件',
252
- position: { x: 1100, y: 200 },
253
- config: {},
254
- },
255
198
  ],
256
199
  edges: [
257
200
  { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
258
201
  { id: 'e-action_1-loop_1', source: 'action_1', target: 'loop_1' },
259
202
  { id: 'e-loop_1-action_2', source: 'loop_1', target: 'action_2' },
260
- { id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
261
203
  ],
262
204
  },
263
205
  },
@@ -304,13 +246,6 @@ const WORKFLOWS = [
304
246
  position: { x: 850, y: 300 },
305
247
  config: {},
306
248
  },
307
- {
308
- id: 'action_3',
309
- type: 'send_notification',
310
- label: '部署完成通知',
311
- position: { x: 1100, y: 200 },
312
- config: {},
313
- },
314
249
  ],
315
250
  edges: [
316
251
  {
@@ -333,16 +268,13 @@ const WORKFLOWS = [
333
268
  source: 'approval_1',
334
269
  target: 'action_2',
335
270
  },
336
- { id: 'e-action_1-action_3', source: 'action_1', target: 'action_3' },
337
- { id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
338
271
  ],
339
272
  },
340
273
  },
341
274
  {
342
275
  code: 'user_role_change',
343
- name: '用户角色变更通知',
344
- description:
345
- '当 t_admin_user_role 表中用户角色发生变更时,记录审计日志并通知相关人员',
276
+ name: '用户角色变更审计',
277
+ description: '当 t_admin_user_role 表中用户角色发生变更时,记录审计日志',
346
278
  status: 'PUBLISHED' as const,
347
279
  isEnabled: true,
348
280
  creator: 'admin',
@@ -386,17 +318,6 @@ const WORKFLOWS = [
386
318
  ],
387
319
  },
388
320
  },
389
- {
390
- id: 'action_3',
391
- type: 'send_notification',
392
- label: '发送安全告警',
393
- position: { x: 1100, y: 100 },
394
- config: {
395
- channel: 'wecom',
396
- recipients: ['security-team'],
397
- message: '用户 {{username}} 被授予 SYSTEM_ADMIN 角色',
398
- },
399
- },
400
321
  {
401
322
  id: 'action_4',
402
323
  type: 'db_execute',
@@ -414,9 +335,9 @@ const WORKFLOWS = [
414
335
  target: 'condition_1',
415
336
  },
416
337
  {
417
- id: 'e-condition_1-action_3',
338
+ id: 'e-condition_1-action_4-true',
418
339
  source: 'condition_1',
419
- target: 'action_3',
340
+ target: 'action_4',
420
341
  label: 'true',
421
342
  },
422
343
  {
@@ -425,15 +346,13 @@ const WORKFLOWS = [
425
346
  target: 'action_4',
426
347
  label: 'false',
427
348
  },
428
- { id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
429
349
  ],
430
350
  },
431
351
  },
432
352
  {
433
353
  code: 'event_remind_sync',
434
354
  name: 'Event活动提醒与同步',
435
- description:
436
- '当 t_event 表新增活动或活动即将开始时,自动同步到 ServiceNow 并通知参与人员',
355
+ description: '当 t_event 表新增活动或活动即将开始时,自动同步到 ServiceNow',
437
356
  status: 'PUBLISHED' as const,
438
357
  isEnabled: true,
439
358
  creator: 'admin',
@@ -504,17 +423,6 @@ const WORKFLOWS = [
504
423
  position: { x: 1600, y: 80 },
505
424
  config: { operation: 'update', table: 't_event', where: 'id = $1' },
506
425
  },
507
- {
508
- id: 'action_4',
509
- type: 'send_notification',
510
- label: '通知OIT负责人',
511
- position: { x: 1350, y: 250 },
512
- config: {
513
- channel: 'email',
514
- message:
515
- '活动 {{currentItem.city}} - {{currentItem.eventType}} 将于 {{currentItem.start_date}} 开始',
516
- },
517
- },
518
426
  {
519
427
  id: 'action_5',
520
428
  type: 'code',
@@ -551,14 +459,7 @@ const WORKFLOWS = [
551
459
  target: 'action_2',
552
460
  label: 'false',
553
461
  },
554
- {
555
- id: 'e-condition_2-action_4',
556
- source: 'condition_2',
557
- target: 'action_4',
558
- label: 'true',
559
- },
560
462
  { id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
561
- { id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
562
463
  ],
563
464
  },
564
465
  },
@@ -566,7 +467,7 @@ const WORKFLOWS = [
566
467
  code: 'new_user_onboard',
567
468
  name: '新增用户自动化流程',
568
469
  description:
569
- '当 t_admin_user_role 新增用户时,自动分配默认角色、同步到外部系统并发送欢迎通知',
470
+ '当 t_admin_user_role 新增用户时,自动分配默认角色、同步到外部系统并记录审计日志',
570
471
  status: 'PUBLISHED' as const,
571
472
  isEnabled: true,
572
473
  creator: 'admin',
@@ -632,18 +533,6 @@ const WORKFLOWS = [
632
533
  method: 'POST',
633
534
  },
634
535
  },
635
- {
636
- id: 'action_5',
637
- type: 'send_notification',
638
- label: '发送欢迎通知',
639
- position: { x: 1350, y: 200 },
640
- config: {
641
- channel: 'wecom',
642
- recipients: ['{{userid}}'],
643
- message:
644
- '欢迎加入系统!您的初始角色为 GUEST,如需更多权限请联系管理员。',
645
- },
646
- },
647
536
  {
648
537
  id: 'action_6',
649
538
  type: 'db_execute',
@@ -677,8 +566,7 @@ const WORKFLOWS = [
677
566
  label: 'false',
678
567
  },
679
568
  { id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
680
- { id: 'e-action_4-action_5', source: 'action_4', target: 'action_5' },
681
- { id: 'e-action_5-action_6', source: 'action_5', target: 'action_6' },
569
+ { id: 'e-action_4-action_6', source: 'action_4', target: 'action_6' },
682
570
  ],
683
571
  },
684
572
  },
@@ -23,6 +23,7 @@ import { HealthModule } from './modules/health/health.module';
23
23
  import { RoleModule } from './modules/role/role.module';
24
24
  import { RoleService } from './modules/role/role.service';
25
25
  import { RolesRefresherService } from './modules/role/roles-refresher.service';
26
+ import { WorkflowRuntimeConfigModule } from './modules/workflowRuntimeConfig/workflowRuntimeConfig.module';
26
27
 
27
28
  @Module({
28
29
  imports: [
@@ -84,6 +85,8 @@ import { RolesRefresherService } from './modules/role/roles-refresher.service';
84
85
  ttl: 5 * 60 * 1000, // 5 minutes
85
86
  }),
86
87
 
88
+ WorkflowRuntimeConfigModule,
89
+
87
90
  ...modules,
88
91
  AgendaModule,
89
92
  HealthModule,
@@ -53,7 +53,6 @@ const config: Record<string, any> = {
53
53
  address: 'http://127.0.0.1:3000/upload/',
54
54
  maxSize: 1024 * 1024 * 10,
55
55
  },
56
- humanApprovalRuntime: runtimeConfig(),
57
56
  workflow: {
58
57
  publicBaseUrl: process.env.WORKFLOW_PUBLIC_BASE_URL ?? '',
59
58
  publicHost: process.env.WORKFLOW_PUBLIC_HOST ?? '',
@@ -63,24 +62,4 @@ const config: Record<string, any> = {
63
62
  },
64
63
  };
65
64
 
66
- function runtimeConfig() {
67
- const rawEnabled =
68
- process.env.WORKFLOW_RUNTIME_ENABLED ??
69
- process.env.HUMAN_APPROVAL_RUNTIME_ENABLED;
70
- if (rawEnabled !== undefined && !['true', 'false'].includes(rawEnabled))
71
- throw new Error('WORKFLOW_RUNTIME_ENABLED must be exactly true or false');
72
- const enabled = rawEnabled === 'true';
73
- const token = process.env.WORKFLOW_RUNTIME_TOKEN ?? '';
74
- if (enabled && token.length < 32)
75
- throw new Error(
76
- 'WORKFLOW_RUNTIME_TOKEN must contain at least 32 characters when Workflow runtime is enabled',
77
- );
78
- const tenantNamespace = process.env.GADMIN_TENANT_NAMESPACE ?? '';
79
- if (enabled && !tenantNamespace)
80
- throw new Error(
81
- 'GADMIN_TENANT_NAMESPACE is required when Workflow runtime is enabled',
82
- );
83
- return { enabled, token, tenantNamespace };
84
- }
85
-
86
65
  export default () => config;
@@ -6,10 +6,10 @@ import {
6
6
  Post,
7
7
  UseGuards,
8
8
  } from '@nestjs/common';
9
- import { ConfigService } from '@nestjs/config';
10
9
  import { PrismaService } from 'nestjs-prisma';
11
10
  import { AllowUnauthorizedRequest } from '../../lib/auth.guard';
12
11
  import { RuntimeTokenGuard } from '../humanApproval/runtime/runtime-token.guard';
12
+ import { WorkflowRuntimeConfigService } from '../workflowRuntimeConfig/workflow-runtime-config.service';
13
13
  import { CodeNodeConfig, executeCode } from './code-executor';
14
14
  import { ExecuteCodeRuntimeDto } from './code-runtime.dto';
15
15
 
@@ -18,20 +18,13 @@ import { ExecuteCodeRuntimeDto } from './code-runtime.dto';
18
18
  export class CodeRuntimeController {
19
19
  constructor(
20
20
  private readonly prisma: PrismaService,
21
- private readonly config: ConfigService,
21
+ private readonly runtimeConfig: WorkflowRuntimeConfigService,
22
22
  ) {}
23
23
 
24
24
  @Post('execute')
25
25
  @AllowUnauthorizedRequest()
26
26
  async execute(@Body() input: ExecuteCodeRuntimeDto) {
27
- const runtime = this.config.get<{ tenantNamespace: string }>(
28
- 'humanApprovalRuntime',
29
- );
30
- if (input.tenantNamespace !== runtime?.tenantNamespace)
31
- throw runtimeError(
32
- 'WORKFLOW_RUNTIME_TENANT_MISMATCH',
33
- 'Runtime tenant does not match this application',
34
- );
27
+ await this.runtimeConfig.assertTenant(input.tenantNamespace);
35
28
 
36
29
  const instanceId = parseId(input.workflowInstanceId);
37
30
  const versionId = parseId(input.workflowVersionId);
@@ -1,5 +1,4 @@
1
1
  import { Injectable, Module, OnModuleInit } from '@nestjs/common';
2
- import { RuntimeTokenGuard } from '../humanApproval/runtime/runtime-token.guard';
3
2
  import { WidgetCatalogService } from '../workflowConfig/widget-catalog.service';
4
3
  import { WorkflowConfigModule } from '../workflowConfig/workflowConfig.module';
5
4
  import { CodeRuntimeController } from './code-runtime.controller';
@@ -16,6 +15,6 @@ class CodeWidgetRegistrar implements OnModuleInit {
16
15
  @Module({
17
16
  imports: [WorkflowConfigModule],
18
17
  controllers: [CodeRuntimeController],
19
- providers: [RuntimeTokenGuard, CodeWidgetRegistrar],
18
+ providers: [CodeWidgetRegistrar],
20
19
  })
21
20
  export class CodeRuntimeModule {}
@@ -0,0 +1,48 @@
1
+ import { ExternalCallbackController } from './external-callback.controller';
2
+
3
+ describe('ExternalCallbackController', () => {
4
+ it('extracts the callback token from MyOA data and submits the raw callback', async () => {
5
+ const interactions = {
6
+ submitResult: jest.fn().mockResolvedValue({ accepted: true }),
7
+ };
8
+ const controller = new ExternalCallbackController(
9
+ interactions as any,
10
+ {
11
+ getRequired: jest.fn().mockResolvedValue({ namespace: 'tenant-a' }),
12
+ } as any,
13
+ );
14
+ const body = {
15
+ id: 'workitem-1',
16
+ status: 'approved',
17
+ data: [
18
+ { key: 'business_id', value: ['SO-1'] },
19
+ { key: 'gadmin_callback_token', value: ['ext_cb_approval'] },
20
+ ],
21
+ };
22
+
23
+ await expect(
24
+ controller.receive(body, { 'content-type': 'text/json' }),
25
+ ).resolves.toEqual({ accepted: true });
26
+ expect(interactions.submitResult).toHaveBeenCalledWith('tenant-a', {
27
+ callbackToken: 'ext_cb_approval',
28
+ eventId: 'external:workitem-1',
29
+ outcome: 'RECEIVED',
30
+ data: { callback: body, contentType: 'text/json' },
31
+ });
32
+ });
33
+
34
+ it('rejects a callback without the one-time token', async () => {
35
+ const controller = new ExternalCallbackController(
36
+ { submitResult: jest.fn() } as any,
37
+ {
38
+ getRequired: jest.fn().mockResolvedValue({ namespace: 'tenant-a' }),
39
+ } as any,
40
+ );
41
+
42
+ await expect(
43
+ controller.receive({ id: 'workitem-1' }, {}),
44
+ ).rejects.toMatchObject({
45
+ response: { code: 'EXTERNAL_CALLBACK_TOKEN_MISSING' },
46
+ });
47
+ });
48
+ });
@@ -0,0 +1,96 @@
1
+ import {
2
+ BadRequestException,
3
+ Body,
4
+ Controller,
5
+ Headers,
6
+ Post,
7
+ } from '@nestjs/common';
8
+ import { createHash } from 'node:crypto';
9
+ import { AllowUnauthorizedRequest } from '../../lib/auth.guard';
10
+ import { WorkflowRuntimeConfigService } from '../workflowRuntimeConfig/workflow-runtime-config.service';
11
+ import { ExternalInteractionService } from './external-interaction.service';
12
+
13
+ @Controller('workflow/external-callback')
14
+ export class ExternalCallbackController {
15
+ constructor(
16
+ private readonly interactions: ExternalInteractionService,
17
+ private readonly runtimeConfig: WorkflowRuntimeConfigService,
18
+ ) {}
19
+
20
+ @Post()
21
+ @AllowUnauthorizedRequest()
22
+ async receive(
23
+ @Body() body: unknown,
24
+ @Headers() headers: Record<string, string | string[] | undefined>,
25
+ ) {
26
+ const callbackToken = extractCallbackToken(body);
27
+ if (!callbackToken)
28
+ throw new BadRequestException({
29
+ code: 'EXTERNAL_CALLBACK_TOKEN_MISSING',
30
+ message: '回调数据中缺少 gadmin_callback_token',
31
+ });
32
+ const runtime = await this.runtimeConfig.getRequired();
33
+ return this.interactions.submitResult(runtime.namespace, {
34
+ callbackToken,
35
+ eventId: externalEventId(body),
36
+ outcome: 'RECEIVED',
37
+ data: {
38
+ callback: body,
39
+ contentType: headerValue(headers['content-type']),
40
+ },
41
+ });
42
+ }
43
+ }
44
+
45
+ function extractCallbackToken(value: unknown): string | undefined {
46
+ const body = asObject(value);
47
+ if (typeof body.callbackToken === 'string' && body.callbackToken.trim())
48
+ return body.callbackToken.trim();
49
+
50
+ const data = body.data;
51
+ if (Array.isArray(data)) {
52
+ for (const rawItem of data) {
53
+ const item = asObject(rawItem);
54
+ if (item.key !== 'gadmin_callback_token') continue;
55
+ const token = Array.isArray(item.value) ? item.value[0] : item.value;
56
+ if (typeof token === 'string' && token.trim()) return token.trim();
57
+ }
58
+ }
59
+ const dataObject = asObject(data);
60
+ const nested = dataObject.gadmin_callback_token;
61
+ if (typeof nested === 'string' && nested.trim()) return nested.trim();
62
+ if (Array.isArray(nested) && typeof nested[0] === 'string')
63
+ return nested[0].trim() || undefined;
64
+ return undefined;
65
+ }
66
+
67
+ function externalEventId(body: unknown): string {
68
+ const value = asObject(body);
69
+ for (const key of [
70
+ 'eventId',
71
+ 'event_id',
72
+ 'id',
73
+ 'workitemId',
74
+ 'workitem_id',
75
+ ]) {
76
+ const candidate = value[key];
77
+ if (
78
+ (typeof candidate === 'string' || typeof candidate === 'number') &&
79
+ String(candidate).trim()
80
+ )
81
+ return `external:${String(candidate).trim()}`;
82
+ }
83
+ return `external:${createHash('sha256')
84
+ .update(JSON.stringify(body ?? null))
85
+ .digest('hex')}`;
86
+ }
87
+
88
+ function headerValue(value: string | string[] | undefined): string | undefined {
89
+ return Array.isArray(value) ? value[0] : value;
90
+ }
91
+
92
+ function asObject(value: unknown): Record<string, any> {
93
+ return value && typeof value === 'object' && !Array.isArray(value)
94
+ ? (value as Record<string, any>)
95
+ : {};
96
+ }