@gadmin2n/schematics 0.0.147 → 0.0.149
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.
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +2 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +67 -15
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +83 -53
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +5 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.spec.ts +24 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.ts +15 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +4 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +31 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.controller.spec.ts +2 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.spec.ts +28 -29
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.ts +16 -34
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.dto.ts +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.service.spec.ts +61 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.controller.spec.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.controller.ts +11 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.spec.ts +126 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.ts +136 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.controller.ts +24 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.dto.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.service.spec.ts +62 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.service.ts +94 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.types.ts +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staffDirectory.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user.provider.ts +6 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.spec.ts +20 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.ts +21 -19
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +11 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +97 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.ts +150 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.controller.ts +13 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.dto.ts +13 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.errors.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.spec.ts +125 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.ts +118 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-cancellation.spec.ts +20 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +6 -9
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-catalog.spec.ts +166 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-trigger.pg.spec.ts +82 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-runtime.facade.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +4 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +5 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +7 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +106 -37
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +59 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.spec.ts +33 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.ts +10 -23
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/StaffSelect.test.tsx +69 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/StaffSelect.tsx +145 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/index.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/staff-directory.api.ts +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +20 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +60 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +60 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +41 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/PeopleGroupModal.tsx +181 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/index.tsx +203 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/people-groups.api.ts +145 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/PersonModal.tsx +112 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/index.tsx +214 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/person-directory.api.ts +79 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/form-modal.tsx +7 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +7 -7
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.test.tsx +46 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.tsx +14 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.test.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.tsx +2 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.test.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.tsx +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeOutputSummary.test.tsx +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +97 -28
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +87 -119
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/WorkflowCodeEditor.test.tsx +22 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/WorkflowCodeEditor.tsx +8 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/connections.css +8 -46
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/connectionsApi.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.test.tsx +32 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +163 -106
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +8 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.test.tsx +26 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +34 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-config.css +13 -16
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-instance-show.css +185 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +224 -105
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CodeConfigEditor.test.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/DbExecuteConfigEditor.tsx +21 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.test.tsx +140 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.tsx +124 -62
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.test.tsx +160 -8
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.tsx +125 -55
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SimplifiedNodeEditors.test.tsx +78 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.test.tsx +33 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.tsx +28 -46
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/event-catalog.api.ts +4 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/human-approval-config.css +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/subject-provider.api.ts +0 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +24 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +14 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -0
- package/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CreateNodeInstanceDrawer.tsx +0 -162
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/UserGroupCreateModal.tsx +0 -72
|
@@ -149,6 +149,7 @@ model WorkflowInstance {
|
|
|
149
149
|
retryFromNodeId String? @db.VarChar(64) @map("retry_from_node_id") // 本实例从哪个失败节点继续执行
|
|
150
150
|
cancellationStatus WorkflowCancellationStatus @default(NONE) @map("cancellation_status")
|
|
151
151
|
cancellationRequestedAt DateTime? @map("cancellation_requested_at") @db.Timestamptz(3)
|
|
152
|
+
cancellationRequestedBy String? @map("cancellation_requested_by") @db.VarChar(128)
|
|
152
153
|
cancellationReason String? @map("cancellation_reason") @db.VarChar(512)
|
|
153
154
|
cancellationLastError String? @map("cancellation_last_error") @db.Text
|
|
154
155
|
cancellationBlockedAt DateTime? @map("cancellation_blocked_at") @db.Timestamptz(3)
|
|
@@ -377,7 +378,7 @@ model WorkflowBusinessEventRegistration {
|
|
|
377
378
|
registrationKey String @unique @map("registration_key") @db.Char(64)
|
|
378
379
|
schemaName String @map("schema_name") @db.VarChar(63)
|
|
379
380
|
tableName String @map("table_name") @db.VarChar(63)
|
|
380
|
-
|
|
381
|
+
operations WorkflowEventOperation[]
|
|
381
382
|
watchedFields Json @map("watched_fields")
|
|
382
383
|
primaryKeyFields Json @map("primary_key_fields")
|
|
383
384
|
isEnabled Boolean @default(true) @map("is_enabled")
|
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
*
|
|
13
13
|
* 职责(仅本模块自家):
|
|
14
14
|
* 1. permission 父分组 page (顶级 admin/administration → permission)
|
|
15
|
-
* 2.
|
|
16
|
-
*
|
|
15
|
+
* 2. 7 个 leaf page: permission_readme / person_directory / user /
|
|
16
|
+
* people_group / role / page / resource
|
|
17
|
+
* 3. permission 模块所需 resource 及页面绑定
|
|
17
18
|
* 4. PageResource 绑定(page ↔ resource,多对多)
|
|
18
19
|
* 5. SYSTEM_ADMIN 对上述全部 page + resource 的全量授权
|
|
19
20
|
*
|
|
@@ -54,14 +55,31 @@ export async function seedPermissionModule(
|
|
|
54
55
|
enName: 'Menu Guide',
|
|
55
56
|
sortOrder: 1005,
|
|
56
57
|
},
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
{
|
|
59
|
+
code: 'person_directory',
|
|
60
|
+
zhName: '人员目录',
|
|
61
|
+
enName: 'People Directory',
|
|
62
|
+
sortOrder: 1010,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
code: 'user',
|
|
66
|
+
zhName: '访问授权',
|
|
67
|
+
enName: 'Access Grants',
|
|
68
|
+
sortOrder: 1020,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
code: 'people_group',
|
|
72
|
+
zhName: '人员组',
|
|
73
|
+
enName: 'People Groups',
|
|
74
|
+
sortOrder: 1030,
|
|
75
|
+
},
|
|
76
|
+
{ code: 'role', zhName: '角色管理', enName: 'Role Mgnt', sortOrder: 1040 },
|
|
77
|
+
{ code: 'page', zhName: '页面管理', enName: 'Page Mgnt', sortOrder: 1050 },
|
|
60
78
|
{
|
|
61
79
|
code: 'resource',
|
|
62
80
|
zhName: '资源管理',
|
|
63
81
|
enName: 'Resource Management',
|
|
64
|
-
sortOrder:
|
|
82
|
+
sortOrder: 1060,
|
|
65
83
|
},
|
|
66
84
|
];
|
|
67
85
|
|
|
@@ -69,14 +87,23 @@ export async function seedPermissionModule(
|
|
|
69
87
|
pagesByCode.set(permissionPage.code, permissionPage.id);
|
|
70
88
|
|
|
71
89
|
for (const def of leafDefs) {
|
|
72
|
-
const p = await upsertPage(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
const p = await upsertPage(
|
|
91
|
+
prisma,
|
|
92
|
+
{
|
|
93
|
+
code: def.code,
|
|
94
|
+
name: def.code,
|
|
95
|
+
zhName: def.zhName,
|
|
96
|
+
enName: def.enName,
|
|
97
|
+
sortOrder: def.sortOrder,
|
|
98
|
+
parentId: permissionPage.id,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
zhName: def.zhName,
|
|
102
|
+
enName: def.enName,
|
|
103
|
+
sortOrder: def.sortOrder,
|
|
104
|
+
parentId: permissionPage.id,
|
|
105
|
+
},
|
|
106
|
+
);
|
|
80
107
|
pagesByCode.set(p.code, p.id);
|
|
81
108
|
console.log(`[permission] Page: ${p.code} (id=${p.id})`);
|
|
82
109
|
}
|
|
@@ -87,7 +114,32 @@ export async function seedPermissionModule(
|
|
|
87
114
|
description: string;
|
|
88
115
|
pageCode: string;
|
|
89
116
|
}[] = [
|
|
90
|
-
{
|
|
117
|
+
{
|
|
118
|
+
code: 'user',
|
|
119
|
+
description: 'People directory',
|
|
120
|
+
pageCode: 'person_directory',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
code: 'staffDirectory',
|
|
124
|
+
description: 'Search the shared staff directory',
|
|
125
|
+
pageCode: 'person_directory',
|
|
126
|
+
},
|
|
127
|
+
{ code: 'user', description: 'Access grants', pageCode: 'user' },
|
|
128
|
+
{
|
|
129
|
+
code: 'userGroup:read',
|
|
130
|
+
description: 'Read reusable people groups',
|
|
131
|
+
pageCode: 'people_group',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
code: 'userGroup:create',
|
|
135
|
+
description: 'Maintain reusable people groups',
|
|
136
|
+
pageCode: 'people_group',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
code: 'user',
|
|
140
|
+
description: 'Read people for group membership',
|
|
141
|
+
pageCode: 'people_group',
|
|
142
|
+
},
|
|
91
143
|
{ code: 'role', description: 'Role management', pageCode: 'role' },
|
|
92
144
|
{
|
|
93
145
|
code: 'rolePages',
|
package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts
CHANGED
|
@@ -202,65 +202,82 @@ const NODE_DEFINITIONS: BaseNodeTypeSeed[] = [
|
|
|
202
202
|
minLength: 1,
|
|
203
203
|
description: '前端登记的 PostgreSQL 表字段监听标识',
|
|
204
204
|
},
|
|
205
|
-
|
|
206
|
-
type: '
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
205
|
+
operationRules: {
|
|
206
|
+
type: 'array',
|
|
207
|
+
minItems: 1,
|
|
208
|
+
maxItems: 3,
|
|
209
|
+
description: '按数据操作配置启动规则;未启用的操作不启动',
|
|
210
|
+
items: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
operation: {
|
|
214
|
+
type: 'string',
|
|
215
|
+
enum: ['INSERT', 'UPDATE', 'DELETE'],
|
|
216
|
+
},
|
|
217
|
+
match: {
|
|
216
218
|
type: 'object',
|
|
217
219
|
properties: {
|
|
218
|
-
|
|
219
|
-
left: {
|
|
220
|
-
type: 'object',
|
|
221
|
-
properties: {
|
|
222
|
-
source: { const: 'WORKFLOW' },
|
|
223
|
-
path: { type: 'string', minLength: 1 },
|
|
224
|
-
},
|
|
225
|
-
required: ['source', 'path'],
|
|
226
|
-
additionalProperties: false,
|
|
227
|
-
},
|
|
228
|
-
operator: {
|
|
220
|
+
combineOperation: {
|
|
229
221
|
type: 'string',
|
|
230
|
-
enum: ['
|
|
231
|
-
},
|
|
232
|
-
right: {
|
|
233
|
-
type: 'object',
|
|
234
|
-
properties: {
|
|
235
|
-
source: { const: 'FIXED' },
|
|
236
|
-
valueType: {
|
|
237
|
-
type: 'string',
|
|
238
|
-
enum: ['STRING', 'NUMBER', 'BOOLEAN'],
|
|
239
|
-
},
|
|
240
|
-
value: { type: 'string' },
|
|
241
|
-
},
|
|
242
|
-
required: ['source', 'valueType', 'value'],
|
|
243
|
-
additionalProperties: false,
|
|
222
|
+
enum: ['AND', 'OR'],
|
|
244
223
|
},
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
224
|
+
conditions: {
|
|
225
|
+
type: 'array',
|
|
226
|
+
minItems: 1,
|
|
227
|
+
items: {
|
|
228
|
+
type: 'object',
|
|
250
229
|
properties: {
|
|
251
|
-
|
|
230
|
+
id: { type: 'string', minLength: 1 },
|
|
231
|
+
left: {
|
|
232
|
+
type: 'object',
|
|
233
|
+
properties: {
|
|
234
|
+
source: { const: 'WORKFLOW' },
|
|
235
|
+
path: { type: 'string', minLength: 1 },
|
|
236
|
+
},
|
|
237
|
+
required: ['source', 'path'],
|
|
238
|
+
additionalProperties: false,
|
|
239
|
+
},
|
|
240
|
+
operator: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
enum: ['EQ', 'NEQ', 'IN', 'EMPTY', 'NOT_EMPTY'],
|
|
243
|
+
},
|
|
244
|
+
right: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
properties: {
|
|
247
|
+
source: { const: 'FIXED' },
|
|
248
|
+
valueType: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
enum: ['STRING', 'NUMBER', 'BOOLEAN'],
|
|
251
|
+
},
|
|
252
|
+
value: { type: 'string' },
|
|
253
|
+
},
|
|
254
|
+
required: ['source', 'valueType', 'value'],
|
|
255
|
+
additionalProperties: false,
|
|
256
|
+
},
|
|
252
257
|
},
|
|
253
|
-
required: ['operator'],
|
|
258
|
+
required: ['id', 'left', 'operator'],
|
|
259
|
+
allOf: [
|
|
260
|
+
{
|
|
261
|
+
if: {
|
|
262
|
+
properties: {
|
|
263
|
+
operator: { enum: ['EQ', 'NEQ', 'IN'] },
|
|
264
|
+
},
|
|
265
|
+
required: ['operator'],
|
|
266
|
+
},
|
|
267
|
+
then: { required: ['right'] },
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
additionalProperties: false,
|
|
254
271
|
},
|
|
255
|
-
then: { required: ['right'] },
|
|
256
272
|
},
|
|
257
|
-
|
|
273
|
+
},
|
|
274
|
+
required: ['combineOperation', 'conditions'],
|
|
258
275
|
additionalProperties: false,
|
|
259
276
|
},
|
|
260
277
|
},
|
|
278
|
+
required: ['operation'],
|
|
279
|
+
additionalProperties: false,
|
|
261
280
|
},
|
|
262
|
-
required: ['combineOperation', 'conditions'],
|
|
263
|
-
additionalProperties: false,
|
|
264
281
|
},
|
|
265
282
|
processingMode: {
|
|
266
283
|
type: 'string',
|
|
@@ -281,7 +298,7 @@ const NODE_DEFINITIONS: BaseNodeTypeSeed[] = [
|
|
|
281
298
|
description: '顺序调度器最多同时处理的业务对象数量',
|
|
282
299
|
},
|
|
283
300
|
},
|
|
284
|
-
required: ['registrationCode', 'processingMode'],
|
|
301
|
+
required: ['registrationCode', 'operationRules', 'processingMode'],
|
|
285
302
|
allOf: [
|
|
286
303
|
{
|
|
287
304
|
if: {
|
|
@@ -980,7 +997,11 @@ const HUMAN_APPROVAL_CONFIG_SCHEMA: NodeTypeSeed['configSchema'] = {
|
|
|
980
997
|
type: 'object',
|
|
981
998
|
properties: {
|
|
982
999
|
source: { type: 'string', enum: ['STATIC', 'DYNAMIC'] },
|
|
983
|
-
provider: {
|
|
1000
|
+
provider: {
|
|
1001
|
+
type: 'string',
|
|
1002
|
+
enum: ['USER', 'USER_GROUP'],
|
|
1003
|
+
default: 'USER',
|
|
1004
|
+
},
|
|
984
1005
|
refs: {
|
|
985
1006
|
type: 'array',
|
|
986
1007
|
minItems: 1,
|
|
@@ -998,7 +1019,7 @@ const HUMAN_APPROVAL_CONFIG_SCHEMA: NodeTypeSeed['configSchema'] = {
|
|
|
998
1019
|
additionalProperties: false,
|
|
999
1020
|
},
|
|
1000
1021
|
},
|
|
1001
|
-
required: ['source'
|
|
1022
|
+
required: ['source'],
|
|
1002
1023
|
allOf: [
|
|
1003
1024
|
{
|
|
1004
1025
|
if: { properties: { source: { const: 'STATIC' } } },
|
|
@@ -1093,7 +1114,7 @@ const humanApprovalNode: NodeTypeSeed = {
|
|
|
1093
1114
|
configSchema: HUMAN_APPROVAL_CONFIG_SCHEMA,
|
|
1094
1115
|
outputs: ['true', 'false'],
|
|
1095
1116
|
runtimeKey: 'human_approval',
|
|
1096
|
-
schemaVersion:
|
|
1117
|
+
schemaVersion: 22,
|
|
1097
1118
|
instancePolicy: 'OPTIONAL',
|
|
1098
1119
|
uiSchema: HUMAN_APPROVAL_UI_SCHEMA,
|
|
1099
1120
|
};
|
|
@@ -1323,7 +1344,7 @@ const dedicatedEditorNodes: NodeTypeSeed[] = [
|
|
|
1323
1344
|
),
|
|
1324
1345
|
schemaVersion:
|
|
1325
1346
|
type === 'event_trigger'
|
|
1326
|
-
?
|
|
1347
|
+
? 5
|
|
1327
1348
|
: type === 'db_execute'
|
|
1328
1349
|
? 4
|
|
1329
1350
|
: type === 'parallel'
|
|
@@ -1361,7 +1382,16 @@ export const NODE_TYPES: NodeTypeSeed[] = [
|
|
|
1361
1382
|
|
|
1362
1383
|
export async function seedNodeTypes(prisma: PrismaClient) {
|
|
1363
1384
|
console.log('Seeding workflow node types...');
|
|
1364
|
-
for (const removedType of [
|
|
1385
|
+
for (const removedType of [
|
|
1386
|
+
'return_result',
|
|
1387
|
+
'db_query',
|
|
1388
|
+
'approval_request_trigger',
|
|
1389
|
+
'send_notification',
|
|
1390
|
+
'set_variable',
|
|
1391
|
+
'submit_approval_result',
|
|
1392
|
+
'submit_external_result',
|
|
1393
|
+
'error_handler',
|
|
1394
|
+
]) {
|
|
1365
1395
|
const removedNodeType = await prisma.workflowNodeType.findUnique({
|
|
1366
1396
|
where: { type: removedType },
|
|
1367
1397
|
select: { id: true },
|
|
@@ -60,6 +60,11 @@ const config: Record<string, any> = {
|
|
|
60
60
|
integration: {
|
|
61
61
|
secretKey: process.env.INTEGRATION_SECRET_KEY ?? '',
|
|
62
62
|
},
|
|
63
|
+
staffDirectory: {
|
|
64
|
+
baseUrl: process.env.STAFF_DIRECTORY_BASE_URL ?? '',
|
|
65
|
+
apiKey: process.env.STAFF_DIRECTORY_API_KEY ?? '',
|
|
66
|
+
searchPath: process.env.STAFF_DIRECTORY_SEARCH_PATH ?? '/api/staff/search',
|
|
67
|
+
},
|
|
63
68
|
};
|
|
64
69
|
|
|
65
70
|
export default () => config;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ArgumentsHost, BadRequestException } from '@nestjs/common';
|
|
2
|
+
import { Prisma } from '@prisma/client';
|
|
2
3
|
import { StructuredAllExceptionsFilter } from './structured-all-exceptions.filter';
|
|
3
4
|
|
|
4
5
|
function httpHost(url = '/api/workflowNodeInstance') {
|
|
@@ -51,4 +52,27 @@ describe('StructuredAllExceptionsFilter', () => {
|
|
|
51
52
|
path: '/api/workflowNodeInstance',
|
|
52
53
|
});
|
|
53
54
|
});
|
|
55
|
+
|
|
56
|
+
it('returns a safe 409 response for an unhandled Prisma unique conflict', () => {
|
|
57
|
+
const filter = new StructuredAllExceptionsFilter();
|
|
58
|
+
const structured = httpHost('/api/user/createOne');
|
|
59
|
+
|
|
60
|
+
filter.catch(
|
|
61
|
+
new Prisma.PrismaClientKnownRequestError('sensitive database details', {
|
|
62
|
+
code: 'P2002',
|
|
63
|
+
clientVersion: '6.19.3',
|
|
64
|
+
meta: { target: ['userid'] },
|
|
65
|
+
}),
|
|
66
|
+
structured.host,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(structured.response.status).toHaveBeenCalledWith(409);
|
|
70
|
+
expect(structured.response.json).toHaveBeenCalledWith({
|
|
71
|
+
statusCode: 409,
|
|
72
|
+
code: 'RESOURCE_ALREADY_EXISTS',
|
|
73
|
+
message: '记录已存在,请勿重复提交',
|
|
74
|
+
error: 'Conflict',
|
|
75
|
+
path: '/api/user/createOne',
|
|
76
|
+
});
|
|
77
|
+
});
|
|
54
78
|
});
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
ExceptionFilter,
|
|
5
5
|
HttpStatus,
|
|
6
6
|
} from '@nestjs/common';
|
|
7
|
+
import { Prisma } from '@prisma/client';
|
|
7
8
|
|
|
8
9
|
interface ControlledException {
|
|
9
10
|
getStatus?: () => number;
|
|
@@ -24,6 +25,20 @@ export class StructuredAllExceptionsFilter implements ExceptionFilter {
|
|
|
24
25
|
const error = controlled?.name ?? 'Error';
|
|
25
26
|
const path = request.url ?? '';
|
|
26
27
|
|
|
28
|
+
if (
|
|
29
|
+
exception instanceof Prisma.PrismaClientKnownRequestError &&
|
|
30
|
+
exception.code === 'P2002'
|
|
31
|
+
) {
|
|
32
|
+
response.status(HttpStatus.CONFLICT).json({
|
|
33
|
+
statusCode: HttpStatus.CONFLICT,
|
|
34
|
+
code: 'RESOURCE_ALREADY_EXISTS',
|
|
35
|
+
message: '记录已存在,请勿重复提交',
|
|
36
|
+
error: 'Conflict',
|
|
37
|
+
path,
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
if (isRecord(payload)) {
|
|
28
43
|
response.status(statusCode).json({
|
|
29
44
|
...payload,
|
|
@@ -22,6 +22,8 @@ class JobsQueryDto {
|
|
|
22
22
|
toggle?: string;
|
|
23
23
|
module?: string;
|
|
24
24
|
q?: string;
|
|
25
|
+
dateFrom?: string;
|
|
26
|
+
dateTo?: string;
|
|
25
27
|
skip?: number;
|
|
26
28
|
limit?: number;
|
|
27
29
|
}
|
|
@@ -55,6 +57,8 @@ export class AgendaController {
|
|
|
55
57
|
toggle: query.toggle,
|
|
56
58
|
module: query.module,
|
|
57
59
|
q: query.q,
|
|
60
|
+
dateFrom: query.dateFrom,
|
|
61
|
+
dateTo: query.dateTo,
|
|
58
62
|
skip: query.skip ? Number(query.skip) : 0,
|
|
59
63
|
limit: query.limit ? Number(query.limit) : 20,
|
|
60
64
|
});
|
|
@@ -16,6 +16,8 @@ interface GetJobsParams {
|
|
|
16
16
|
toggle?: string; // 'active' | 'paused'
|
|
17
17
|
module?: string;
|
|
18
18
|
q?: string;
|
|
19
|
+
dateFrom?: string; // ISO:按 lastRunAt 过滤区间下界
|
|
20
|
+
dateTo?: string; // ISO:按 lastRunAt 过滤区间上界
|
|
19
21
|
skip?: number;
|
|
20
22
|
limit?: number;
|
|
21
23
|
}
|
|
@@ -364,7 +366,17 @@ export class AgendaService
|
|
|
364
366
|
totalPages: 0,
|
|
365
367
|
};
|
|
366
368
|
}
|
|
367
|
-
const {
|
|
369
|
+
const {
|
|
370
|
+
job,
|
|
371
|
+
state,
|
|
372
|
+
toggle,
|
|
373
|
+
module,
|
|
374
|
+
q,
|
|
375
|
+
dateFrom,
|
|
376
|
+
dateTo,
|
|
377
|
+
skip = 0,
|
|
378
|
+
limit = 20,
|
|
379
|
+
} = params;
|
|
368
380
|
|
|
369
381
|
const allQuery: JobsQueryOptions = {};
|
|
370
382
|
if (job) allQuery.name = job;
|
|
@@ -381,6 +393,21 @@ export class AgendaService
|
|
|
381
393
|
)
|
|
382
394
|
: allResult.jobs;
|
|
383
395
|
|
|
396
|
+
// 应用层过滤 lastRunAt 时间区间(Agenda queryJobs 同样不支持日期过滤)。
|
|
397
|
+
// 未执行过(lastRunAt 为空)的任务在设置了时间区间时不纳入。
|
|
398
|
+
const fromTs = dateFrom ? new Date(dateFrom).getTime() : null;
|
|
399
|
+
const toTs = dateTo ? new Date(dateTo).getTime() : null;
|
|
400
|
+
const dateFiltered =
|
|
401
|
+
fromTs !== null || toTs !== null
|
|
402
|
+
? moduleFiltered.filter((j) => {
|
|
403
|
+
if (!j.lastRunAt) return false;
|
|
404
|
+
const t = new Date(j.lastRunAt).getTime();
|
|
405
|
+
if (fromTs !== null && t < fromTs) return false;
|
|
406
|
+
if (toTs !== null && t > toTs) return false;
|
|
407
|
+
return true;
|
|
408
|
+
})
|
|
409
|
+
: moduleFiltered;
|
|
410
|
+
|
|
384
411
|
const overview: Record<string, number> = {
|
|
385
412
|
running: 0,
|
|
386
413
|
scheduled: 0,
|
|
@@ -392,15 +419,15 @@ export class AgendaService
|
|
|
392
419
|
active: 0,
|
|
393
420
|
};
|
|
394
421
|
|
|
395
|
-
for (const j of
|
|
422
|
+
for (const j of dateFiltered) {
|
|
396
423
|
const s = j.state;
|
|
397
424
|
if (s in overview) overview[s]++;
|
|
398
425
|
}
|
|
399
426
|
|
|
400
427
|
// 第一步:按 job 原始状态过滤(running/queued 等)
|
|
401
428
|
const stateFiltered = state
|
|
402
|
-
?
|
|
403
|
-
:
|
|
429
|
+
? dateFiltered.filter((j) => j.state === state)
|
|
430
|
+
: dateFiltered;
|
|
404
431
|
|
|
405
432
|
// 从状态过滤后的数据计算 active/paused 数量
|
|
406
433
|
for (const j of stateFiltered) {
|
|
@@ -28,12 +28,11 @@ describe('CodeRuntimeController', () => {
|
|
|
28
28
|
subjects: [
|
|
29
29
|
{
|
|
30
30
|
source: 'DYNAMIC',
|
|
31
|
-
provider: 'USER',
|
|
32
31
|
inputKey: 'direct_manager',
|
|
33
32
|
lookup: {
|
|
34
33
|
result: {
|
|
35
34
|
script:
|
|
36
|
-
|
|
35
|
+
'return [response.data.leader, input.user_id];',
|
|
37
36
|
},
|
|
38
37
|
},
|
|
39
38
|
},
|
|
@@ -89,10 +88,7 @@ describe('CodeRuntimeController', () => {
|
|
|
89
88
|
workflow: {},
|
|
90
89
|
}),
|
|
91
90
|
).resolves.toEqual({
|
|
92
|
-
result:
|
|
93
|
-
subjects: [{ provider: 'USER', refs: ['leader-a'] }],
|
|
94
|
-
requester: 'applicant-a',
|
|
95
|
-
},
|
|
91
|
+
result: ['leader-a', 'applicant-a'],
|
|
96
92
|
});
|
|
97
93
|
});
|
|
98
94
|
});
|
|
@@ -5,36 +5,22 @@ describe('materializeApprovalSubjects', () => {
|
|
|
5
5
|
expect(
|
|
6
6
|
materializeApprovalSubjects(
|
|
7
7
|
[
|
|
8
|
-
{ source: 'STATIC',
|
|
8
|
+
{ source: 'STATIC', refs: ['alice', 'bob'] },
|
|
9
9
|
{
|
|
10
10
|
source: 'DYNAMIC',
|
|
11
|
-
provider: 'USER',
|
|
12
11
|
inputKey: 'request_owner',
|
|
13
12
|
},
|
|
14
13
|
{
|
|
15
14
|
source: 'DYNAMIC',
|
|
16
|
-
|
|
17
|
-
inputKey: 'review_groups',
|
|
15
|
+
inputKey: 'backup_approvers',
|
|
18
16
|
},
|
|
19
17
|
],
|
|
20
18
|
{
|
|
21
|
-
request_owner:
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
review_groups: {
|
|
25
|
-
subjects: [
|
|
26
|
-
{
|
|
27
|
-
provider: 'USER_GROUP',
|
|
28
|
-
refs: ['finance', 'risk', 'finance'],
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
},
|
|
19
|
+
request_owner: ['bob', 'carol'],
|
|
20
|
+
backup_approvers: ['alice', 'dave', 'carol'],
|
|
32
21
|
},
|
|
33
22
|
),
|
|
34
|
-
).toEqual([
|
|
35
|
-
{ provider: 'USER', refs: ['alice', 'bob'] },
|
|
36
|
-
{ provider: 'USER_GROUP', refs: ['finance', 'risk'] },
|
|
37
|
-
]);
|
|
23
|
+
).toEqual([{ provider: 'USER', refs: ['alice', 'bob', 'carol', 'dave'] }]);
|
|
38
24
|
});
|
|
39
25
|
|
|
40
26
|
it('rejects a missing dynamic approver input', () => {
|
|
@@ -43,7 +29,6 @@ describe('materializeApprovalSubjects', () => {
|
|
|
43
29
|
[
|
|
44
30
|
{
|
|
45
31
|
source: 'DYNAMIC',
|
|
46
|
-
provider: 'USER',
|
|
47
32
|
inputKey: 'request_owner',
|
|
48
33
|
},
|
|
49
34
|
],
|
|
@@ -58,32 +43,46 @@ describe('materializeApprovalSubjects', () => {
|
|
|
58
43
|
[
|
|
59
44
|
{
|
|
60
45
|
source: 'DYNAMIC',
|
|
61
|
-
provider: 'USER',
|
|
62
46
|
inputKey: 'direct_manager',
|
|
63
47
|
},
|
|
64
48
|
],
|
|
65
|
-
{
|
|
66
|
-
direct_manager: {
|
|
67
|
-
subjects: [{ provider: 'USER', refs: ['leader-a'] }],
|
|
68
|
-
},
|
|
69
|
-
},
|
|
49
|
+
{ direct_manager: ['leader-a'] },
|
|
70
50
|
),
|
|
71
51
|
).toEqual([{ provider: 'USER', refs: ['leader-a'] }]);
|
|
72
52
|
});
|
|
73
53
|
|
|
74
|
-
it('
|
|
54
|
+
it('keeps fixed user groups for runtime expansion while deduplicating users', () => {
|
|
55
|
+
expect(
|
|
56
|
+
materializeApprovalSubjects(
|
|
57
|
+
[
|
|
58
|
+
{ source: 'STATIC', provider: 'USER', refs: ['alice'] },
|
|
59
|
+
{
|
|
60
|
+
source: 'STATIC',
|
|
61
|
+
provider: 'USER_GROUP',
|
|
62
|
+
refs: ['finance'],
|
|
63
|
+
},
|
|
64
|
+
{ source: 'DYNAMIC', inputKey: 'backup_approvers' },
|
|
65
|
+
],
|
|
66
|
+
{ backup_approvers: ['alice', 'bob', 'bob'] },
|
|
67
|
+
),
|
|
68
|
+
).toEqual([
|
|
69
|
+
{ provider: 'USER', refs: ['alice', 'bob'] },
|
|
70
|
+
{ provider: 'USER_GROUP', refs: ['finance'] },
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('rejects a lookup result that is not a user ID array', () => {
|
|
75
75
|
expect(() =>
|
|
76
76
|
materializeApprovalSubjects(
|
|
77
77
|
[
|
|
78
78
|
{
|
|
79
79
|
source: 'DYNAMIC',
|
|
80
|
-
provider: 'USER',
|
|
81
80
|
inputKey: 'direct_manager',
|
|
82
81
|
},
|
|
83
82
|
],
|
|
84
83
|
{
|
|
85
84
|
direct_manager: {
|
|
86
|
-
subjects: [{ provider: '
|
|
85
|
+
subjects: [{ provider: 'USER', refs: ['leader-a'] }],
|
|
87
86
|
},
|
|
88
87
|
},
|
|
89
88
|
),
|