@gadmin2n/schematics 0.0.72 → 0.0.75

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 (173) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +2 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/job.prisma +62 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/system.prisma +0 -21
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +171 -0
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/AgendaJob.ts +60 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +1 -1
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowEventOutbox.ts +62 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeInstance.ts +62 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeType.ts +62 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env +5 -0
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +5 -4
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +14 -7
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +4 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +49 -3
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +746 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflows.ts +786 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +6 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +79 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.spec.ts +20 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.ts +145 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.module.ts +10 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/{canvas/canvas.service.spec.ts → agendaJob/agendaJob.service.spec.ts} +71 -65
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.service.ts +83 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +2 -1
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.module.ts +9 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +100 -0
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution.dto.ts +19 -0
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.dto.ts +43 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.service.ts +317 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.controller.ts +16 -0
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.service.ts +13 -0
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +220 -0
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +82 -0
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.module.ts +16 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +505 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.spec.ts +22 -0
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.ts +147 -0
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.module.ts +10 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.spec.ts +356 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.ts +110 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.spec.ts +22 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.ts +216 -0
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.module.ts +10 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +356 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.ts +168 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.spec.ts +22 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.ts +199 -0
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.module.ts +10 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +348 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.ts +106 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/yarn.lock +579 -1082
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +278 -0
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/config/development-sql.yaml +5 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/docker-compose.yml +25 -0
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/package.json +13 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/sql/create-event-trigger.sql +87 -0
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.env +7 -0
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/SANDBOX.md +122 -0
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package-lock.json +4285 -0
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package.json +28 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/activities/code-execute.test.ts +44 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/activities/http-request.test.ts +87 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/helpers.test.ts +225 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/node-type-consistency.test.ts +101 -0
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/code-execute.ts +51 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-execute.ts +85 -0
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-query.ts +35 -0
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/http-request.ts +54 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +6 -0
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +62 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/send-notification.ts +47 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/config.ts +13 -0
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/condition.ts +101 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/context.ts +58 -0
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/graph.ts +184 -0
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/helpers.ts +133 -0
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/node-types.ts +57 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/types.ts +77 -0
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +36 -0
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +226 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +411 -0
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/tsconfig.json +19 -0
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/vitest.config.ts +8 -0
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/yarn.lock +1905 -0
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package-lock.json +17555 -0
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +5 -2
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +1 -0
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +5 -1
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +1 -1
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +63 -0
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DevShell.tsx +91 -2
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/list.tsx +48 -2
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/show.tsx +43 -2
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +14 -9
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +14 -9
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +309 -56
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/show.tsx +1 -3
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/create.tsx +108 -0
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/edit.tsx +124 -0
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/index.tsx +4 -0
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +245 -0
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/show.tsx +70 -0
  104. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +0 -1
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +160 -2
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +120 -148
  107. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CodeFloatWindow.tsx +74 -181
  108. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/LivePreview.tsx +15 -13
  109. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasConfigRegistry.tsx +2 -2
  110. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasContextMenuRegistry.tsx +338 -3
  111. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +18 -17
  112. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/BarChartDataSourceModal.tsx +10 -4
  113. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/LineChartDataSourceModal.tsx +10 -4
  114. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/{ChartViewerConfigModal.tsx → MultiChartConfigModal.tsx} +30 -18
  115. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/MultiChartDataSourceModal.tsx +427 -0
  116. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/NumCardDataSourceModal.tsx +10 -4
  117. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/PromptModal.tsx +6 -14
  118. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/RadarChartDataSourceModal.tsx +10 -4
  119. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/TableDataSourceModal.tsx +10 -4
  120. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/canvasModalProps.ts +24 -0
  121. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/demos.ts +45 -63
  122. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.tsx +99 -0
  123. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExportModal.tsx +87 -0
  124. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +322 -0
  125. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ImportModal.tsx +175 -0
  126. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeEditModal.tsx +60 -0
  127. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +1150 -0
  128. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/RunWorkflowModal.tsx +101 -0
  129. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/StatusCards.tsx +198 -0
  130. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/VersionPanel.tsx +81 -0
  131. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +566 -0
  132. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/hooks/useWorkflowAgent.ts +224 -0
  133. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +524 -0
  134. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +343 -0
  135. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +243 -0
  136. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/CreateNodeInstanceModal.tsx +363 -0
  137. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/DynamicConfigForm.tsx +154 -0
  138. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +176 -0
  139. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/create.tsx +77 -0
  140. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/edit.tsx +112 -0
  141. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/index.tsx +305 -0
  142. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +282 -0
  143. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +469 -0
  144. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +92 -0
  145. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/create.tsx +111 -0
  146. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/edit.tsx +127 -0
  147. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/index.tsx +4 -0
  148. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/list.tsx +254 -0
  149. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/show.tsx +74 -0
  150. package/dist/lib/application/files/gadmin2-game-angle-demo/web/yarn.lock +1501 -1199
  151. package/package.json +1 -1
  152. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.controller.spec.ts +0 -22
  153. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/BarChart/index.tsx +0 -896
  154. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/ChartSwitcher/index.tsx +0 -219
  155. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/ChartViewer/index.tsx +0 -159
  156. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Filter/index.tsx +0 -192
  157. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/LineChart/index.tsx +0 -1034
  158. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumCard/NumCard.module.css +0 -8
  159. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumCard/index.tsx +0 -509
  160. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumLineCard/index.tsx +0 -66
  161. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/PieChart/index.tsx +0 -552
  162. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/RadarChart/index.tsx +0 -263
  163. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Section/index.tsx +0 -35
  164. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Table/index.tsx +0 -207
  165. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/TreemapChart/index.tsx +0 -382
  166. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/WorldMap/index.tsx +0 -135
  167. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/chart-constants.ts +0 -53
  168. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/icon/InfoIcon.tsx +0 -8
  169. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/icon/index.ts +0 -1
  170. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/config.ts +0 -31
  171. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/nameMap.json +0 -9
  172. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/world.geo.json +0 -39349
  173. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/metric-info-tooltip/index.tsx +0 -19
