@gadmin2n/schematics 0.0.87 → 0.0.89

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 (99) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/.dockerignore +16 -2
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.codegen +40 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.server +76 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.web +53 -0
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +219 -33
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/compose-ctl.sh +250 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +4 -1
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/dev/postgres/init.sql +12 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.md +170 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.yml +254 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +8 -7
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/lib/page-helpers.ts +1 -1
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/prismaModels.ts +1 -1
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/agenda.seed.ts +39 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/audit.seed.ts +40 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/bootstrap.ts +56 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/canvas.seed.ts +39 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/{scripts/sync-data-mngt-pages.ts → seed/data-mngt.seed.ts} +36 -20
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/game.seed.ts +44 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +30 -6
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +130 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-event-trigger.ts +60 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +11 -25
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +108 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +1 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.spec.ts +31 -2
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.spec.ts +31 -2
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.spec.ts +41 -57
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.spec.ts +31 -2
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.spec.ts +309 -1
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.spec.ts +31 -2
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.spec.ts +315 -1
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.spec.ts +31 -2
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.spec.ts +312 -2
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.spec.ts +31 -2
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.spec.ts +317 -1
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.spec.ts +31 -2
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.spec.ts +309 -1
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.spec.ts +31 -2
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.spec.ts +299 -1
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.spec.ts +31 -2
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.spec.ts +307 -1
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.spec.ts +31 -2
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +309 -1
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.spec.ts +205 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.ts +116 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +158 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +110 -1
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.spec.ts +79 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.ts +54 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +34 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +457 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +241 -4
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.spec.ts +34 -2
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.spec.ts +24 -30
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.spec.ts +34 -2
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +36 -36
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.spec.ts +34 -2
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +48 -24
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +312 -3
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/TODO.md +152 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.dockerignore +12 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/Dockerfile +79 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/GRACEFUL-DEPLOYMENT.md +270 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +1 -1
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +23 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +70 -5
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +246 -90
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/cron-trigger-workflow.test.ts +20 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +96 -8
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/nginx.conf +74 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/ElementInspector.tsx +18 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/promptGenerator.ts +1 -1
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/form.tsx +1 -1
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +3 -3
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +3 -3
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +4 -1
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasEditPage.tsx +9 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +156 -139
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +14 -2
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +62 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/PublishModal.tsx +4 -6
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasApi.ts +18 -27
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +32 -11
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/demos.ts +48 -61
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas-page/index.tsx +3 -6
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/DslView.tsx +16 -16
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +28 -35
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +34 -3
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +1 -1
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -1
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +6 -0
  93. package/package.json +1 -1
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile +0 -63
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/sync-resources.ts +0 -100
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +0 -302
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.spec.ts +0 -20
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/sql/create-event-trigger.sql +0 -87
  99. /package/dist/lib/application/files/gadmin2-game-angle-demo/{GRACEFUL-DEPLOYMENT.md → server/GRACEFUL-DEPLOYMENT.md} +0 -0
