@gadmin2n/schematics 0.0.72 → 0.0.74

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
@@ -40,6 +40,12 @@ class JobLogsQueryDto {
40
40
  export class AgendaController {
41
41
  constructor(private readonly agendaService: AgendaService) {}
42
42
 
43
+ @Get('overview')
44
+ @ApiOkResponse({ description: 'Get execution metrics overview (24h/total)' })
45
+ async getOverview() {
46
+ return this.agendaService.getMetricsOverview();
47
+ }
48
+
43
49
  @Get('jobs')
44
50
  @ApiOkResponse({ description: 'List jobs with overview stats' })
45
51
  async getJobs(@Query() query: JobsQueryDto) {
@@ -267,6 +267,85 @@ export class AgendaService
267
267
 
268
268
  // ─── UI management methods ────────────────────────────────────────────────
269
269
 
270
+ /** 获取执行健康指标概览 */
271
+ async getMetricsOverview() {
272
+ const now = new Date();
273
+ const last24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
274
+
275
+ const [totalAll, total24h, successAll, success24h, failedAll, failed24h] =
276
+ await Promise.all([
277
+ this.prisma.jobLog.count(),
278
+ this.prisma.jobLog.count({ where: { startAt: { gte: last24h } } }),
279
+ this.prisma.jobLog.count({ where: { status: 'success' } }),
280
+ this.prisma.jobLog.count({
281
+ where: { status: 'success', startAt: { gte: last24h } },
282
+ }),
283
+ this.prisma.jobLog.count({ where: { status: 'failed' } }),
284
+ this.prisma.jobLog.count({
285
+ where: { status: 'failed', startAt: { gte: last24h } },
286
+ }),
287
+ ]);
288
+
289
+ // Success rate
290
+ const denomAll = successAll + failedAll;
291
+ const denom24h = success24h + failed24h;
292
+ const successRateAll = denomAll > 0 ? (successAll / denomAll) * 100 : 0;
293
+ const successRate24h = denom24h > 0 ? (success24h / denom24h) * 100 : 0;
294
+
295
+ // Duration AVG/P95 via raw SQL (duration_ms is integer, result in ms)
296
+ const durationAll = await this.prisma.$queryRaw<
297
+ { avg: number | null; p95: number | null }[]
298
+ >`
299
+ SELECT
300
+ AVG(duration_ms) as avg,
301
+ PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY duration_ms) as p95
302
+ FROM t_job_log
303
+ WHERE status = 'success'
304
+ `;
305
+
306
+ const duration24h = await this.prisma.$queryRaw<
307
+ { avg: number | null; p95: number | null }[]
308
+ >`
309
+ SELECT
310
+ AVG(duration_ms) as avg,
311
+ PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY duration_ms) as p95
312
+ FROM t_job_log
313
+ WHERE status = 'success' AND start_at >= ${last24h}
314
+ `;
315
+
316
+ // Running/Queued from Agenda queue
317
+ let running = 0;
318
+ let queued = 0;
319
+ if (this.ready) {
320
+ const result = await this.agenda.queryJobs({});
321
+ for (const j of result.jobs) {
322
+ if (j.state === 'running') running++;
323
+ if (j.state === 'queued') queued++;
324
+ }
325
+ }
326
+
327
+ return {
328
+ totalRuns: { last24h: total24h, total: totalAll },
329
+ successRate: {
330
+ last24h: Math.round(successRate24h * 10) / 10,
331
+ total: Math.round(successRateAll * 10) / 10,
332
+ },
333
+ failed: { last24h: failed24h, total: failedAll },
334
+ duration: {
335
+ last24h: {
336
+ avg: Number(duration24h[0]?.avg) || 0,
337
+ p95: Number(duration24h[0]?.p95) || 0,
338
+ },
339
+ total: {
340
+ avg: Number(durationAll[0]?.avg) || 0,
341
+ p95: Number(durationAll[0]?.p95) || 0,
342
+ },
343
+ },
344
+ running,
345
+ queued,
346
+ };
347
+ }
348
+
270
349
  /** 获取任务列表 + 各状态概览 */
271
350
  async getJobs(params: GetJobsParams) {
272
351
  if (!this.ready) {
@@ -0,0 +1,20 @@
1
+ import { Test, TestingModule } from '@nestjs/testing';
2
+ import { AgendaJobController } from './agendaJob.controller';
3
+ import { AgendaJobService } from './agendaJob.service';
4
+
5
+ describe('AgendaJobController', () => {
6
+ let controller: AgendaJobController;
7
+
8
+ beforeEach(async () => {
9
+ const module: TestingModule = await Test.createTestingModule({
10
+ controllers: [AgendaJobController],
11
+ providers: [AgendaJobService],
12
+ }).compile();
13
+
14
+ controller = module.get<AgendaJobController>(AgendaJobController);
15
+ });
16
+
17
+ it('should be defined', () => {
18
+ expect(controller).toBeDefined();
19
+ });
20
+ });
@@ -0,0 +1,145 @@
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
+ ParseUUIDPipe,
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
+ AgendaJobFindManyArgs,
33
+ AgendaJobWhereSelect,
34
+ EffectedCount,
35
+ UploudResponse,
36
+ } from '../../generated/agendaJob/dto/agendaJob-types.dto';
37
+ import { AgendaJobEntity } from '../../generated/agendaJob/entities/agendaJob.entity';
38
+ import { AgendaJobService } from './agendaJob.service';
39
+
40
+ import { ConfigService } from '@nestjs/config';
41
+
42
+ @UseGuards(ACGuard)
43
+ @Controller('agendaJob')
44
+ @ApiTags('agendaJob')
45
+ export class AgendaJobController {
46
+ constructor(
47
+ private readonly agendaJobService: AgendaJobService,
48
+ private readonly config: ConfigService,
49
+ @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
50
+ ) {}
51
+
52
+ @Post('createOne')
53
+ @UseRoles({
54
+ resource: 'agendaJob',
55
+ action: 'create',
56
+ possession: 'any',
57
+ })
58
+ @ApiCreatedResponse({ type: AgendaJobEntity })
59
+ async createOne() {
60
+ // 该资源为只读,创建接口已禁用
61
+ throw new NotFoundException();
62
+ }
63
+
64
+ @Post('findMany')
65
+ @UseRoles({
66
+ resource: 'agendaJob',
67
+ action: 'read',
68
+ possession: 'any',
69
+ })
70
+ @ApiPaginatedResponse(AgendaJobEntity)
71
+ async findMany(
72
+ @Body() body: AgendaJobFindManyArgs,
73
+ @ACAttrFilter() acAttrFilter,
74
+ ): Promise<PageDto<AgendaJobEntity>> {
75
+ const { itemCount, entities } = await this.agendaJobService.findMany(
76
+ body as Prisma.AgendaJobFindManyArgs,
77
+ );
78
+ const { skip = 0, take = 10 } = body as Prisma.AgendaJobFindManyArgs;
79
+ const pageOptionsDto: PageOptionsDto = { skip, take };
80
+ const pageMetaDto = new PageMetaDto({ itemCount, pageOptionsDto });
81
+
82
+ return new PageDto(
83
+ entities.map((item) => new AgendaJobEntity(acAttrFilter(item))),
84
+ pageMetaDto,
85
+ );
86
+ }
87
+
88
+ @Post('/findUnique/:id')
89
+ @UseRoles({
90
+ resource: 'agendaJob',
91
+ action: 'read',
92
+ possession: 'any',
93
+ })
94
+ @ApiOkResponse({ type: AgendaJobEntity })
95
+ @ApiNotFoundResponse({ description: 'Cannot found this record.' })
96
+ async findUnique(
97
+ @Param('id', ParseUUIDPipe) id: string,
98
+ @Body() body: AgendaJobWhereSelect,
99
+ @ACAttrFilter() acAttrFilter,
100
+ ) {
101
+ const item = await this.agendaJobService.findUnique(id, body);
102
+ if (!item) {
103
+ throw new NotFoundException();
104
+ }
105
+ return new AgendaJobEntity(acAttrFilter(item));
106
+ }
107
+
108
+ @Patch('updateUnique/:id')
109
+ @UseRoles({
110
+ resource: 'agendaJob',
111
+ action: 'update',
112
+ possession: 'any',
113
+ })
114
+ @ApiOkResponse({ type: AgendaJobEntity })
115
+ @ApiNotFoundResponse({ description: 'Cannot found this record.' })
116
+ async updateUnique() {
117
+ // 该资源为只读,更新接口已禁用
118
+ throw new NotFoundException();
119
+ }
120
+
121
+ @Delete('deleteUnique/:id')
122
+ @UseRoles({
123
+ resource: 'agendaJob',
124
+ action: 'delete',
125
+ possession: 'any',
126
+ })
127
+ @ApiOkResponse({ type: EffectedCount })
128
+ @ApiNotFoundResponse({ description: 'Cannot found this record.' })
129
+ async deleteUnique() {
130
+ // 该资源为只读,删除接口已禁用
131
+ throw new NotFoundException();
132
+ }
133
+
134
+ @Post('upload')
135
+ @UseRoles({
136
+ resource: 'agendaJob',
137
+ action: 'create',
138
+ possession: 'any',
139
+ })
140
+ @ApiCreatedResponse({ type: UploudResponse })
141
+ async uploadFile() {
142
+ // 该资源为只读,上传接口已禁用
143
+ throw new NotFoundException();
144
+ }
145
+ }
@@ -0,0 +1,10 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { AgendaJobController } from './agendaJob.controller';
3
+ import { AgendaJobService } from './agendaJob.service';
4
+
5
+ @Module({
6
+ controllers: [AgendaJobController],
7
+ providers: [AgendaJobService],
8
+ exports: [AgendaJobService],
9
+ })
10
+ export class AgendaJobModule {}
@@ -9,11 +9,11 @@ jest.mock('nestjs-prisma', () => ({
9
9
  }));
10
10
 
11
11
  import { PrismaService } from 'nestjs-prisma';
12
- import { CanvasService } from './canvas.service';
12
+ import { AgendaJobService } from './agendaJob.service';
13
13
 
14
14
  // Mock PrismaService
15
15
  const mockPrismaService = {
16
- canvas: {
16
+ agendaJob: {
17
17
  create: jest.fn(),
18
18
  createMany: jest.fn(),
19
19
  findMany: jest.fn(),
@@ -42,8 +42,8 @@ const mockConfigService = {
42
42
  get: jest.fn(),
43
43
  };
44
44
 
45
- describe('CanvasService', () => {
46
- let service: CanvasService;
45
+ describe('AgendaJobService', () => {
46
+ let service: AgendaJobService;
47
47
  let prisma: typeof mockPrismaService;
48
48
 
49
49
  beforeEach(async () => {
@@ -52,14 +52,14 @@ describe('CanvasService', () => {
52
52
 
53
53
  const module: TestingModule = await Test.createTestingModule({
54
54
  providers: [
55
- CanvasService,
55
+ AgendaJobService,
56
56
  { provide: PrismaService, useValue: mockPrismaService },
57
57
  { provide: ConfigService, useValue: mockConfigService },
58
58
  { provide: WINSTON_MODULE_PROVIDER, useValue: mockLogger },
59
59
  ],
60
60
  }).compile();
61
61
 
62
- service = module.get<CanvasService>(CanvasService);
62
+ service = module.get<AgendaJobService>(AgendaJobService);
63
63
  prisma = mockPrismaService;
64
64
  });
65
65
 
@@ -68,23 +68,24 @@ describe('CanvasService', () => {
68
68
  });
69
69
 
70
70
  describe('createOne', () => {
71
- it('should create a single canvas record', async () => {
71
+ it('should create a single agendaJob record', async () => {
72
72
  const createArgs = {
73
73
  data: {
74
74
  name: 'test_name',
75
- userId: 'test_userId',
76
- items: '[]',
77
- layout: '[]',
75
+ priority: 0,
76
+ nextRunAt: new Date(),
77
+ type: 'normal',
78
+ lockedAt: new Date(),
78
79
  },
79
80
  };
80
81
  const expectedResult = { id: 'test-id-123', ...createArgs.data };
81
82
 
82
- prisma.canvas.create.mockResolvedValue(expectedResult);
83
+ prisma.agendaJob.create.mockResolvedValue(expectedResult);
83
84
 
84
85
  const result = await service.createOne(createArgs);
85
86
 
86
- expect(prisma.canvas.create).toHaveBeenCalledWith(createArgs);
87
- expect(prisma.canvas.create).toHaveBeenCalledTimes(1);
87
+ expect(prisma.agendaJob.create).toHaveBeenCalledWith(createArgs);
88
+ expect(prisma.agendaJob.create).toHaveBeenCalledTimes(1);
88
89
  expect(result).toEqual(expectedResult);
89
90
  });
90
91
 
@@ -92,14 +93,15 @@ describe('CanvasService', () => {
92
93
  const createArgs = {
93
94
  data: {
94
95
  name: 'test_name',
95
- userId: 'test_userId',
96
- items: '[]',
97
- layout: '[]',
96
+ priority: 0,
97
+ nextRunAt: new Date(),
98
+ type: 'normal',
99
+ lockedAt: new Date(),
98
100
  },
99
101
  };
100
102
  const error = new Error('Database connection failed');
101
103
 
102
- prisma.canvas.create.mockRejectedValue(error);
104
+ prisma.agendaJob.create.mockRejectedValue(error);
103
105
 
104
106
  await expect(service.createOne(createArgs)).rejects.toThrow(
105
107
  'Database connection failed',
@@ -108,28 +110,30 @@ describe('CanvasService', () => {
108
110
  });
109
111
 
110
112
  describe('createMany', () => {
111
- it('should create multiple canvas records with skipDuplicates', async () => {
113
+ it('should create multiple agendaJob records with skipDuplicates', async () => {
112
114
  const data = [
113
115
  {
114
116
  name: 'test_name',
115
- userId: 'test_userId',
116
- items: '[]',
117
- layout: '[]',
117
+ priority: 0,
118
+ nextRunAt: new Date(),
119
+ type: 'normal',
120
+ lockedAt: new Date(),
118
121
  },
119
122
  {
120
123
  name: 'test_name',
121
- userId: 'test_userId',
122
- items: '[]',
123
- layout: '[]',
124
+ priority: 0,
125
+ nextRunAt: new Date(),
126
+ type: 'normal',
127
+ lockedAt: new Date(),
124
128
  },
125
129
  ];
126
130
  const expectedResult = { count: 2 };
127
131
 
128
- prisma.canvas.createMany.mockResolvedValue(expectedResult);
132
+ prisma.agendaJob.createMany.mockResolvedValue(expectedResult);
129
133
 
130
134
  const result = await service.createMany(data);
131
135
 
132
- expect(prisma.canvas.createMany).toHaveBeenCalledWith({
136
+ expect(prisma.agendaJob.createMany).toHaveBeenCalledWith({
133
137
  data,
134
138
  skipDuplicates: true,
135
139
  });
@@ -146,15 +150,15 @@ describe('CanvasService', () => {
146
150
  };
147
151
  const mockEntities = [{ id: 'test-id-123' }, { id: 'test-id-123' }];
148
152
 
149
- prisma.canvas.count.mockResolvedValue(2);
150
- prisma.canvas.findMany.mockResolvedValue(mockEntities);
153
+ prisma.agendaJob.count.mockResolvedValue(2);
154
+ prisma.agendaJob.findMany.mockResolvedValue(mockEntities);
151
155
 
152
156
  const result = await service.findMany(findArgs);
153
157
 
154
- expect(prisma.canvas.count).toHaveBeenCalledWith({
158
+ expect(prisma.agendaJob.count).toHaveBeenCalledWith({
155
159
  where: findArgs.where,
156
160
  });
157
- expect(prisma.canvas.findMany).toHaveBeenCalledWith(findArgs);
161
+ expect(prisma.agendaJob.findMany).toHaveBeenCalledWith(findArgs);
158
162
  expect(result).toEqual({
159
163
  itemCount: 2,
160
164
  entities: mockEntities,
@@ -164,8 +168,8 @@ describe('CanvasService', () => {
164
168
  it('should return empty result when no records found', async () => {
165
169
  const findArgs = { where: {} };
166
170
 
167
- prisma.canvas.count.mockResolvedValue(0);
168
- prisma.canvas.findMany.mockResolvedValue([]);
171
+ prisma.agendaJob.count.mockResolvedValue(0);
172
+ prisma.agendaJob.findMany.mockResolvedValue([]);
169
173
 
170
174
  const result = await service.findMany(findArgs);
171
175
 
@@ -174,16 +178,16 @@ describe('CanvasService', () => {
174
178
  });
175
179
 
176
180
  describe('findUnique', () => {
177
- it('should find a single canvas record by id', async () => {
181
+ it('should find a single agendaJob record by id', async () => {
178
182
  const id = 'test-id-123';
179
183
  const select = { id: true };
180
184
  const expectedResult = { id: 'test-id-123' };
181
185
 
182
- prisma.canvas.findUnique.mockResolvedValue(expectedResult);
186
+ prisma.agendaJob.findUnique.mockResolvedValue(expectedResult);
183
187
 
184
188
  const result = await service.findUnique(id, select);
185
189
 
186
- expect(prisma.canvas.findUnique).toHaveBeenCalledWith({
190
+ expect(prisma.agendaJob.findUnique).toHaveBeenCalledWith({
187
191
  where: { id },
188
192
  select,
189
193
  });
@@ -191,7 +195,7 @@ describe('CanvasService', () => {
191
195
  });
192
196
 
193
197
  it('should return null when record not found', async () => {
194
- prisma.canvas.findUnique.mockResolvedValue(null);
198
+ prisma.agendaJob.findUnique.mockResolvedValue(null);
195
199
 
196
200
  const result = await service.findUnique('test-id-123', { id: true });
197
201
 
@@ -200,21 +204,22 @@ describe('CanvasService', () => {
200
204
  });
201
205
 
202
206
  describe('updateUnique', () => {
203
- it('should update a single canvas record', async () => {
207
+ it('should update a single agendaJob record', async () => {
204
208
  const id = 'test-id-123';
205
209
  const data = {
206
210
  name: 'test_name',
207
- userId: 'test_userId',
208
- items: '[]',
209
- layout: '[]',
211
+ priority: 0,
212
+ nextRunAt: new Date(),
213
+ type: 'normal',
214
+ lockedAt: new Date(),
210
215
  };
211
216
  const expectedResult = { id: 'test-id-123', ...data };
212
217
 
213
- prisma.canvas.update.mockResolvedValue(expectedResult);
218
+ prisma.agendaJob.update.mockResolvedValue(expectedResult);
214
219
 
215
220
  const result = await service.updateUnique(id, data);
216
221
 
217
- expect(prisma.canvas.update).toHaveBeenCalledWith({
222
+ expect(prisma.agendaJob.update).toHaveBeenCalledWith({
218
223
  where: { id },
219
224
  data,
220
225
  });
@@ -223,95 +228,96 @@ describe('CanvasService', () => {
223
228
  });
224
229
 
225
230
  describe('updateMany', () => {
226
- it('should update multiple canvas records', async () => {
231
+ it('should update multiple agendaJob records', async () => {
227
232
  const updateArgs = {
228
233
  where: {},
229
234
  data: {
230
235
  name: 'test_name',
231
- userId: 'test_userId',
232
- items: '[]',
233
- layout: '[]',
236
+ priority: 0,
237
+ nextRunAt: new Date(),
238
+ type: 'normal',
239
+ lockedAt: new Date(),
234
240
  },
235
241
  };
236
242
  const expectedResult = { count: 5 };
237
243
 
238
- prisma.canvas.updateMany.mockResolvedValue(expectedResult);
244
+ prisma.agendaJob.updateMany.mockResolvedValue(expectedResult);
239
245
 
240
246
  const result = await service.updateMany(updateArgs);
241
247
 
242
- expect(prisma.canvas.updateMany).toHaveBeenCalledWith(updateArgs);
248
+ expect(prisma.agendaJob.updateMany).toHaveBeenCalledWith(updateArgs);
243
249
  expect(result).toEqual(expectedResult);
244
250
  });
245
251
  });
246
252
 
247
253
  describe('deleteUnique', () => {
248
- it('should delete a single canvas record and return count', async () => {
254
+ it('should delete a single agendaJob record and return count', async () => {
249
255
  const id = 'test-id-123';
250
256
 
251
- prisma.canvas.delete.mockResolvedValue({ id: 'test-id-123' });
257
+ prisma.agendaJob.delete.mockResolvedValue({ id: 'test-id-123' });
252
258
 
253
259
  const result = await service.deleteUnique(id);
254
260
 
255
- expect(prisma.canvas.delete).toHaveBeenCalledWith({ where: { id } });
261
+ expect(prisma.agendaJob.delete).toHaveBeenCalledWith({ where: { id } });
256
262
  expect(result).toEqual({ count: 1 });
257
263
  });
258
264
  });
259
265
 
260
266
  describe('deleteMany', () => {
261
- it('should delete multiple canvas records', async () => {
267
+ it('should delete multiple agendaJob records', async () => {
262
268
  const deleteArgs = { where: {} };
263
269
  const expectedResult = { count: 3 };
264
270
 
265
- prisma.canvas.deleteMany.mockResolvedValue(expectedResult);
271
+ prisma.agendaJob.deleteMany.mockResolvedValue(expectedResult);
266
272
 
267
273
  const result = await service.deleteMany(deleteArgs);
268
274
 
269
- expect(prisma.canvas.deleteMany).toHaveBeenCalledWith(deleteArgs);
275
+ expect(prisma.agendaJob.deleteMany).toHaveBeenCalledWith(deleteArgs);
270
276
  expect(result).toEqual(expectedResult);
271
277
  });
272
278
  });
273
279
 
274
280
  describe('count', () => {
275
- it('should count canvas records with filter', async () => {
281
+ it('should count agendaJob records with filter', async () => {
276
282
  const countArgs = { where: {} };
277
283
 
278
- prisma.canvas.count.mockResolvedValue(10);
284
+ prisma.agendaJob.count.mockResolvedValue(10);
279
285
 
280
286
  const result = await service.count(countArgs);
281
287
 
282
- expect(prisma.canvas.count).toHaveBeenCalledWith(countArgs);
288
+ expect(prisma.agendaJob.count).toHaveBeenCalledWith(countArgs);
283
289
  expect(result).toBe(10);
284
290
  });
285
291
 
286
292
  it('should count all records when no filter provided', async () => {
287
- prisma.canvas.count.mockResolvedValue(100);
293
+ prisma.agendaJob.count.mockResolvedValue(100);
288
294
 
289
295
  const result = await service.count();
290
296
 
291
- expect(prisma.canvas.count).toHaveBeenCalledWith(undefined);
297
+ expect(prisma.agendaJob.count).toHaveBeenCalledWith(undefined);
292
298
  expect(result).toBe(100);
293
299
  });
294
300
  });
295
301
 
296
302
  describe('groupBy', () => {
297
- it('should group canvas records by specified field', async () => {
303
+ it('should group agendaJob records by specified field', async () => {
298
304
  const groupByArgs = {
299
305
  by: ['id'],
300
306
  _count: { id: true },
301
307
  };
302
308
  const expectedResult = [{ id: 'test-id-123', _count: { id: 50 } }];
303
309
 
304
- prisma.canvas.groupBy.mockResolvedValue(expectedResult);
310
+ prisma.agendaJob.groupBy.mockResolvedValue(expectedResult);
305
311
 
306
312
  const result = await service.groupBy(groupByArgs as any);
307
313
 
308
- expect(prisma.canvas.groupBy).toHaveBeenCalledWith(groupByArgs);
314
+ expect(prisma.agendaJob.groupBy).toHaveBeenCalledWith(groupByArgs);
309
315
  expect(result).toEqual(expectedResult);
310
316
  });
311
317
  });
312
318
 
313
319
  describe('aggregate', () => {
314
- it('should aggregate canvas records', async () => {
320
+ it('should aggregate agendaJob records', async () => {
315
321
  const aggregateArgs = {
316
322
  _count: true,
317
323
  _max: { id: true },
@@ -323,11 +329,11 @@ describe('CanvasService', () => {
323
329
  _min: { id: 'test-id-123' },
324
330
  };
325
331
 
326
- prisma.canvas.aggregate.mockResolvedValue(expectedResult);
332
+ prisma.agendaJob.aggregate.mockResolvedValue(expectedResult);
327
333
 
328
334
  const result = await service.aggregate(aggregateArgs);
329
335
 
330
- expect(prisma.canvas.aggregate).toHaveBeenCalledWith(aggregateArgs);
336
+ expect(prisma.agendaJob.aggregate).toHaveBeenCalledWith(aggregateArgs);
331
337
  expect(result).toEqual(expectedResult);
332
338
  });
333
339
  });