@@ -0,0 +1,505 @@
1
+ import { Injectable, NotFoundException } from '@nestjs/common';
2
+ import { Prisma } from '@prisma/client';
3
+ import { PrismaService } from 'nestjs-prisma';
4
+
5
+ @Injectable()
6
+ export class WorkflowService {
7
+ constructor(private prisma: PrismaService) {}
8
+
9
+ async findMany(params: {
10
+ status?: string;
11
+ instanceStatus?: string;
12
+ name?: string;
13
+ skip?: number;
14
+ limit?: number;
15
+ }) {
16
+ const where: Prisma.WorkflowWhereInput = {};
17
+ if (params.status) {
18
+ where.status = params.status as any;
19
+ }
20
+ if (params.instanceStatus) {
21
+ where.instances = { some: { status: params.instanceStatus as any } };
22
+ }
23
+ if (params.name) {
24
+ where.name = { contains: params.name, mode: 'insensitive' };
25
+ }
26
+
27
+ const [items, total] = await Promise.all([
28
+ this.prisma.workflow.findMany({
29
+ where,
30
+ orderBy: { updatedAt: 'desc' },
31
+ skip: params.skip || 0,
32
+ take: params.limit || 20,
33
+ include: {
34
+ versions: {
35
+ orderBy: { version: 'desc' },
36
+ take: 1,
37
+ select: { version: true, changeSummary: true },
38
+ },
39
+ _count: { select: { instances: true } },
40
+ },
41
+ }),
42
+ this.prisma.workflow.count({ where }),
43
+ ]);
44
+
45
+ // Add per-status instance counts
46
+ const workflowIds = items.map((w) => w.id);
47
+ const instanceCounts = workflowIds.length
48
+ ? await this.prisma.workflowInstance.groupBy({
49
+ by: ['workflowId', 'status'],
50
+ where: { workflowId: { in: workflowIds } },
51
+ _count: true,
52
+ })
53
+ : [];
54
+
55
+ const runsMap = new Map<
56
+ bigint,
57
+ { pending: number; running: number; completed: number; failed: number }
58
+ >();
59
+ for (const row of instanceCounts) {
60
+ if (!runsMap.has(row.workflowId)) {
61
+ runsMap.set(row.workflowId, {
62
+ pending: 0,
63
+ running: 0,
64
+ completed: 0,
65
+ failed: 0,
66
+ });
67
+ }
68
+ const entry = runsMap.get(row.workflowId)!;
69
+ const key = row.status.toLowerCase() as keyof typeof entry;
70
+ if (key in entry) entry[key] = row._count;
71
+ }
72
+
73
+ // Get last run time for each workflow
74
+ const lastRuns = workflowIds.length
75
+ ? await this.prisma.workflowInstance.findMany({
76
+ where: { workflowId: { in: workflowIds } },
77
+ orderBy: { createdAt: 'desc' },
78
+ distinct: ['workflowId'],
79
+ select: { workflowId: true, createdAt: true, status: true },
80
+ })
81
+ : [];
82
+ const lastRunMap = new Map(lastRuns.map((r) => [r.workflowId, r]));
83
+
84
+ const enrichedItems = items.map((item) => ({
85
+ ...item,
86
+ runs: runsMap.get(item.id) || {
87
+ pending: 0,
88
+ running: 0,
89
+ completed: 0,
90
+ failed: 0,
91
+ },
92
+ lastRun: lastRunMap.get(item.id)?.createdAt || null,
93
+ }));
94
+
95
+ return { items: enrichedItems, total };
96
+ }
97
+
98
+ async findOne(id: bigint) {
99
+ const workflow = await this.prisma.workflow.findUnique({
100
+ where: { id },
101
+ include: {
102
+ versions: {
103
+ orderBy: { version: 'desc' },
104
+ take: 1,
105
+ },
106
+ },
107
+ });
108
+ if (!workflow) throw new NotFoundException('Workflow not found');
109
+ return workflow;
110
+ }
111
+
112
+ async create(
113
+ data: { name: string; description?: string; dsl?: any },
114
+ creator: string,
115
+ ) {
116
+ const workflow = await this.prisma.workflow.create({
117
+ data: {
118
+ name: data.name,
119
+ description: data.description,
120
+ dsl: data.dsl || null,
121
+ creator,
122
+ },
123
+ });
124
+
125
+ return workflow;
126
+ }
127
+
128
+ async update(id: bigint, data: { name?: string; description?: string }) {
129
+ const workflow = await this.prisma.workflow.findUnique({ where: { id } });
130
+ if (!workflow) throw new NotFoundException('Workflow not found');
131
+ return this.prisma.workflow.update({ where: { id }, data });
132
+ }
133
+
134
+ async saveDraft(id: bigint, dsl: any) {
135
+ const workflow = await this.prisma.workflow.findUnique({ where: { id } });
136
+ if (!workflow) throw new NotFoundException('Workflow not found');
137
+ return this.prisma.workflow.update({
138
+ where: { id },
139
+ data: { dsl, status: 'DRAFT' },
140
+ });
141
+ }
142
+
143
+ async toggleEnabled(id: bigint, enabled: boolean) {
144
+ const workflow = await this.prisma.workflow.findUnique({ where: { id } });
145
+ if (!workflow) throw new NotFoundException('Workflow not found');
146
+ return this.prisma.workflow.update({
147
+ where: { id },
148
+ data: { isEnabled: enabled },
149
+ });
150
+ }
151
+
152
+ async remove(id: bigint) {
153
+ const workflow = await this.prisma.workflow.findUnique({ where: { id } });
154
+ if (!workflow) throw new NotFoundException('Workflow not found');
155
+ return this.prisma.workflow.delete({ where: { id } });
156
+ }
157
+
158
+ async getVersions(workflowId: bigint) {
159
+ return this.prisma.workflowVersion.findMany({
160
+ where: { workflowId },
161
+ orderBy: { version: 'desc' },
162
+ select: {
163
+ id: true,
164
+ version: true,
165
+ prompt: true,
166
+ changeSummary: true,
167
+ creator: true,
168
+ createdAt: true,
169
+ },
170
+ });
171
+ }
172
+
173
+ async getVersionDsl(workflowId: bigint, version: number) {
174
+ const v = await this.prisma.workflowVersion.findUnique({
175
+ where: { workflowId_version: { workflowId, version } },
176
+ });
177
+ if (!v) throw new NotFoundException('Version not found');
178
+ return v;
179
+ }
180
+
181
+ async publish(
182
+ workflowId: bigint,
183
+ data: { changeSummary: string },
184
+ creator: string,
185
+ ) {
186
+ const workflow = await this.prisma.workflow.findUnique({
187
+ where: { id: workflowId },
188
+ });
189
+ if (!workflow) throw new NotFoundException('Workflow not found');
190
+
191
+ // Use workflow.dsl if available; otherwise fall back to latest version's DSL
192
+ let dslToPublish = workflow.dsl;
193
+ if (!dslToPublish) {
194
+ const latestVersion = await this.prisma.workflowVersion.findFirst({
195
+ where: { workflowId },
196
+ orderBy: { version: 'desc' },
197
+ select: { dsl: true },
198
+ });
199
+ dslToPublish = latestVersion?.dsl ?? null;
200
+ }
201
+ if (!dslToPublish) {
202
+ throw new NotFoundException('No draft DSL to publish');
203
+ }
204
+
205
+ return this.prisma.$transaction(async (tx) => {
206
+ const lastVersion = await tx.workflowVersion.findFirst({
207
+ where: { workflowId },
208
+ orderBy: { version: 'desc' },
209
+ select: { version: true },
210
+ });
211
+ const nextVersion = (lastVersion?.version ?? 0) + 1;
212
+
213
+ const version = await tx.workflowVersion.create({
214
+ data: {
215
+ workflowId,
216
+ version: nextVersion,
217
+ dsl: dslToPublish,
218
+ prompt: data.changeSummary,
219
+ changeSummary: data.changeSummary,
220
+ creator,
221
+ },
222
+ });
223
+
224
+ await tx.workflow.update({
225
+ where: { id: workflowId },
226
+ data: { status: 'PUBLISHED' },
227
+ });
228
+
229
+ return version;
230
+ });
231
+ }
232
+
233
+ async getStats(workflowId: bigint) {
234
+ const [running, failed, completed, total] = await Promise.all([
235
+ this.prisma.workflowInstance.count({
236
+ where: { workflowId, status: 'RUNNING' },
237
+ }),
238
+ this.prisma.workflowInstance.count({
239
+ where: { workflowId, status: 'FAILED' },
240
+ }),
241
+ this.prisma.workflowInstance.count({
242
+ where: { workflowId, status: 'COMPLETED' },
243
+ }),
244
+ this.prisma.workflowInstance.count({ where: { workflowId } }),
245
+ ]);
246
+ return { running, failed, completed, total };
247
+ }
248
+
249
+ async getOverview() {
250
+ const now = new Date();
251
+ const last24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
252
+
253
+ const [
254
+ totalAll,
255
+ total24h,
256
+ completedAll,
257
+ completed24h,
258
+ failedAll,
259
+ failed24h,
260
+ running,
261
+ pending,
262
+ ] = await Promise.all([
263
+ this.prisma.workflowInstance.count(),
264
+ this.prisma.workflowInstance.count({
265
+ where: { createdAt: { gte: last24h } },
266
+ }),
267
+ this.prisma.workflowInstance.count({ where: { status: 'COMPLETED' } }),
268
+ this.prisma.workflowInstance.count({
269
+ where: { status: 'COMPLETED', createdAt: { gte: last24h } },
270
+ }),
271
+ this.prisma.workflowInstance.count({ where: { status: 'FAILED' } }),
272
+ this.prisma.workflowInstance.count({
273
+ where: { status: 'FAILED', createdAt: { gte: last24h } },
274
+ }),
275
+ this.prisma.workflowInstance.count({ where: { status: 'RUNNING' } }),
276
+ this.prisma.workflowInstance.count({ where: { status: 'PENDING' } }),
277
+ ]);
278
+
279
+ // Success rate calculation
280
+ const denomAll = completedAll + failedAll;
281
+ const denom24h = completed24h + failed24h;
282
+ const successRateAll = denomAll > 0 ? (completedAll / denomAll) * 100 : 0;
283
+ const successRate24h = denom24h > 0 ? (completed24h / denom24h) * 100 : 0;
284
+
285
+ // AVG/P95 duration via raw SQL
286
+ const durationAll = await this.prisma.$queryRaw<
287
+ { avg: number | null; p95: number | null }[]
288
+ >`
289
+ SELECT
290
+ AVG(EXTRACT(EPOCH FROM finished_at - started_at)) as avg,
291
+ PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM finished_at - started_at)) as p95
292
+ FROM t_workflow_instance
293
+ WHERE status = 'COMPLETED' AND finished_at IS NOT NULL AND started_at IS NOT NULL
294
+ `;
295
+
296
+ const duration24h = await this.prisma.$queryRaw<
297
+ { avg: number | null; p95: number | null }[]
298
+ >`
299
+ SELECT
300
+ AVG(EXTRACT(EPOCH FROM finished_at - started_at)) as avg,
301
+ PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM finished_at - started_at)) as p95
302
+ FROM t_workflow_instance
303
+ WHERE status = 'COMPLETED' AND finished_at IS NOT NULL AND started_at IS NOT NULL
304
+ AND created_at >= ${last24h}
305
+ `;
306
+
307
+ return {
308
+ totalRuns: { last24h: total24h, total: totalAll },
309
+ successRate: {
310
+ last24h: Math.round(successRate24h * 10) / 10,
311
+ total: Math.round(successRateAll * 10) / 10,
312
+ },
313
+ failed: { last24h: failed24h, total: failedAll },
314
+ duration: {
315
+ last24h: {
316
+ avg: Number(duration24h[0]?.avg) || 0,
317
+ p95: Number(duration24h[0]?.p95) || 0,
318
+ },
319
+ total: {
320
+ avg: Number(durationAll[0]?.avg) || 0,
321
+ p95: Number(durationAll[0]?.p95) || 0,
322
+ },
323
+ },
324
+ running,
325
+ pending,
326
+ };
327
+ }
328
+
329
+ async getInstances(
330
+ workflowId: bigint,
331
+ params: { status?: string; skip?: number; limit?: number },
332
+ ) {
333
+ const where: any = { workflowId };
334
+ if (params.status) where.status = params.status;
335
+
336
+ const [items, total] = await Promise.all([
337
+ this.prisma.workflowInstance.findMany({
338
+ where,
339
+ orderBy: { createdAt: 'desc' },
340
+ skip: params.skip || 0,
341
+ take: params.limit || 20,
342
+ }),
343
+ this.prisma.workflowInstance.count({ where }),
344
+ ]);
345
+
346
+ return { items, total };
347
+ }
348
+
349
+ // ─── Execution Methods ──────────────────────────────────────────────────────
350
+
351
+ async executeWorkflow(
352
+ workflowId: bigint,
353
+ dto: { context?: Record<string, any>; versionId?: number },
354
+ creator: string,
355
+ temporalService: any,
356
+ ) {
357
+ // Get the workflow and its latest published version
358
+ const workflow = await this.prisma.workflow.findUnique({
359
+ where: { id: workflowId },
360
+ include: {
361
+ versions: {
362
+ orderBy: { version: 'desc' },
363
+ take: 1,
364
+ },
365
+ },
366
+ });
367
+
368
+ if (!workflow) throw new NotFoundException('Workflow not found');
369
+ if (workflow.status !== 'PUBLISHED') {
370
+ throw new Error('Workflow must be published before execution');
371
+ }
372
+ if (!workflow.isEnabled) {
373
+ throw new Error('Workflow is disabled');
374
+ }
375
+
376
+ // Determine which version to use
377
+ let version;
378
+ if (dto.versionId) {
379
+ version = await this.prisma.workflowVersion.findFirst({
380
+ where: { workflowId, version: dto.versionId },
381
+ });
382
+ } else {
383
+ version = workflow.versions[0];
384
+ }
385
+
386
+ if (!version) throw new NotFoundException('No published version found');
387
+
388
+ // Create a WorkflowInstance record
389
+ const instance = await this.prisma.workflowInstance.create({
390
+ data: {
391
+ workflowId,
392
+ versionId: version.id,
393
+ status: 'PENDING',
394
+ context: dto.context || {},
395
+ creator,
396
+ },
397
+ });
398
+
399
+ // Start the Temporal workflow
400
+ const { temporalRunId } = await temporalService.startWorkflow({
401
+ instanceId: Number(instance.id),
402
+ workflowId: Number(workflowId),
403
+ versionId: Number(version.id),
404
+ dsl: version.dsl,
405
+ context: dto.context || {},
406
+ });
407
+
408
+ // Update instance with Temporal run ID
409
+ await this.prisma.workflowInstance.update({
410
+ where: { id: instance.id },
411
+ data: { temporalRunId },
412
+ });
413
+
414
+ return {
415
+ instanceId: instance.id.toString(),
416
+ temporalRunId,
417
+ status: 'PENDING',
418
+ };
419
+ }
420
+
421
+ async getInstanceDetail(instanceId: bigint) {
422
+ const instance = await this.prisma.workflowInstance.findUnique({
423
+ where: { id: instanceId },
424
+ include: {
425
+ workflow: { select: { name: true } },
426
+ nodeExecutions: {
427
+ orderBy: { createdAt: 'asc' },
428
+ },
429
+ },
430
+ });
431
+
432
+ if (!instance) throw new NotFoundException('Instance not found');
433
+ return instance;
434
+ }
435
+
436
+ async cancelInstance(instanceId: bigint, temporalService: any) {
437
+ const instance = await this.prisma.workflowInstance.findUnique({
438
+ where: { id: instanceId },
439
+ });
440
+
441
+ if (!instance) throw new NotFoundException('Instance not found');
442
+ if (instance.status !== 'RUNNING' && instance.status !== 'PENDING') {
443
+ throw new Error('Can only cancel RUNNING or PENDING instances');
444
+ }
445
+
446
+ if (instance.temporalRunId) {
447
+ await temporalService.cancelWorkflow(instance.temporalRunId);
448
+ }
449
+
450
+ await this.prisma.workflowInstance.update({
451
+ where: { id: instanceId },
452
+ data: { status: 'CANCELLED', finishedAt: new Date() },
453
+ });
454
+
455
+ return { success: true };
456
+ }
457
+
458
+ async retryInstance(
459
+ instanceId: bigint,
460
+ creator: string,
461
+ temporalService: any,
462
+ ) {
463
+ const instance = await this.prisma.workflowInstance.findUnique({
464
+ where: { id: instanceId },
465
+ });
466
+
467
+ if (!instance) throw new NotFoundException('Instance not found');
468
+ if (instance.status !== 'FAILED') {
469
+ throw new Error('Can only retry FAILED instances');
470
+ }
471
+
472
+ // Create a new instance with the same context
473
+ return this.executeWorkflow(
474
+ instance.workflowId,
475
+ {
476
+ context: instance.context as Record<string, any>,
477
+ versionId: undefined,
478
+ },
479
+ creator,
480
+ temporalService,
481
+ );
482
+ }
483
+
484
+ async signalInstance(
485
+ instanceId: bigint,
486
+ dto: { signalName: string; payload: Record<string, any> },
487
+ temporalService: any,
488
+ ) {
489
+ const instance = await this.prisma.workflowInstance.findUnique({
490
+ where: { id: instanceId },
491
+ });
492
+
493
+ if (!instance) throw new NotFoundException('Instance not found');
494
+ if (!instance.temporalRunId) {
495
+ throw new Error('Instance has no Temporal run ID');
496
+ }
497
+
498
+ await temporalService.signalWorkflow(
499
+ instance.temporalRunId,
500
+ dto.signalName,
501
+ dto.payload,
502
+ );
503
+ return { success: true };
504
+ }
505
+ }
@@ -0,0 +1,22 @@
1
+ import { Test, TestingModule } from '@nestjs/testing';
2
+ import { WorkflowEventOutboxController } from './workflowEventOutbox.controller';
3
+ import { WorkflowEventOutboxService } from './workflowEventOutbox.service';
4
+
5
+ describe('WorkflowEventOutboxController', () => {
6
+ let controller: WorkflowEventOutboxController;
7
+
8
+ beforeEach(async () => {
9
+ const module: TestingModule = await Test.createTestingModule({
10
+ controllers: [WorkflowEventOutboxController],
11
+ providers: [WorkflowEventOutboxService],
12
+ }).compile();
13
+
14
+ controller = module.get<WorkflowEventOutboxController>(
15
+ WorkflowEventOutboxController,
16
+ );
17
+ });
18
+
19
+ it('should be defined', () => {
20
+ expect(controller).toBeDefined();
21
+ });
22
+ });
@@ -0,0 +1,147 @@
1
+ import {
2
+ ACAttrFilter,
3
+ ACGuard,
4
+ ApiPaginatedResponse,
5
+ PageDto,
6
+ PageMetaDto,
7
+ PageOptionsDto,
8
+ } from '@gadmin2n/nest-common';
9
+ import {
10
+ Body,
11
+ Controller,
12
+ Delete,
13
+ Inject,
14
+ NotFoundException,
15
+ Param,
16
+ ParseIntPipe,
17
+ Patch,
18
+ Post,
19
+ UseGuards,
20
+ } from '@nestjs/common';
21
+ import {
22
+ ApiCreatedResponse,
23
+ ApiNotFoundResponse,
24
+ ApiOkResponse,
25
+ ApiTags,
26
+ } from '@nestjs/swagger';
27
+ import { Prisma } from '@prisma/client';
28
+ import { UseRoles } from 'nest-access-control';
29
+ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
30
+ import { Logger } from 'winston';
31
+ import {
32
+ EffectedCount,
33
+ UploudResponse,
34
+ WorkflowEventOutboxFindManyArgs,
35
+ WorkflowEventOutboxWhereSelect,
36
+ } from '../../generated/workflowEventOutbox/dto/workflowEventOutbox-types.dto';
37
+ import { WorkflowEventOutboxEntity } from '../../generated/workflowEventOutbox/entities/workflowEventOutbox.entity';
38
+ import { WorkflowEventOutboxService } from './workflowEventOutbox.service';
39
+
40
+ import { ConfigService } from '@nestjs/config';
41
+
42
+ @UseGuards(ACGuard)
43
+ @Controller('workflowEventOutbox')
44
+ @ApiTags('workflowEventOutbox')
45
+ export class WorkflowEventOutboxController {
46
+ constructor(
47
+ private readonly workflowEventOutboxService: WorkflowEventOutboxService,
48
+ private readonly config: ConfigService,
49
+ @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
50
+ ) {}
51
+
52
+ @Post('createOne')
53
+ @UseRoles({
54
+ resource: 'workflowEventOutbox',
55
+ action: 'create',
56
+ possession: 'any',
57
+ })
58
+ @ApiCreatedResponse({ type: WorkflowEventOutboxEntity })
59
+ async createOne() {
60
+ // 该资源为只读,创建接口已禁用
61
+ throw new NotFoundException();
62
+ }
63
+
64
+ @Post('findMany')
65
+ @UseRoles({
66
+ resource: 'workflowEventOutbox',
67
+ action: 'read',
68
+ possession: 'any',
69
+ })
70
+ @ApiPaginatedResponse(WorkflowEventOutboxEntity)
71
+ async findMany(
72
+ @Body() body: WorkflowEventOutboxFindManyArgs,
73
+ @ACAttrFilter() acAttrFilter,
74
+ ): Promise<PageDto<WorkflowEventOutboxEntity>> {
75
+ const { itemCount, entities } =
76
+ await this.workflowEventOutboxService.findMany(
77
+ body as Prisma.WorkflowEventOutboxFindManyArgs,
78
+ );
79
+ const { skip = 0, take = 10 } =
80
+ body as Prisma.WorkflowEventOutboxFindManyArgs;
81
+ const pageOptionsDto: PageOptionsDto = { skip, take };
82
+ const pageMetaDto = new PageMetaDto({ itemCount, pageOptionsDto });
83
+
84
+ return new PageDto(
85
+ entities.map((item) => new WorkflowEventOutboxEntity(acAttrFilter(item))),
86
+ pageMetaDto,
87
+ );
88
+ }
89
+
90
+ @Post('/findUnique/:id')
91
+ @UseRoles({
92
+ resource: 'workflowEventOutbox',
93
+ action: 'read',
94
+ possession: 'any',
95
+ })
96
+ @ApiOkResponse({ type: WorkflowEventOutboxEntity })
97
+ @ApiNotFoundResponse({ description: 'Cannot found this record.' })
98
+ async findUnique(
99
+ @Param('id', ParseIntPipe) id: number,
100
+ @Body() body: WorkflowEventOutboxWhereSelect,
101
+ @ACAttrFilter() acAttrFilter,
102
+ ) {
103
+ const item = await this.workflowEventOutboxService.findUnique(id, body);
104
+ if (!item) {
105
+ throw new NotFoundException();
106
+ }
107
+ return new WorkflowEventOutboxEntity(acAttrFilter(item));
108
+ }
109
+
110
+ @Patch('updateUnique/:id')
111
+ @UseRoles({
112
+ resource: 'workflowEventOutbox',
113
+ action: 'update',
114
+ possession: 'any',
115
+ })
116
+ @ApiOkResponse({ type: WorkflowEventOutboxEntity })
117
+ @ApiNotFoundResponse({ description: 'Cannot found this record.' })
118
+ async updateUnique() {
119
+ // 该资源为只读,更新接口已禁用
120
+ throw new NotFoundException();
121
+ }
122
+
123
+ @Delete('deleteUnique/:id')
124
+ @UseRoles({
125
+ resource: 'workflowEventOutbox',
126
+ action: 'delete',
127
+ possession: 'any',
128
+ })
129
+ @ApiOkResponse({ type: EffectedCount })
130
+ @ApiNotFoundResponse({ description: 'Cannot found this record.' })
131
+ async deleteUnique() {
132
+ // 该资源为只读,删除接口已禁用
133
+ throw new NotFoundException();
134
+ }
135
+
136
+ @Post('upload')
137
+ @UseRoles({
138
+ resource: 'workflowEventOutbox',
139
+ action: 'create',
140
+ possession: 'any',
141
+ })
142
+ @ApiCreatedResponse({ type: UploudResponse })
143
+ async uploadFile() {
144
+ // 该资源为只读,上传接口已禁用
145
+ throw new NotFoundException();
146
+ }
147
+ }
@@ -0,0 +1,10 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { WorkflowEventOutboxController } from './workflowEventOutbox.controller';
3
+ import { WorkflowEventOutboxService } from './workflowEventOutbox.service';
4
+
5
+ @Module({
6
+ controllers: [WorkflowEventOutboxController],
7
+ providers: [WorkflowEventOutboxService],
8
+ exports: [WorkflowEventOutboxService],
9
+ })
10
+ export class WorkflowEventOutboxModule {}