@gadmin2n/schematics 0.0.145 → 0.0.146

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.
@@ -45,13 +45,5 @@ export async function seedWorkflowOutbox(prisma: PrismaClient) {
45
45
  $$;
46
46
  `);
47
47
 
48
- console.log('Seeding workflow outbox partial index...');
49
-
50
- await prisma.$executeRawUnsafe(`
51
- CREATE INDEX IF NOT EXISTS idx_outbox_unprocessed
52
- ON t_workflow_event_outbox (created_at)
53
- WHERE processed = FALSE;
54
- `);
55
-
56
48
  console.log('Workflow outbox initialized.');
57
49
  }
@@ -15,9 +15,9 @@ import { seedWorkflowOutbox } from './workflow-outbox';
15
15
  * 1. 菜单结构:workflow 父分组及其管理、审批任务页面
16
16
  * 2. resource: workflow / workflowNodeInstance
17
17
  * 3. PageResource 绑定与 SYSTEM_ADMIN 授权
18
- * 4. 业务数据:node-types / workflows / Outbox index
18
+ * 4. 业务数据:node-types / Outbox 旧触发器清理
19
19
  *
20
- * 业务数据 seed 函数仍来自 ./workflow-{node-types,workflows,outbox}.ts
20
+ * 业务数据 seed 函数仍来自 ./workflow-{node-types,outbox}.ts
21
21
  * (文件较大,保持独立但只被本入口 import,不再由 seed/index.ts 直接调用)。
22
22
  *
23
23
  * 前置:seedBootstrap 已建好 SYSTEM_ADMIN。
@@ -494,44 +494,34 @@ export class AgendaService
494
494
  };
495
495
  }
496
496
 
497
- /** Requeue jobs — reschedule them to run immediately */
497
+ /**
498
+ * 重排任务 —— 将原实例的 nextRunAt 重置为现在,同一行重跑。
499
+ * 不用 cancel + now(会向队列插入新行,且销毁周期任务的 repeatInterval)。
500
+ */
498
501
  async requeueByIds(jobIds: string[]) {
499
502
  let affected = 0;
500
503
  for (const id of jobIds) {
501
504
  const result = await this.agenda.queryJobs({ id });
502
- if (result.jobs.length > 0) {
503
- const job = result.jobs[0];
504
- const liveJobs = await this.agenda.queryJobs({ id } as any);
505
- if (liveJobs.jobs.length > 0) {
506
- await this.agenda.cancel({ _id: id } as any);
507
- await this.agenda.now(job.name, job.data as Record<string, any>);
508
- affected++;
509
- }
510
- }
505
+ if (result.jobs.length === 0) continue;
506
+ // 用完整 attrs 重建 Job 实例,save() 走 UPDATE 而非 INSERT
507
+ const job = new Job(this.agenda, { ...result.jobs[0] } as any);
508
+ job.schedule(new Date());
509
+ await job.save();
510
+ affected++;
511
511
  }
512
512
  return { affected };
513
513
  }
514
514
 
515
- /** Retry failed jobs */
515
+ /** Retry failed jobs —— 同样重置原实例,不清除周期计划 */
516
516
  async retryByIds(jobIds: string[]) {
517
- let affected = 0;
518
- for (const id of jobIds) {
519
- const result = await this.agenda.queryJobs({ id });
520
- if (result.jobs.length > 0) {
521
- const job = result.jobs[0];
522
- await this.agenda.cancel({ _id: id } as any);
523
- await this.agenda.now(job.name, job.data as Record<string, any>);
524
- affected++;
525
- }
526
- }
527
- return { affected };
517
+ return this.requeueByIds(jobIds);
528
518
  }
529
519
 
530
- /** Delete jobs by IDs */
520
+ /** Delete jobs by IDs(postgres backend 的 cancel 只认 { id },{_id} 会静默匹配 0 条) */
531
521
  async deleteByIds(jobIds: string[]) {
532
522
  let affected = 0;
533
523
  for (const id of jobIds) {
534
- const count = await this.agenda.cancel({ _id: id } as any);
524
+ const count = await this.agenda.cancel({ id });
535
525
  affected += count ?? 0;
536
526
  }
537
527
  return { affected };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gadmin2n/schematics",
3
- "version": "0.0.145",
3
+ "version": "0.0.146",
4
4
  "description": "Gadmin - modern, fast, powerful node.js web framework (@schematics)",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -1,863 +0,0 @@
1
- import { Prisma, PrismaClient } from '@prisma/client';
2
-
3
- /**
4
- * Mock workflow data with DSL, versions, instances, and node executions.
5
- */
6
- const WORKFLOWS = [
7
- {
8
- code: 'daily_data_sync',
9
- name: '每日数据同步',
10
- description: '每天凌晨2点从外部API同步用户数据到本地数据库',
11
- status: 'PUBLISHED' as const,
12
- isEnabled: true,
13
- creator: 'admin',
14
- dsl: {
15
- nodes: [
16
- {
17
- id: 'trigger_1',
18
- type: 'cron_trigger',
19
- label: 'Cron 触发',
20
- position: { x: 100, y: 200 },
21
- },
22
- {
23
- id: 'action_1',
24
- type: 'http_request',
25
- label: '拉取外部数据',
26
- position: { x: 350, y: 200 },
27
- config: {},
28
- },
29
- {
30
- id: 'action_2',
31
- type: 'code',
32
- label: '数据转换',
33
- position: { x: 600, y: 200 },
34
- config: {},
35
- },
36
- {
37
- id: 'action_3',
38
- type: 'db_execute',
39
- label: '写入数据库',
40
- position: { x: 850, y: 200 },
41
- config: {},
42
- },
43
- ],
44
- edges: [
45
- { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
46
- { id: 'e-action_1-action_2', source: 'action_1', target: 'action_2' },
47
- { id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
48
- ],
49
- },
50
- },
51
- {
52
- code: 'user_reg_approval',
53
- name: '新用户注册审批',
54
- description: '新用户注册后触发审批流程,管理员审批通过后激活用户',
55
- status: 'PUBLISHED' as const,
56
- isEnabled: true,
57
- creator: 'admin',
58
- dsl: {
59
- nodes: [
60
- {
61
- id: 'trigger_1',
62
- type: 'webhook_trigger',
63
- label: 'Webhook 接收注册',
64
- position: { x: 100, y: 200 },
65
- },
66
- {
67
- id: 'approval_1',
68
- type: 'approval',
69
- label: '管理员审批',
70
- position: { x: 600, y: 200 },
71
- config: {},
72
- },
73
- {
74
- id: 'condition_1',
75
- type: 'if_else',
76
- label: '审批结果判断',
77
- position: { x: 850, y: 200 },
78
- config: {
79
- conditions: [
80
- {
81
- id: 'approval-approved',
82
- left: { source: 'INPUT', path: 'response.approved' },
83
- operator: 'EQ',
84
- right: {
85
- source: 'FIXED',
86
- valueType: 'BOOLEAN',
87
- value: 'true',
88
- },
89
- },
90
- ],
91
- combineOperation: 'AND',
92
- },
93
- },
94
- {
95
- id: 'action_2',
96
- type: 'db_execute',
97
- label: '激活用户',
98
- position: { x: 1100, y: 100 },
99
- config: {},
100
- },
101
- ],
102
- edges: [
103
- {
104
- id: 'e-trigger_1-approval_1',
105
- source: 'trigger_1',
106
- target: 'approval_1',
107
- },
108
- {
109
- id: 'e-approval_1-condition_1',
110
- source: 'approval_1',
111
- target: 'condition_1',
112
- },
113
- {
114
- id: 'e-condition_1-action_2',
115
- source: 'condition_1',
116
- target: 'action_2',
117
- label: 'true',
118
- },
119
- ],
120
- },
121
- },
122
- {
123
- code: 'error_monitor_alert',
124
- name: '异常监控告警',
125
- description: '监听系统事件并记录异常监控结果',
126
- status: 'PUBLISHED' as const,
127
- isEnabled: true,
128
- creator: 'system',
129
- dsl: {
130
- nodes: [
131
- {
132
- id: 'trigger_1',
133
- type: 'event_trigger',
134
- label: '监听错误事件',
135
- position: { x: 100, y: 200 },
136
- config: {
137
- registrationCode: 'admin-audit-log-insert',
138
- processingMode: 'PARALLEL',
139
- },
140
- },
141
- {
142
- id: 'action_1',
143
- type: 'db_execute',
144
- label: '查询近5分钟错误数',
145
- position: { x: 350, y: 200 },
146
- config: {
147
- allowWrite: false,
148
- sql: "SELECT COUNT(*) AS error_count FROM t_admin_audit_log WHERE created_at >= NOW() - INTERVAL '5 minutes'",
149
- parameters: [],
150
- timeoutSeconds: 30,
151
- },
152
- },
153
- {
154
- id: 'condition_1',
155
- type: 'if_else',
156
- label: '错误率 > 5%?',
157
- position: { x: 600, y: 200 },
158
- config: {
159
- conditions: [
160
- {
161
- id: 'error-count-high',
162
- left: { source: 'INPUT', path: 'rows.0.error_count' },
163
- operator: 'GT',
164
- right: {
165
- source: 'FIXED',
166
- valueType: 'NUMBER',
167
- value: '5',
168
- },
169
- },
170
- ],
171
- combineOperation: 'AND',
172
- },
173
- },
174
- {
175
- id: 'action_3',
176
- type: 'code',
177
- label: '记录日志',
178
- position: { x: 850, y: 300 },
179
- config: {},
180
- },
181
- ],
182
- edges: [
183
- { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
184
- {
185
- id: 'e-action_1-condition_1',
186
- source: 'action_1',
187
- target: 'condition_1',
188
- },
189
- {
190
- id: 'e-condition_1-action_3',
191
- source: 'condition_1',
192
- target: 'action_3',
193
- label: 'false',
194
- },
195
- ],
196
- },
197
- },
198
- {
199
- code: 'batch_data_export',
200
- name: '批量数据导出',
201
- description: '手动触发导出指定表的数据为 CSV 文件',
202
- status: 'DRAFT' as const,
203
- isEnabled: false,
204
- creator: 'admin',
205
- dsl: {
206
- nodes: [
207
- {
208
- id: 'trigger_1',
209
- type: 'manual_trigger',
210
- label: '手动触发',
211
- position: { x: 100, y: 200 },
212
- },
213
- {
214
- id: 'action_1',
215
- type: 'db_execute',
216
- label: '查询数据',
217
- position: { x: 350, y: 200 },
218
- config: {
219
- allowWrite: false,
220
- sql: 'SELECT * FROM t_admin_audit_log',
221
- parameters: [],
222
- timeoutSeconds: 30,
223
- },
224
- },
225
- {
226
- id: 'loop_1',
227
- type: 'for_each',
228
- label: '遍历分页',
229
- position: { x: 600, y: 200 },
230
- config: {
231
- itemsBinding: { source: 'INPUT', path: 'rows' },
232
- execution: { mode: 'SEQUENTIAL' },
233
- errorPolicy: 'STOP',
234
- },
235
- },
236
- {
237
- id: 'action_2',
238
- type: 'code',
239
- label: '转换为CSV',
240
- position: { x: 850, y: 200 },
241
- config: {},
242
- },
243
- ],
244
- edges: [
245
- { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
246
- { id: 'e-action_1-loop_1', source: 'action_1', target: 'loop_1' },
247
- {
248
- id: 'e-loop_1-action_2',
249
- kind: 'CALL',
250
- pairId: 'pair-loop_1-action_2',
251
- source: 'loop_1',
252
- target: 'action_2',
253
- sourceHandle: 'start',
254
- targetHandle: 'data-in',
255
- },
256
- {
257
- id: 'e-action_2-loop_1',
258
- kind: 'RETURN',
259
- pairId: 'pair-loop_1-action_2',
260
- source: 'action_2',
261
- target: 'loop_1',
262
- sourceHandle: 'result-out',
263
- targetHandle: 'end',
264
- valueType: 'JSON',
265
- },
266
- ],
267
- },
268
- },
269
- {
270
- code: 'multi_env_deploy',
271
- name: '多环境部署流水线',
272
- description: '并行部署到staging和production环境,需人工审批production',
273
- status: 'PUBLISHED' as const,
274
- isEnabled: true,
275
- creator: 'devops',
276
- dsl: {
277
- nodes: [
278
- {
279
- id: 'trigger_1',
280
- type: 'webhook_trigger',
281
- label: 'CI 回调',
282
- position: { x: 100, y: 200 },
283
- },
284
- {
285
- id: 'parallel_1',
286
- type: 'parallel',
287
- label: '并行部署',
288
- position: { x: 350, y: 200 },
289
- config: {
290
- branches: [
291
- { id: 'staging', label: '部署 Staging' },
292
- { id: 'production', label: '部署 Production' },
293
- ],
294
- },
295
- },
296
- {
297
- id: 'action_1',
298
- type: 'http_request',
299
- label: '部署 Staging',
300
- position: { x: 600, y: 100 },
301
- config: {},
302
- },
303
- {
304
- id: 'action_2',
305
- type: 'http_request',
306
- label: '部署 Production',
307
- position: { x: 850, y: 300 },
308
- config: {},
309
- },
310
- ],
311
- edges: [
312
- {
313
- id: 'e-trigger_1-parallel_1',
314
- source: 'trigger_1',
315
- target: 'parallel_1',
316
- },
317
- {
318
- id: 'e-parallel_1-action_1',
319
- kind: 'CALL',
320
- pairId: 'pair-staging',
321
- source: 'parallel_1',
322
- target: 'action_1',
323
- sourceHandle: 'staging-start',
324
- targetHandle: 'request-in',
325
- },
326
- {
327
- id: 'e-action_1-parallel_1',
328
- kind: 'RETURN',
329
- pairId: 'pair-staging',
330
- source: 'action_1',
331
- target: 'parallel_1',
332
- sourceHandle: 'response-out',
333
- targetHandle: 'staging-end',
334
- valueType: 'JSON',
335
- },
336
- {
337
- id: 'e-parallel_1-action_2',
338
- kind: 'CALL',
339
- pairId: 'pair-production',
340
- source: 'parallel_1',
341
- target: 'action_2',
342
- sourceHandle: 'production-start',
343
- targetHandle: 'request-in',
344
- },
345
- {
346
- id: 'e-action_2-parallel_1',
347
- kind: 'RETURN',
348
- pairId: 'pair-production',
349
- source: 'action_2',
350
- target: 'parallel_1',
351
- sourceHandle: 'response-out',
352
- targetHandle: 'production-end',
353
- valueType: 'JSON',
354
- },
355
- ],
356
- },
357
- },
358
- {
359
- code: 'user_role_change',
360
- name: '用户角色变更审计',
361
- description: '当 t_admin_user_role 表中用户角色发生变更时,记录审计日志',
362
- status: 'PUBLISHED' as const,
363
- isEnabled: true,
364
- creator: 'admin',
365
- dsl: {
366
- nodes: [
367
- {
368
- id: 'trigger_1',
369
- type: 'event_trigger',
370
- label: '监听角色变更事件',
371
- position: { x: 100, y: 200 },
372
- config: {
373
- registrationCode: 'admin-user-role-update',
374
- processingMode: 'PARALLEL',
375
- },
376
- },
377
- {
378
- id: 'action_1',
379
- type: 'db_execute',
380
- label: '查询用户详情',
381
- position: { x: 350, y: 200 },
382
- config: {
383
- allowWrite: false,
384
- sql: 'SELECT userid, username, roles FROM t_admin_user_role WHERE userid = $1',
385
- parameters: [{ source: 'INPUT', path: 'userid' }],
386
- timeoutSeconds: 30,
387
- },
388
- },
389
- {
390
- id: 'action_2',
391
- type: 'code',
392
- label: '对比角色差异',
393
- position: { x: 600, y: 200 },
394
- config: {
395
- script:
396
- 'const prev = JSON.parse(input.previousRoles); const curr = JSON.parse(input.currentRoles); return { added: curr.filter(r => !prev.includes(r)), removed: prev.filter(r => !curr.includes(r)) };',
397
- },
398
- },
399
- {
400
- id: 'condition_1',
401
- type: 'if_else',
402
- label: '是否涉及ADMIN角色',
403
- position: { x: 850, y: 200 },
404
- config: {
405
- conditions: [
406
- {
407
- id: 'contains-system-admin',
408
- left: { source: 'INPUT', path: 'added' },
409
- operator: 'CONTAINS',
410
- right: {
411
- source: 'FIXED',
412
- valueType: 'STRING',
413
- value: 'SYSTEM_ADMIN',
414
- },
415
- },
416
- ],
417
- combineOperation: 'AND',
418
- },
419
- },
420
- {
421
- id: 'action_4',
422
- type: 'db_execute',
423
- label: '写入审计日志',
424
- position: { x: 1100, y: 300 },
425
- config: { operation: 'insert', table: 't_admin_audit_log' },
426
- },
427
- ],
428
- edges: [
429
- { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
430
- { id: 'e-action_1-action_2', source: 'action_1', target: 'action_2' },
431
- {
432
- id: 'e-action_2-condition_1',
433
- source: 'action_2',
434
- target: 'condition_1',
435
- },
436
- {
437
- id: 'e-condition_1-action_4-true',
438
- source: 'condition_1',
439
- target: 'action_4',
440
- label: 'true',
441
- },
442
- {
443
- id: 'e-condition_1-action_4',
444
- source: 'condition_1',
445
- target: 'action_4',
446
- label: 'false',
447
- },
448
- ],
449
- },
450
- },
451
- {
452
- code: 'event_remind_sync',
453
- name: 'Event活动提醒与同步',
454
- description: '当 t_event 表新增活动或活动即将开始时,自动同步到 ServiceNow',
455
- status: 'PUBLISHED' as const,
456
- isEnabled: true,
457
- creator: 'admin',
458
- dsl: {
459
- nodes: [
460
- {
461
- id: 'trigger_1',
462
- type: 'cron_trigger',
463
- label: '每日9点检查',
464
- position: { x: 100, y: 200 },
465
- config: { cronExpressions: ['0 9 * * *'], timezone: 'Asia/Shanghai' },
466
- },
467
- {
468
- id: 'action_1',
469
- type: 'db_execute',
470
- label: '查询3天内活动',
471
- position: { x: 350, y: 200 },
472
- config: {
473
- allowWrite: false,
474
- sql: "SELECT * FROM t_event WHERE start_date BETWEEN NOW() AND NOW() + INTERVAL '3 days'",
475
- parameters: [],
476
- timeoutSeconds: 30,
477
- },
478
- },
479
- {
480
- id: 'condition_1',
481
- type: 'if_else',
482
- label: '有即将开始的活动?',
483
- position: { x: 600, y: 200 },
484
- config: {
485
- conditions: [
486
- {
487
- id: 'has-upcoming-events',
488
- left: { source: 'INPUT', path: 'rowCount' },
489
- operator: 'GT',
490
- right: {
491
- source: 'FIXED',
492
- valueType: 'NUMBER',
493
- value: '0',
494
- },
495
- },
496
- ],
497
- combineOperation: 'AND',
498
- },
499
- },
500
- {
501
- id: 'loop_1',
502
- type: 'for_each',
503
- label: '遍历活动列表',
504
- position: { x: 850, y: 150 },
505
- config: {
506
- itemsBinding: { source: 'INPUT', path: 'rows' },
507
- execution: { mode: 'PARALLEL', concurrency: 3 },
508
- errorPolicy: 'STOP',
509
- },
510
- },
511
- {
512
- id: 'action_2',
513
- type: 'http_request',
514
- label: '同步到ServiceNow',
515
- position: { x: 1350, y: 80 },
516
- config: {
517
- url: 'https://servicenow.example.com/api/events',
518
- method: 'POST',
519
- },
520
- },
521
- {
522
- id: 'action_3',
523
- type: 'db_execute',
524
- label: '更新sn_sys_id',
525
- position: { x: 1600, y: 80 },
526
- config: { operation: 'update', table: 't_event', where: 'id = $1' },
527
- },
528
- {
529
- id: 'action_5',
530
- type: 'code',
531
- label: '记录检查完成',
532
- position: { x: 600, y: 350 },
533
- config: {
534
- script: 'return { checked: true, noUpcomingEvents: true };',
535
- },
536
- },
537
- ],
538
- edges: [
539
- { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
540
- {
541
- id: 'e-action_1-condition_1',
542
- source: 'action_1',
543
- target: 'condition_1',
544
- },
545
- {
546
- id: 'e-condition_1-loop_1',
547
- source: 'condition_1',
548
- target: 'loop_1',
549
- label: 'true',
550
- },
551
- {
552
- id: 'e-condition_1-action_5',
553
- source: 'condition_1',
554
- target: 'action_5',
555
- label: 'false',
556
- },
557
- {
558
- id: 'e-loop_1-action_2',
559
- kind: 'CALL',
560
- pairId: 'pair-loop_1-action_2',
561
- source: 'loop_1',
562
- target: 'action_2',
563
- sourceHandle: 'start',
564
- targetHandle: 'data-in',
565
- },
566
- {
567
- id: 'e-action_2-action_3',
568
- kind: 'FLOW',
569
- pairId: 'pair-loop_1-action_2',
570
- source: 'action_2',
571
- target: 'action_3',
572
- },
573
- {
574
- id: 'e-action_3-loop_1',
575
- kind: 'RETURN',
576
- pairId: 'pair-loop_1-action_2',
577
- source: 'action_3',
578
- target: 'loop_1',
579
- sourceHandle: 'result-out',
580
- targetHandle: 'end',
581
- valueType: 'JSON',
582
- },
583
- ],
584
- },
585
- },
586
- {
587
- code: 'new_user_onboard',
588
- name: '新增用户自动化流程',
589
- description:
590
- '当 t_admin_user_role 新增用户时,自动分配默认角色、同步到外部系统并记录审计日志',
591
- status: 'PUBLISHED' as const,
592
- isEnabled: true,
593
- creator: 'admin',
594
- dsl: {
595
- nodes: [
596
- {
597
- id: 'trigger_1',
598
- type: 'event_trigger',
599
- label: '监听用户创建事件',
600
- position: { x: 100, y: 200 },
601
- config: {
602
- registrationCode: 'admin-user-role-insert',
603
- processingMode: 'PARALLEL',
604
- },
605
- },
606
- {
607
- id: 'action_1',
608
- type: 'db_execute',
609
- label: '查询新用户信息',
610
- position: { x: 350, y: 200 },
611
- config: {
612
- allowWrite: false,
613
- sql: 'SELECT userid, username, roles, is_active FROM t_admin_user_role WHERE userid = $1',
614
- parameters: [{ source: 'INPUT', path: 'userid' }],
615
- timeoutSeconds: 30,
616
- },
617
- },
618
- {
619
- id: 'action_2',
620
- type: 'code',
621
- label: '初始化默认角色',
622
- position: { x: 600, y: 200 },
623
- config: {
624
- script:
625
- 'const user = input.rows[0]; if (!user.roles || user.roles === "[]") { return { ...user, roles: JSON.stringify(["GUEST"]), needsUpdate: true }; } return { ...user, needsUpdate: false };',
626
- },
627
- },
628
- {
629
- id: 'condition_1',
630
- type: 'if_else',
631
- label: '需要分配默认角色?',
632
- position: { x: 850, y: 200 },
633
- config: {
634
- conditions: [
635
- {
636
- id: 'needs-default-role',
637
- left: { source: 'INPUT', path: 'needsUpdate' },
638
- operator: 'EQ',
639
- right: {
640
- source: 'FIXED',
641
- valueType: 'BOOLEAN',
642
- value: 'true',
643
- },
644
- },
645
- ],
646
- combineOperation: 'AND',
647
- },
648
- },
649
- {
650
- id: 'action_3',
651
- type: 'db_execute',
652
- label: '写入默认角色',
653
- position: { x: 1100, y: 100 },
654
- config: {
655
- operation: 'update',
656
- table: 't_admin_user_role',
657
- values: [
658
- {
659
- column: 'roles',
660
- binding: {
661
- source: 'FIXED',
662
- valueType: 'STRING',
663
- value: '["GUEST"]',
664
- },
665
- },
666
- ],
667
- where: 'userid = $2',
668
- parameters: [{ source: 'INPUT', path: 'userid' }],
669
- timeoutSeconds: 30,
670
- },
671
- },
672
- {
673
- id: 'action_4',
674
- type: 'http_request',
675
- label: '同步到企业通讯录',
676
- position: { x: 1100, y: 300 },
677
- config: {
678
- url: 'https://api.wecom.example.com/user/sync',
679
- method: 'POST',
680
- },
681
- },
682
- {
683
- id: 'action_6',
684
- type: 'db_execute',
685
- label: '记录审计日志',
686
- position: { x: 1600, y: 200 },
687
- config: {
688
- operation: 'insert',
689
- table: 't_admin_audit_log',
690
- values: [
691
- {
692
- column: 'action',
693
- binding: {
694
- source: 'FIXED',
695
- valueType: 'STRING',
696
- value: 'user.onboarded',
697
- },
698
- },
699
- {
700
- column: 'resource',
701
- binding: {
702
- source: 'FIXED',
703
- valueType: 'STRING',
704
- value: 'user',
705
- },
706
- },
707
- ],
708
- parameters: [],
709
- timeoutSeconds: 30,
710
- },
711
- },
712
- ],
713
- edges: [
714
- { id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
715
- { id: 'e-action_1-action_2', source: 'action_1', target: 'action_2' },
716
- {
717
- id: 'e-action_2-condition_1',
718
- source: 'action_2',
719
- target: 'condition_1',
720
- },
721
- {
722
- id: 'e-condition_1-action_3',
723
- source: 'condition_1',
724
- target: 'action_3',
725
- label: 'true',
726
- },
727
- {
728
- id: 'e-condition_1-action_4',
729
- source: 'condition_1',
730
- target: 'action_4',
731
- label: 'false',
732
- },
733
- { id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
734
- { id: 'e-action_4-action_6', source: 'action_4', target: 'action_6' },
735
- ],
736
- },
737
- },
738
- ];
739
-
740
- export async function seedWorkflows(prisma: PrismaClient) {
741
- console.log('Seeding workflows...');
742
-
743
- for (const wf of WORKFLOWS) {
744
- // Upsert workflow by code
745
- const workflow = await prisma.workflow.upsert({
746
- where: { code: wf.code },
747
- update: {
748
- name: wf.name,
749
- description: wf.description,
750
- status: wf.status,
751
- isEnabled: wf.isEnabled,
752
- dsl: wf.dsl,
753
- },
754
- create: {
755
- code: wf.code,
756
- name: wf.name,
757
- description: wf.description,
758
- status: wf.status,
759
- isEnabled: wf.isEnabled,
760
- dsl: wf.dsl,
761
- creator: wf.creator,
762
- },
763
- });
764
-
765
- // Create version 1
766
- await prisma.workflowVersion.upsert({
767
- where: { workflowId_version: { workflowId: workflow.id, version: 1 } },
768
- update: {},
769
- create: {
770
- workflowId: workflow.id,
771
- version: 1,
772
- dsl: wf.dsl,
773
- prompt: `创建工作流: ${wf.name}`,
774
- changeSummary: '初始版本',
775
- creator: wf.creator,
776
- },
777
- });
778
-
779
- // For published workflows, create some mock instances
780
- if (wf.status === 'PUBLISHED') {
781
- const version = await prisma.workflowVersion.findFirst({
782
- where: { workflowId: workflow.id, version: 1 },
783
- });
784
-
785
- if (!version) continue;
786
-
787
- // Create 3 instances per published workflow with different statuses
788
- const instanceConfigs = [
789
- { status: 'COMPLETED' as const, daysAgo: 3 },
790
- { status: 'COMPLETED' as const, daysAgo: 1 },
791
- { status: 'RUNNING' as const, daysAgo: 0 },
792
- ];
793
-
794
- for (const cfg of instanceConfigs) {
795
- const startedAt = new Date(
796
- Date.now() - cfg.daysAgo * 24 * 60 * 60 * 1000,
797
- );
798
- const finishedAt =
799
- cfg.status === 'COMPLETED'
800
- ? new Date(startedAt.getTime() + 30000) // 30s later
801
- : null;
802
-
803
- const instance = await prisma.workflowInstance.create({
804
- data: {
805
- workflowId: workflow.id,
806
- versionId: version.id,
807
- status: cfg.status,
808
- context: { trigger: 'auto', env: 'production' },
809
- result:
810
- cfg.status === 'COMPLETED'
811
- ? { success: true, processedCount: 128 }
812
- : Prisma.JsonNull,
813
- startedAt,
814
- finishedAt,
815
- creator: wf.creator,
816
- },
817
- });
818
-
819
- // Create node executions for each instance
820
- const nodes = (wf.dsl as any).nodes || [];
821
- for (let i = 0; i < nodes.length; i++) {
822
- const node = nodes[i];
823
- const nodeStarted = new Date(startedAt.getTime() + i * 5000);
824
- const isLastNode = i === nodes.length - 1;
825
-
826
- let nodeStatus: 'COMPLETED' | 'RUNNING' | 'PENDING' = 'COMPLETED';
827
- if (cfg.status === 'RUNNING') {
828
- if (i === nodes.length - 1) nodeStatus = 'RUNNING';
829
- if (i > nodes.length - 1) nodeStatus = 'PENDING';
830
- }
831
-
832
- await prisma.workflowNodeExecution.upsert({
833
- where: {
834
- instanceId_nodeId: { instanceId: instance.id, nodeId: node.id },
835
- },
836
- update: {},
837
- create: {
838
- instanceId: instance.id,
839
- nodeId: node.id,
840
- nodeType: node.type,
841
- nodeLabel: node.label,
842
- status: nodeStatus,
843
- input: { data: `mock_input_${i}` },
844
- output:
845
- nodeStatus === 'COMPLETED'
846
- ? { data: `mock_output_${i}` }
847
- : Prisma.JsonNull,
848
- startedAt: nodeStarted,
849
- finishedAt:
850
- nodeStatus === 'COMPLETED'
851
- ? new Date(nodeStarted.getTime() + 3000)
852
- : null,
853
- },
854
- });
855
- }
856
- }
857
- }
858
- }
859
-
860
- console.log(
861
- `Seeded ${WORKFLOWS.length} workflows with versions, instances, and node executions.`,
862
- );
863
- }