@@ -1,63 +0,0 @@
1
- # syntax=docker/dockerfile:1
2
-
3
- # ─── 阶段 1:构建 ────────────────────────────────────────────
4
- FROM node:20-alpine AS builder
5
-
6
- WORKDIR /app
7
-
8
- # 禁止 husky 在无 .git 环境下报错
9
- ENV HUSKY=0
10
-
11
- # 安装 gadmin2 CLI(建议锁定版本号以保证构建可复现)
12
- RUN yarn global add @gadmin2n/cli@0.0.80
13
-
14
- # 先复制依赖描述文件,源码未变时此层可复用缓存
15
- COPY server/package.json server/yarn.lock ./server/
16
-
17
- # 安装全量依赖(含 devDependencies,用于代码生成和 TypeScript 编译)
18
- RUN --mount=type=cache,target=/root/.yarn \
19
- cd server && yarn install --frozen-lockfile
20
-
21
- # 复制 Prisma schema 配置和所有源码
22
- COPY config/ ./config/
23
- COPY server/ ./server/
24
-
25
- # 代码生成 + 编译
26
- RUN gadmin2 g prisma:server && cd server && yarn build
27
-
28
- # ─── 阶段 2:运行 ────────────────────────────────────────────
29
- FROM node:20-alpine AS runner
30
-
31
- WORKDIR /app/server
32
-
33
- STOPSIGNAL SIGQUIT
34
-
35
- # 禁止 husky 在无 .git 环境下报错
36
- ENV HUSKY=0
37
-
38
- # 先复制依赖描述文件
39
- COPY server/package.json server/yarn.lock ./
40
-
41
- # 只安装 production 依赖,大幅减少镜像体积
42
- RUN --mount=type=cache,target=/root/.yarn \
43
- yarn install --production --frozen-lockfile
44
-
45
- # 从构建阶段复制 Prisma 生成的 Client(含平台原生二进制)
46
- COPY --from=builder /app/server/node_modules/.prisma ./node_modules/.prisma
47
-
48
- # 从构建阶段复制编译产物
49
- COPY --from=builder /app/server/dist ./dist
50
-
51
- # 复制 Prisma schema(feature 分支部署时 prisma db push 需要)
52
- COPY --from=builder /app/server/prisma ./prisma
53
-
54
- # 复制启动脚本和数据库迁移脚本
55
- COPY server/start-prod.sh server/migrate-between-pg-schemas.js ./
56
- RUN chmod +x start-prod.sh
57
-
58
- # 复制环境变量配置(作为默认值,K8s 注入的同名变量优先级更高)
59
- COPY server/.env ./.env
60
-
61
- EXPOSE 8000
62
-
63
- CMD ["yarn", "start:prod"]
@@ -1,100 +0,0 @@
1
- /**
2
- * sync-resources.ts
3
- *
4
- * Syncs all non-system Prisma business models as Resource records into the
5
- * `t_resource` table.
6
- * - Reads business models from config/prisma/ via readPrismaModels() (excludes
7
- * system.prisma and models annotated with @IgnoreReactCode).
8
- * - Uses `upsert` (where: { code }) so it is idempotent and safe to re-run.
9
- * - Only inserts when the resource does not yet exist; existing records are left
10
- * untouched (update: {}) to preserve any manual edits.
11
- *
12
- * Invoked automatically by `postgenerate:dev` in server/package.json after
13
- * `generate:dev` (migrate + prisma generate) completes.
14
- */
15
-
16
- import { PrismaClient } from '@prisma/client';
17
- import { readPrismaModels } from './prismaModels';
18
-
19
- const prisma = new PrismaClient();
20
-
21
- async function main() {
22
- const businessModels = readPrismaModels();
23
-
24
- console.log(
25
- `[sync-resources] Found ${
26
- businessModels.length
27
- } business model(s) to sync: [${businessModels
28
- .map((m) => m.name)
29
- .join(', ')}]`,
30
- );
31
-
32
- let inserted = 0;
33
- let skipped = 0;
34
-
35
- for (const model of businessModels) {
36
- const code = model.code;
37
- const modelName = model.name;
38
-
39
- const result = await prisma.resource.upsert({
40
- where: { code },
41
- // Already exists → leave it as-is (preserve any manual name / description edits)
42
- update: {},
43
- create: {
44
- code,
45
- name: code,
46
- description: `${modelName} resource (auto-synced by sync-resources)`,
47
- isCommonResource: false,
48
- },
49
- });
50
-
51
- // Detect whether the record was just created or already existed.
52
- // upsert does not expose a "created" flag, so we compare timestamps.
53
- const wasCreated =
54
- Math.abs(result.createdAt.getTime() - result.updatedAt.getTime()) < 1000;
55
-
56
- if (wasCreated) {
57
- inserted++;
58
- console.log(
59
- `[sync-resources] ✚ Created Resource: ${code} (id=${result.id})`,
60
- );
61
-
62
- // Write audit log for newly created resources; failure must not abort the main flow.
63
- try {
64
- await prisma.audit.create({
65
- data: {
66
- creator: 'system',
67
- authorId: 'system',
68
- authorName: 'sync-resources',
69
- action: 'create',
70
- resource: code,
71
- resourceId: String(result.id),
72
- data: JSON.stringify({ code, name: code }),
73
- previousData: null,
74
- },
75
- });
76
- } catch (auditErr) {
77
- console.warn(
78
- `[sync-resources] ⚠ Failed to write audit log for Resource "${code}":`,
79
- auditErr,
80
- );
81
- }
82
- } else {
83
- skipped++;
84
- console.log(
85
- `[sync-resources] · Exists Resource: ${code} (id=${result.id}) — skipped`,
86
- );
87
- }
88
- }
89
-
90
- console.log(
91
- `[sync-resources] ✅ Done. inserted=${inserted}, already-existed=${skipped} (total=${businessModels.length})`,
92
- );
93
- }
94
-
95
- main()
96
- .catch((e) => {
97
- console.error('[sync-resources] Fatal error:', e);
98
- process.exit(1);
99
- })
100
- .finally(() => prisma.$disconnect());
@@ -1,302 +0,0 @@
1
- import { PrismaClient } from '@prisma/client';
2
- import {
3
- bindResourceToPage,
4
- FULL_ACTIONS,
5
- upsertPage,
6
- upsertResource,
7
- } from '../scripts/lib/page-helpers';
8
-
9
- /**
10
- * Seed permission-related tables so that the SYSTEM_ADMIN role is recognised
11
- * by nest-access-control on first startup.
12
- *
13
- * Covers the two prisma schema files that define the admin/permission modules:
14
- * - config/prisma/default-schema.prisma → user, audit resources
15
- * - config/prisma/page.prisma → role, page, resource resources
16
- *
17
- * Safe to re-run: every write uses upsert / createMany + skipDuplicates.
18
- *
19
- * Data model (single source of truth):
20
- * resourceDefs[].pageCode — which page "owns" this resource.
21
- * • undefined → pure system resource, no page binding (currently unused)
22
- * • string → creates a t_page_resource row linking the resource to that page
23
- * Multiple resources can share the same pageCode (e.g. rolePages + roleResource
24
- * both belong to the 'role' page because that page's UI calls both APIs).
25
- */
26
- export async function seedPermissions(prisma: PrismaClient) {
27
- // ------------------------------------------------------------------ Role --
28
- const systemAdminRole = await prisma.role.upsert({
29
- where: { name: 'SYSTEM_ADMIN' },
30
- update: {},
31
- create: {
32
- name: 'SYSTEM_ADMIN',
33
- description: 'System administrator with full access',
34
- },
35
- });
36
- console.log(`[seed] Role: SYSTEM_ADMIN (id=${systemAdminRole.id})`);
37
-
38
- // --------------------------------------------------------------- Resources -
39
- // pageCode: which leaf page "owns" this resource (drives t_page_resource rows).
40
- const resourceDefs: {
41
- code: string;
42
- description: string;
43
- pageCode?: string;
44
- }[] = [
45
- // ── permission module ────────────────────────────────────────────────────
46
- { code: 'user', description: 'User management', pageCode: 'user' },
47
- { code: 'role', description: 'Role management', pageCode: 'role' },
48
- {
49
- code: 'rolePages',
50
- description: 'Role-page assignments',
51
- pageCode: 'role',
52
- },
53
- {
54
- code: 'roleResource',
55
- description: 'Role-resource grants',
56
- pageCode: 'role',
57
- },
58
- { code: 'page', description: 'Page / menu management', pageCode: 'page' },
59
- {
60
- code: 'pageResource',
61
- description: 'Page-resource configuration',
62
- pageCode: 'page',
63
- },
64
- {
65
- code: 'resource',
66
- description: 'Resource management',
67
- pageCode: 'resource',
68
- },
69
- { code: 'audit', description: 'Audit logs', pageCode: 'audits' },
70
- // ── business module ──────────────────────────────────────────────────────
71
- {
72
- code: 'game',
73
- description: 'Business / Game management',
74
- pageCode: 'game',
75
- },
76
- { code: 'canvas', description: 'Canvas management', pageCode: 'canvas' },
77
- {
78
- code: 'agenda',
79
- description: 'Agenda job management',
80
- pageCode: 'agenda',
81
- },
82
- {
83
- code: 'workflow',
84
- description: 'Workflow management',
85
- pageCode: 'workflow_list',
86
- },
87
- {
88
- code: 'workflowNodeInstance',
89
- description: 'Workflow node instance management',
90
- pageCode: 'workflow_node_instance',
91
- },
92
- ];
93
-
94
- const resources: { id: number; code: string }[] = [];
95
- for (const def of resourceDefs) {
96
- const r = await upsertResource(prisma, def.code, def.description);
97
- resources.push({ id: r.id, code: r.code });
98
- console.log(`[seed] Resource: ${r.code} (id=${r.id})`);
99
- }
100
-
101
- // ------------------------------------------------------------------ Pages --
102
- // Created in order (parent first) so that parentId can be resolved.
103
-
104
- const pages: { id: number; code: string }[] = [];
105
-
106
- // Level 1: top-level group (系统管理)
107
- const adminPage = await upsertPage(prisma, {
108
- code: 'admin',
109
- name: 'admin',
110
- zhName: '系统管理',
111
- enName: 'System Mgnt',
112
- sortOrder: 1000,
113
- });
114
- pages.push({ id: adminPage.id, code: adminPage.code });
115
- console.log(`[seed] Page: ${adminPage.code} (id=${adminPage.id})`);
116
-
117
- // Level 2: 数据管理 — child of admin
118
- const dataMngt = await upsertPage(prisma, {
119
- code: 'dataMngt',
120
- name: '数据管理',
121
- zhName: '数据管理',
122
- enName: 'Data Management',
123
- sortOrder: 1000,
124
- parentId: adminPage.id,
125
- });
126
- pages.push({ id: dataMngt.id, code: dataMngt.code });
127
- console.log(`[seed] Page: ${dataMngt.code} (id=${dataMngt.id})`);
128
-
129
- // Level 2: permission group (权限管理) — child of admin
130
- const permissionPage = await upsertPage(prisma, {
131
- code: 'permission',
132
- name: 'permission',
133
- zhName: '权限管理',
134
- enName: 'Permission Mgnt',
135
- sortOrder: 1040,
136
- parentId: adminPage.id,
137
- });
138
- pages.push({ id: permissionPage.id, code: permissionPage.code });
139
- console.log(`[seed] Page: ${permissionPage.code} (id=${permissionPage.id})`);
140
-
141
- // Level 3: leaf pages under permission group
142
- const permissionChildDefs = [
143
- {
144
- code: 'permission_readme',
145
- name: 'permission_readme',
146
- zhName: '菜单指引',
147
- enName: 'Menu Guide',
148
- sortOrder: 1005,
149
- },
150
- {
151
- code: 'user',
152
- name: 'user',
153
- zhName: '用户管理',
154
- enName: 'User Mgnt',
155
- sortOrder: 1010,
156
- },
157
- {
158
- code: 'role',
159
- name: 'role',
160
- zhName: '角色管理',
161
- enName: 'Role Mgnt',
162
- sortOrder: 1020,
163
- },
164
- {
165
- code: 'page',
166
- name: 'page',
167
- zhName: '页面管理',
168
- enName: 'Page Mgnt',
169
- sortOrder: 1030,
170
- },
171
- {
172
- code: 'resource',
173
- name: 'resource',
174
- zhName: '资源管理',
175
- enName: 'Resource Management',
176
- sortOrder: 1040,
177
- },
178
- ];
179
-
180
- for (const def of permissionChildDefs) {
181
- const p = await upsertPage(prisma, { ...def, parentId: permissionPage.id });
182
- pages.push({ id: p.id, code: p.code });
183
- console.log(`[seed] Page: ${p.code} (id=${p.id})`);
184
- }
185
-
186
- // Level 2: other top-level children of admin (业务、审计日志)
187
- const adminChildDefs = [
188
- {
189
- code: 'canvas',
190
- name: 'canvas',
191
- zhName: 'Canvas',
192
- enName: 'Canvas',
193
- sortOrder: 1010,
194
- },
195
- {
196
- code: 'agenda',
197
- name: 'agenda',
198
- zhName: 'Cron Jobs',
199
- enName: 'Cron Jobs',
200
- sortOrder: 1020,
201
- },
202
- {
203
- code: 'game',
204
- name: 'game',
205
- zhName: '业务管理',
206
- enName: 'Business Mgnt',
207
- sortOrder: 1050,
208
- },
209
- {
210
- code: 'audits',
211
- name: 'audits',
212
- zhName: '审计日志',
213
- enName: 'Audit Log',
214
- sortOrder: 1060,
215
- },
216
- ];
217
-
218
- for (const def of adminChildDefs) {
219
- const p = await upsertPage(prisma, { ...def, parentId: adminPage.id });
220
- pages.push({ id: p.id, code: p.code });
221
- console.log(`[seed] Page: ${p.code} (id=${p.id})`);
222
- }
223
-
224
- // Level 2: Workflow group — child of admin (parent menu with sub-pages)
225
- const workflowPage = await upsertPage(prisma, {
226
- code: 'workflow',
227
- name: 'workflow',
228
- zhName: 'Workflow',
229
- enName: 'Workflow',
230
- sortOrder: 1030,
231
- parentId: adminPage.id,
232
- });
233
- pages.push({ id: workflowPage.id, code: workflowPage.code });
234
- console.log(`[seed] Page: ${workflowPage.code} (id=${workflowPage.id})`);
235
-
236
- // Level 3: Workflow child pages
237
- const workflowChildDefs = [
238
- {
239
- code: 'workflow_list',
240
- name: 'workflow_list',
241
- zhName: '工作流列表',
242
- enName: 'List',
243
- sortOrder: 1,
244
- },
245
- {
246
- code: 'workflow_node_instance',
247
- name: 'workflow_node_instance',
248
- zhName: 'Node模版',
249
- enName: 'Node Instance',
250
- sortOrder: 2,
251
- },
252
- ];
253
-
254
- for (const def of workflowChildDefs) {
255
- const p = await upsertPage(prisma, { ...def, parentId: workflowPage.id });
256
- pages.push({ id: p.id, code: p.code });
257
- console.log(`[seed] Page: ${p.code} (id=${p.id})`);
258
- }
259
-
260
- // ------------------------------------------------------- PageResource links -
261
- for (const def of resourceDefs) {
262
- if (!def.pageCode) continue;
263
-
264
- const targetPage = pages.find((p) => p.code === def.pageCode);
265
- if (!targetPage) {
266
- console.warn(
267
- `[seed] PageResource: page "${def.pageCode}" not found — skipping ${def.code}`,
268
- );
269
- continue;
270
- }
271
-
272
- await bindResourceToPage(prisma, targetPage.id, def.code, def.description);
273
- console.log(
274
- `[seed] PageResource: page(${def.pageCode}) ↔ resource(${def.code})`,
275
- );
276
- }
277
-
278
- // --------------------------------------------------- RoleResource (grants) -
279
- await prisma.roleResource.createMany({
280
- data: resources.map((r) => ({
281
- roleId: systemAdminRole.id,
282
- resourceId: r.id,
283
- actions: FULL_ACTIONS,
284
- })),
285
- skipDuplicates: true,
286
- });
287
- console.log(
288
- `[seed] RoleResource: SYSTEM_ADMIN granted full access to ${resources.length} resources`,
289
- );
290
-
291
- // ------------------------------------------------------ RolePages (access) -
292
- await prisma.rolePages.createMany({
293
- data: pages.map((p) => ({
294
- roleId: systemAdminRole.id,
295
- pageId: p.id,
296
- })),
297
- skipDuplicates: true,
298
- });
299
- console.log(
300
- `[seed] RolePages: SYSTEM_ADMIN granted access to ${pages.length} pages`,
301
- );
302
- }
@@ -1,20 +0,0 @@
1
- import { Test, TestingModule } from '@nestjs/testing';
2
- import { CanvasController } from './canvas.controller';
3
- import { CanvasService } from './canvas.service';
4
-
5
- describe('CanvasController', () => {
6
- let controller: CanvasController;
7
-
8
- beforeEach(async () => {
9
- const module: TestingModule = await Test.createTestingModule({
10
- controllers: [CanvasController],
11
- providers: [CanvasService],
12
- }).compile();
13
-
14
- controller = module.get<CanvasController>(CanvasController);
15
- });
16
-
17
- it('should be defined', () => {
18
- expect(controller).toBeDefined();
19
- });
20
- });
@@ -1,87 +0,0 @@
1
- -- ============================================================================
2
- -- Workflow Event Trigger — Outbox Pattern (轮询方案)
3
- -- ============================================================================
4
- -- 当业务表发生 INSERT/UPDATE/DELETE 时,触发器将事件写入 outbox 表。
5
- -- Worker 的 outbox-poller 定时扫描未处理事件,匹配并触发对应的 workflow。
6
- --
7
- -- 优点:
8
- -- - 事务保证不丢事件(outbox INSERT 与业务操作在同一事务)
9
- -- - Worker 重启不会丢事件(积压事件恢复后立即处理)
10
- -- - 无需 PG LISTEN/NOTIFY,逻辑更简单
11
- --
12
- -- 使用方式:
13
- -- 1. 执行本文件创建 outbox 表和通用触发器函数
14
- -- 2. 对需要监听的表创建触发器(见下方示例)
15
- -- ============================================================================
16
-
17
- -- ─── Outbox 表 ───────────────────────────────────────────────────────────────
18
-
19
- CREATE TABLE IF NOT EXISTS t_workflow_event_outbox (
20
- id BIGSERIAL PRIMARY KEY,
21
- event_name VARCHAR(128) NOT NULL, -- e.g. "t_order.insert"
22
- payload JSONB NOT NULL, -- 行数据
23
- processed BOOLEAN DEFAULT FALSE,
24
- created_at TIMESTAMP DEFAULT NOW(),
25
- processed_at TIMESTAMP,
26
- retry_count INT NOT NULL DEFAULT 0, -- 处理失败重试次数; >= MAX_RETRIES 视为终态失败
27
- last_error TEXT -- 最近一次失败的错误信息(成功时不清空,排障用)
28
- );
29
-
30
- -- 只扫未处理行的局部索引
31
- CREATE INDEX IF NOT EXISTS idx_outbox_unprocessed
32
- ON t_workflow_event_outbox (created_at)
33
- WHERE processed = FALSE;
34
-
35
- -- ─── 通用触发器函数 ──────────────────────────────────────────────────────────
36
-
37
- CREATE OR REPLACE FUNCTION notify_workflow_event() RETURNS trigger AS $$
38
- DECLARE
39
- event_payload jsonb;
40
- event_name text;
41
- BEGIN
42
- -- 拼接事件名:表名.操作 (e.g. "t_order.insert")
43
- event_name := TG_TABLE_NAME || '.' || lower(TG_OP);
44
-
45
- -- 构建 payload
46
- IF (TG_OP = 'DELETE') THEN
47
- event_payload = row_to_json(OLD)::jsonb;
48
- ELSE
49
- event_payload = row_to_json(NEW)::jsonb;
50
- END IF;
51
-
52
- -- 写入 outbox(与业务操作同一事务,保证不丢)
53
- INSERT INTO t_workflow_event_outbox (event_name, payload)
54
- VALUES (event_name, event_payload);
55
-
56
- RETURN COALESCE(NEW, OLD);
57
- END;
58
- $$ LANGUAGE plpgsql;
59
-
60
- -- ============================================================================
61
- -- 示例
62
- -- ============================================================================
63
-
64
- -- 监听 t_order 表的 INSERT
65
- -- CREATE TRIGGER trg_order_workflow
66
- -- AFTER INSERT ON t_order
67
- -- FOR EACH ROW EXECUTE FUNCTION notify_workflow_event();
68
-
69
- -- 监听 t_order 表的所有变更
70
- -- CREATE TRIGGER trg_order_workflow
71
- -- AFTER INSERT OR UPDATE OR DELETE ON t_order
72
- -- FOR EACH ROW EXECUTE FUNCTION notify_workflow_event();
73
-
74
- -- 只在特定条件下写入 outbox(例如状态变为 APPROVED)
75
- -- CREATE OR REPLACE FUNCTION notify_order_approved() RETURNS trigger AS $$
76
- -- BEGIN
77
- -- IF NEW.status = 'APPROVED' AND (OLD.status IS NULL OR OLD.status != 'APPROVED') THEN
78
- -- INSERT INTO t_workflow_event_outbox (event_name, payload)
79
- -- VALUES ('t_order.status_approved', row_to_json(NEW)::jsonb);
80
- -- END IF;
81
- -- RETURN NEW;
82
- -- END;
83
- -- $$ LANGUAGE plpgsql;
84
- --
85
- -- CREATE TRIGGER trg_order_approved
86
- -- AFTER UPDATE ON t_order
87
- -- FOR EACH ROW EXECUTE FUNCTION notify_order_approved();