@steedos-labs/plugin-workflow 3.0.21 → 3.0.23
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/APPROVAL_COMMENTS_OPERATIONS.md +673 -0
- package/APPROVAL_COMMENTS_UPGRADE.md +854 -0
- package/main/default/manager/handlers_manager.js +11 -12
- package/main/default/manager/uuflow_manager.js +4 -1
- package/main/default/manager/workflow_manager.js +12 -0
- package/main/default/pages/instance_tasks_detail.page.amis.json +16 -0
- package/main/default/pages/page_instance_print.page.amis.json +1 -1
- package/main/default/routes/api_files.router.js +15 -1
- package/main/default/routes/api_workflow_chart.router.js +41 -9
- package/main/default/routes/api_workflow_next_step.router.js +1 -1
- package/main/default/routes/api_workflow_next_step_users.router.js +7 -2
- package/main/default/routes/flow_form_design.ejs +21 -1
- package/main/default/triggers/amis_form_design.trigger.js +6 -4
- package/main/default/utils/designerManager.js +1 -2
- package/package.json +1 -1
- package/public/workflow/index.css +3 -0
- package/src/rests/approvalCommentsConsole.js +460 -0
- package/src/rests/index.js +5 -1
- package/src/rests/integrationTestApprovalComments.js +96 -0
- package/src/rests/migrateApprovalCommentsField.js +536 -0
- package/src/rests/rollbackApprovalCommentsField.js +319 -0
- package/src/util/templateConverter.js +8 -2
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
# Approval Comments Field Upgrade API
|
|
2
|
+
|
|
3
|
+
This module provides API endpoints to upgrade old approval comments field configurations in forms to the new steedos-field format, including support for both `current.fields`/`historys.fields` and `amis_schema` transformations.
|
|
4
|
+
|
|
5
|
+
## Legacy Spec Compliance
|
|
6
|
+
|
|
7
|
+
This migration tool fully complies with the **"老版本意见栏脚本逻辑规范 (Legacy Approval Script Specs)"** including:
|
|
8
|
+
|
|
9
|
+
✅ **Logic Inversion:** Correctly converts `only_cc: true` → `show_handler: false` and `only_handler: true` → `show_cc: false`
|
|
10
|
+
✅ **Signature Support:** Detects `signature.traces` keyword and `image_sign` parameter
|
|
11
|
+
✅ **Default Values:** When parameters are undefined, both `show_cc` and `show_handler` default to `true`
|
|
12
|
+
✅ **Parameter Mapping:** All legacy `yijianlan` parameters (`step`, `default`, `only_cc`, `only_handler`, `image_sign`) are correctly mapped
|
|
13
|
+
✅ **Edge Cases:** Empty step names are automatically skipped as invalid configurations
|
|
14
|
+
|
|
15
|
+
See the [Special Boolean Logic](#special-boolean-logic) section for detailed mapping.
|
|
16
|
+
|
|
17
|
+
## Endpoints
|
|
18
|
+
|
|
19
|
+
### 1. Migrate Endpoint
|
|
20
|
+
|
|
21
|
+
**GET** `/api/workflow/migrateApprovalCommentsField`
|
|
22
|
+
|
|
23
|
+
Transforms old approval comments field configurations to the new steedos-field format.
|
|
24
|
+
|
|
25
|
+
#### Parameters
|
|
26
|
+
|
|
27
|
+
- `batchSize` (number, optional): Number of bulk operations per batch. Default: 500
|
|
28
|
+
- `dryRun` (boolean, optional): When true, simulates the transformation without modifying data. Default: false
|
|
29
|
+
- `fid` (string, optional): Flow ID to process only a single flow's form. When provided, only the form associated with this flow will be processed.
|
|
30
|
+
|
|
31
|
+
#### Request Examples
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Process all enabled forms
|
|
35
|
+
curl -X GET "http://localhost:5000/api/workflow/migrateApprovalCommentsField?dryRun=true" \
|
|
36
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
37
|
+
|
|
38
|
+
# Process only a specific flow's form
|
|
39
|
+
curl -X GET "http://localhost:5000/api/workflow/migrateApprovalCommentsField?fid=flow_id_here" \
|
|
40
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### Response
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"totalForms": 10,
|
|
48
|
+
"totalFields": 25,
|
|
49
|
+
"errors": [],
|
|
50
|
+
"dryRun": true,
|
|
51
|
+
"message": "Dry run completed - no changes made"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### What It Does
|
|
56
|
+
|
|
57
|
+
The migration endpoint performs three types of transformations on **enabled forms only** (`state: 'enabled'`):
|
|
58
|
+
|
|
59
|
+
##### 1. Transform `current.fields` and `historys.fields`
|
|
60
|
+
|
|
61
|
+
Scans field arrays and matches fields with approval comment patterns in `formula` or `default_value` properties:
|
|
62
|
+
- `{traces.XXX}`
|
|
63
|
+
- `${traces.XXX}`
|
|
64
|
+
- `{signature.traces.XXX}`
|
|
65
|
+
- `${signature.traces.XXX}`
|
|
66
|
+
- `{yijianlan:...}`
|
|
67
|
+
|
|
68
|
+
**Important:** Fields with empty step names (e.g., `{traces.}`, `${signature.traces.}`) are automatically skipped. Only fields with valid step names will be transformed.
|
|
69
|
+
|
|
70
|
+
Transforms matched fields:
|
|
71
|
+
- Changes `type` to `steedos-field`
|
|
72
|
+
- Adds `__approval_comments_transformed: true` marker
|
|
73
|
+
- Stores original type in `__approval_comments_original_type`
|
|
74
|
+
- Backs up and removes original `formula` to `__approval_comments_original_formula`
|
|
75
|
+
- Backs up and removes original `default_value` to `__approval_comments_original_default_value`
|
|
76
|
+
- Adds `config` object with approval comments configuration
|
|
77
|
+
- Renames `_amisField` to `__approval_comments_original__amisField` (if exists) and removes original
|
|
78
|
+
|
|
79
|
+
##### 2. Transform `amis_schema`
|
|
80
|
+
|
|
81
|
+
Processes the root-level `amis_schema` JSON string:
|
|
82
|
+
- Parses the JSON string
|
|
83
|
+
- Scans first-level `body` array nodes
|
|
84
|
+
- Matches nodes by checking their `value` property (same patterns as above)
|
|
85
|
+
- **Important:** Nodes with empty step names (e.g., `{traces.}`) are automatically skipped
|
|
86
|
+
- Transforms matched nodes:
|
|
87
|
+
- Changes `type` to `sfield-approvalcomments`
|
|
88
|
+
- Adds `config` object
|
|
89
|
+
- Stores original type in `__approval_comments_original_type`
|
|
90
|
+
- **Removes `value` property** from transformed node
|
|
91
|
+
- Preserves all other original properties (id, label, className, required, etc.)
|
|
92
|
+
- Backs up original to `__approval_comments_original__amis_schema`
|
|
93
|
+
- Stringifies and saves transformed JSON
|
|
94
|
+
|
|
95
|
+
**Note:** Only the root `amis_schema` is transformed. `current.amis_schema` and `historys[*].amis_schema` are NOT processed.
|
|
96
|
+
|
|
97
|
+
##### 3. Handle `_amisField` Property
|
|
98
|
+
|
|
99
|
+
- If a field has `_amisField`, it's renamed to `__approval_comments_original__amisField`
|
|
100
|
+
- The original `_amisField` is removed via `$unset`
|
|
101
|
+
- This preserves the original data for perfect rollback
|
|
102
|
+
|
|
103
|
+
##### 4. Flow-based Filtering (Optional)
|
|
104
|
+
|
|
105
|
+
When the `fid` parameter is provided:
|
|
106
|
+
1. Queries the `flows` collection to find the flow by ID
|
|
107
|
+
2. Extracts the `form` property from the flow
|
|
108
|
+
3. Filters forms collection to process only that specific form
|
|
109
|
+
4. Useful for testing or targeted migrations
|
|
110
|
+
|
|
111
|
+
#### Transformation Examples
|
|
112
|
+
|
|
113
|
+
##### Example 1: current.fields Transformation
|
|
114
|
+
|
|
115
|
+
**Before:**
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"code": "部门经理意见",
|
|
119
|
+
"name": "部门领导意见",
|
|
120
|
+
"type": "input",
|
|
121
|
+
"formula": "{yijianlan:{step:'部门经理审核',default:'已阅'}}"
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**After:**
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"code": "部门经理意见",
|
|
129
|
+
"name": "部门领导意见",
|
|
130
|
+
"type": "steedos-field",
|
|
131
|
+
"__approval_comments_transformed": true,
|
|
132
|
+
"__approval_comments_original_type": "input",
|
|
133
|
+
"__approval_comments_original_formula": "{yijianlan:{step:'部门经理审核',default:'已阅'}}",
|
|
134
|
+
"config": {
|
|
135
|
+
"type": "approval_comments",
|
|
136
|
+
"label": "部门领导意见",
|
|
137
|
+
"object": "",
|
|
138
|
+
"name": "部门经理意见",
|
|
139
|
+
"steps": [
|
|
140
|
+
{
|
|
141
|
+
"name": "部门经理审核",
|
|
142
|
+
"show_cc": true,
|
|
143
|
+
"show_handler": true,
|
|
144
|
+
"show_image_sign": false,
|
|
145
|
+
"default": "已阅"
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Note:** Original `formula` is backed up to `__approval_comments_original_formula` and then removed from the field.
|
|
153
|
+
|
|
154
|
+
##### Example 2: amis_schema Transformation
|
|
155
|
+
|
|
156
|
+
**Before:**
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"type": "input-text",
|
|
160
|
+
"label": "项目所在单位技术发展部",
|
|
161
|
+
"name": "项目所在单位技术发展部",
|
|
162
|
+
"required": true,
|
|
163
|
+
"className": "is_wide",
|
|
164
|
+
"value": "${signature.traces.项目所在单位技术发展部审批}",
|
|
165
|
+
"id": "u:e1562890eed5"
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**After:**
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"type": "sfield-approvalcomments",
|
|
173
|
+
"label": "项目所在单位技术发展部",
|
|
174
|
+
"name": "项目所在单位技术发展部",
|
|
175
|
+
"required": true,
|
|
176
|
+
"className": "is_wide",
|
|
177
|
+
"id": "u:e1562890eed5",
|
|
178
|
+
"__approval_comments_original_type": "input-text",
|
|
179
|
+
"config": {
|
|
180
|
+
"type": "approval_comments",
|
|
181
|
+
"label": "项目所在单位技术发展部",
|
|
182
|
+
"object": "",
|
|
183
|
+
"name": "项目所在单位技术发展部",
|
|
184
|
+
"steps": [
|
|
185
|
+
{
|
|
186
|
+
"name": "项目所在单位技术发展部审批",
|
|
187
|
+
"show_cc": true,
|
|
188
|
+
"show_handler": true,
|
|
189
|
+
"show_image_sign": true
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Note:** Original `value` property is removed from the transformed node. Original `type` is backed up to `__approval_comments_original_type`.
|
|
197
|
+
|
|
198
|
+
##### Example 3: _amisField Handling
|
|
199
|
+
|
|
200
|
+
**Before:**
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"code": "test_field",
|
|
204
|
+
"type": "input",
|
|
205
|
+
"formula": "{traces.测试}",
|
|
206
|
+
"_amisField": {
|
|
207
|
+
"label": "测试字段",
|
|
208
|
+
"type": "input-text"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**After:**
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"code": "test_field",
|
|
217
|
+
"type": "steedos-field",
|
|
218
|
+
"__approval_comments_transformed": true,
|
|
219
|
+
"__approval_comments_original_type": "input",
|
|
220
|
+
"__approval_comments_original_formula": "{traces.测试}",
|
|
221
|
+
"__approval_comments_original__amisField": {
|
|
222
|
+
"label": "测试字段",
|
|
223
|
+
"type": "input-text"
|
|
224
|
+
},
|
|
225
|
+
"config": {
|
|
226
|
+
"type": "approval_comments",
|
|
227
|
+
"label": "test_field",
|
|
228
|
+
"object": "",
|
|
229
|
+
"name": "test_field",
|
|
230
|
+
"steps": [...]
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Note:** Original `_amisField` is renamed to `__approval_comments_original__amisField` and removed. Original `formula` is backed up and removed.
|
|
236
|
+
Note: Original `_amisField` is removed via `$unset`.
|
|
237
|
+
|
|
238
|
+
### 2. Rollback Endpoint
|
|
239
|
+
|
|
240
|
+
**GET** `/api/workflow/rollbackApprovalCommentsField`
|
|
241
|
+
|
|
242
|
+
Reverts fields that were transformed back to their original format.
|
|
243
|
+
|
|
244
|
+
#### Parameters
|
|
245
|
+
|
|
246
|
+
- `batchSize` (number, optional): Number of bulk operations per batch. Default: 500
|
|
247
|
+
- `dryRun` (boolean, optional): When true, simulates the rollback without modifying data. Default: false
|
|
248
|
+
- `fid` (string, optional): Flow ID to rollback only a single flow's form. When provided, only the form associated with this flow will be processed.
|
|
249
|
+
|
|
250
|
+
#### Request Examples
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Rollback all enabled forms
|
|
254
|
+
curl -X GET "http://localhost:5000/api/workflow/rollbackApprovalCommentsField" \
|
|
255
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
256
|
+
|
|
257
|
+
# Rollback only a specific flow's form
|
|
258
|
+
curl -X GET "http://localhost:5000/api/workflow/rollbackApprovalCommentsField?fid=flow_id_here" \
|
|
259
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### Response
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"totalForms": 10,
|
|
267
|
+
"totalFields": 25,
|
|
268
|
+
"errors": [],
|
|
269
|
+
"dryRun": false,
|
|
270
|
+
"message": "Rollback completed"
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### What It Does
|
|
275
|
+
|
|
276
|
+
The rollback endpoint performs three types of restorations on **enabled forms only** (`state: 'enabled'`) with enhanced safety guarantees:
|
|
277
|
+
|
|
278
|
+
##### 1. Restore `current.fields` and `historys.fields`
|
|
279
|
+
|
|
280
|
+
**Idempotency (幂等性):**
|
|
281
|
+
- Only processes fields marked with `__approval_comments_transformed: true`
|
|
282
|
+
- Safe to run multiple times without side effects
|
|
283
|
+
|
|
284
|
+
**Zero Loss (零丢失):**
|
|
285
|
+
1. Restores `type` with priority:
|
|
286
|
+
- **First**: Checks `__approval_comments_original_type` (if stored during migration)
|
|
287
|
+
- **Fallback**: Uses 'input' (for backward compatibility with legacy data)
|
|
288
|
+
2. Restores `formula` from `__approval_comments_original_formula` (if exists)
|
|
289
|
+
3. Restores `default_value` from `__approval_comments_original_default_value` (if exists)
|
|
290
|
+
4. Restores `_amisField` from `__approval_comments_original__amisField` (if exists)
|
|
291
|
+
5. This ensures perfect restoration of all original field properties
|
|
292
|
+
|
|
293
|
+
**Full Coverage (全覆盖):**
|
|
294
|
+
- Processes all `historys` array items recursively
|
|
295
|
+
- No fields are missed in any history version
|
|
296
|
+
|
|
297
|
+
**Silent Failure (静默失败):**
|
|
298
|
+
- Field-level error handling with try-catch
|
|
299
|
+
- Individual field errors don't stop batch processing
|
|
300
|
+
- All errors are recorded with detailed context:
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"formId": "form_123",
|
|
304
|
+
"formName": "申请表",
|
|
305
|
+
"fieldPath": "current.fields.5",
|
|
306
|
+
"fieldCode": "approval_field",
|
|
307
|
+
"error": "Field data format mismatch"
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Restoration steps:**
|
|
312
|
+
1. Finds forms with fields marked `__approval_comments_transformed: true` and `state: 'enabled'`
|
|
313
|
+
2. Restores `type` from `__approval_comments_original_type` (or defaults to 'input')
|
|
314
|
+
3. Restores `formula` from `__approval_comments_original_formula` (if exists)
|
|
315
|
+
4. Restores `default_value` from `__approval_comments_original_default_value` (if exists)
|
|
316
|
+
5. Restores `_amisField` from `__approval_comments_original__amisField` (if exists)
|
|
317
|
+
6. Removes added properties:
|
|
318
|
+
- `__approval_comments_transformed`
|
|
319
|
+
- `config`
|
|
320
|
+
- `__approval_comments_original__amisField`
|
|
321
|
+
- `__approval_comments_original_type`
|
|
322
|
+
- `__approval_comments_original_formula`
|
|
323
|
+
- `__approval_comments_original_default_value`
|
|
324
|
+
|
|
325
|
+
##### 2. Restore `amis_schema`
|
|
326
|
+
|
|
327
|
+
1. Checks if `__approval_comments_original__amis_schema` exists
|
|
328
|
+
2. Restores original `amis_schema` from backup
|
|
329
|
+
3. Removes `__approval_comments_original__amis_schema`
|
|
330
|
+
|
|
331
|
+
This completely restores the amis_schema JSON string to its original state.
|
|
332
|
+
|
|
333
|
+
##### 3. Restore `_amisField` Property
|
|
334
|
+
|
|
335
|
+
- If `__approval_comments_origin__amisField` exists, it's restored back to `_amisField`
|
|
336
|
+
- The backup property is then removed via `$unset`
|
|
337
|
+
- This ensures perfect restoration of the original field structure
|
|
338
|
+
|
|
339
|
+
#### Rollback Example
|
|
340
|
+
|
|
341
|
+
**Before Rollback (Transformed):**
|
|
342
|
+
```json
|
|
343
|
+
{
|
|
344
|
+
"type": "steedos-field",
|
|
345
|
+
"__approval_comments_transformed": true,
|
|
346
|
+
"__approval_comments_original_type": "textarea",
|
|
347
|
+
"__approval_comments_original_formula": "{traces.审批}",
|
|
348
|
+
"__approval_comments_original_default_value": "已阅",
|
|
349
|
+
"__approval_comments_original__amisField": {
|
|
350
|
+
"label": "测试",
|
|
351
|
+
"type": "input-text"
|
|
352
|
+
},
|
|
353
|
+
"config": {...}
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**After Rollback (Restored):**
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"type": "textarea",
|
|
361
|
+
"formula": "{traces.审批}",
|
|
362
|
+
"default_value": "已阅",
|
|
363
|
+
"_amisField": {
|
|
364
|
+
"label": "测试",
|
|
365
|
+
"type": "input-text"
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Zero Loss Guarantee:**
|
|
371
|
+
- Original type "textarea" is restored from `__approval_comments_original_type`
|
|
372
|
+
- Original `formula` is restored from `__approval_comments_original_formula`
|
|
373
|
+
- Original `default_value` is restored from `__approval_comments_original_default_value`
|
|
374
|
+
- Original `_amisField` is restored from `__approval_comments_original__amisField`
|
|
375
|
+
- Not hardcoded - preserves exact original values
|
|
376
|
+
- Backward compatible: Falls back to "input" for type if backup doesn't exist
|
|
377
|
+
|
|
378
|
+
#### Enhanced Error Handling
|
|
379
|
+
|
|
380
|
+
Field-level errors are captured with detailed context:
|
|
381
|
+
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"totalForms": 10,
|
|
385
|
+
"totalFields": 23,
|
|
386
|
+
"errors": [
|
|
387
|
+
{
|
|
388
|
+
"formId": "form-123",
|
|
389
|
+
"formName": "申请表",
|
|
390
|
+
"fieldPath": "current.fields.5",
|
|
391
|
+
"fieldCode": "approval_field",
|
|
392
|
+
"error": "Field data format mismatch"
|
|
393
|
+
}
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Silent Failure Benefits:**
|
|
399
|
+
- Individual field errors don't stop the batch
|
|
400
|
+
- All errors are recorded with field identification
|
|
401
|
+
- Easy to trace and fix problematic fields
|
|
402
|
+
- Batch processing continues for other fields
|
|
403
|
+
|
|
404
|
+
## Query Scope and Filtering
|
|
405
|
+
|
|
406
|
+
### State Filtering
|
|
407
|
+
|
|
408
|
+
Both migration and rollback endpoints only process forms with `state: 'enabled'`:
|
|
409
|
+
|
|
410
|
+
```javascript
|
|
411
|
+
// Query filter includes
|
|
412
|
+
{
|
|
413
|
+
state: 'enabled',
|
|
414
|
+
// ... other conditions
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Why enabled forms only?**
|
|
419
|
+
- Disabled forms are typically archived or deprecated
|
|
420
|
+
- Prevents unnecessary processing of inactive forms
|
|
421
|
+
- Reduces processing time and potential errors
|
|
422
|
+
- Only active production forms are migrated
|
|
423
|
+
|
|
424
|
+
### Flow-based Filtering (fid Parameter)
|
|
425
|
+
|
|
426
|
+
When the optional `fid` parameter is provided, only that specific flow's form is processed:
|
|
427
|
+
|
|
428
|
+
**Migration with fid:**
|
|
429
|
+
```bash
|
|
430
|
+
GET /api/workflow/migrateApprovalCommentsField?fid=flow_abc123
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Rollback with fid:**
|
|
434
|
+
```bash
|
|
435
|
+
GET /api/workflow/rollbackApprovalCommentsField?fid=flow_abc123
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**How it works:**
|
|
439
|
+
1. Query `flows` collection: `{ _id: fid }`
|
|
440
|
+
2. Extract `flow.form` property
|
|
441
|
+
3. Add to query filter: `{ _id: flow.form, state: 'enabled' }`
|
|
442
|
+
|
|
443
|
+
**Use cases:**
|
|
444
|
+
- **Testing**: Test migration on a single flow before full rollout
|
|
445
|
+
- **Debugging**: Troubleshoot specific flow issues
|
|
446
|
+
- **Targeted Updates**: Update only certain flows incrementally
|
|
447
|
+
- **Error Recovery**: Re-run migration for specific flows that had errors
|
|
448
|
+
|
|
449
|
+
**Error handling:**
|
|
450
|
+
- If flow not found: Returns error `"Flow with ID {fid} not found"`
|
|
451
|
+
- If flow has no form: Returns error `"Flow {fid} does not have a form associated"`
|
|
452
|
+
|
|
453
|
+
### What Gets Transformed
|
|
454
|
+
|
|
455
|
+
✅ **Processes:**
|
|
456
|
+
- Forms with `state: 'enabled'`
|
|
457
|
+
- `current.fields` array (first level only)
|
|
458
|
+
- `historys[n].fields` arrays (first level only, all history items)
|
|
459
|
+
- Root-level `amis_schema` JSON string
|
|
460
|
+
|
|
461
|
+
❌ **Does NOT Process:**
|
|
462
|
+
- Forms with `state != 'enabled'` (archived, disabled, etc.)
|
|
463
|
+
- Nested fields (fields inside fields)
|
|
464
|
+
- `current.amis_schema` (not the root one)
|
|
465
|
+
- `historys[n].amis_schema` (history-specific schemas)
|
|
466
|
+
- Fields without approval comment patterns
|
|
467
|
+
- Fields with empty step names (`{traces.}`)
|
|
468
|
+
|
|
469
|
+
## Security
|
|
470
|
+
|
|
471
|
+
### Authentication & Authorization
|
|
472
|
+
|
|
473
|
+
- Both endpoints require authentication
|
|
474
|
+
- Only users with `is_space_admin: true` can execute these endpoints
|
|
475
|
+
- Proper error messages are returned for unauthorized access
|
|
476
|
+
|
|
477
|
+
### Data Integrity
|
|
478
|
+
|
|
479
|
+
- Original field properties are preserved (except `type` and added properties)
|
|
480
|
+
- Uses MongoDB's `bulkWrite` with `ordered: false` for atomic operations
|
|
481
|
+
- Transformations are reversible using the rollback endpoint
|
|
482
|
+
- Dry run mode allows previewing changes before applying
|
|
483
|
+
|
|
484
|
+
### Performance & Safety
|
|
485
|
+
|
|
486
|
+
- **Memory Efficient**: Uses MongoDB cursor iteration instead of loading all documents
|
|
487
|
+
- **Batch Processing**: BulkWrite operations in configurable batches (default 500)
|
|
488
|
+
- **Error Handling**: Errors are logged and returned without stopping entire operation
|
|
489
|
+
- **No Code Injection**: Uses safe string matching and JSON parsing
|
|
490
|
+
|
|
491
|
+
## Configuration Parsing
|
|
492
|
+
|
|
493
|
+
### Config Object Structure
|
|
494
|
+
|
|
495
|
+
The `config` object generated during transformation has the following structure:
|
|
496
|
+
|
|
497
|
+
```json
|
|
498
|
+
{
|
|
499
|
+
"type": "approval_comments",
|
|
500
|
+
"label": "<field label>",
|
|
501
|
+
"object": "",
|
|
502
|
+
"name": "<field name or code>",
|
|
503
|
+
"steps": [
|
|
504
|
+
{
|
|
505
|
+
"name": "<step name>",
|
|
506
|
+
"show_cc": true/false,
|
|
507
|
+
"show_handler": true/false,
|
|
508
|
+
"show_image_sign": true/false,
|
|
509
|
+
"default": "<optional default value>"
|
|
510
|
+
}
|
|
511
|
+
]
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Key Properties:**
|
|
516
|
+
- `type`: Always "approval_comments"
|
|
517
|
+
- `label`: From field's `name` or `code` for fields, from `label` or `name` for amis nodes
|
|
518
|
+
- `object`: Always empty string ""
|
|
519
|
+
- `name`:
|
|
520
|
+
- For `current.fields`/`historys.fields`: field's `code` or `name`
|
|
521
|
+
- For `amis_schema` nodes: original node's `name` property
|
|
522
|
+
- `steps`: Array of step configurations
|
|
523
|
+
|
|
524
|
+
**Differences between field types:**
|
|
525
|
+
- `current.fields`/`historys.fields`: Transform to `type: "steedos-field"` with `config.name = field.code`
|
|
526
|
+
- `amis_schema` nodes: Transform to `type: "sfield-approvalcomments"` with `config.name = node.name`
|
|
527
|
+
|
|
528
|
+
### yijianlan Format
|
|
529
|
+
|
|
530
|
+
The endpoint handles non-standard JSON format in `{yijianlan:...}`:
|
|
531
|
+
|
|
532
|
+
```javascript
|
|
533
|
+
// Input format (unquoted keys, single quotes for values)
|
|
534
|
+
{yijianlan:{step:'部门经理审核',default:'已阅',only_cc:true}}
|
|
535
|
+
|
|
536
|
+
// Parsed to:
|
|
537
|
+
{
|
|
538
|
+
step: "部门经理审核",
|
|
539
|
+
default: "已阅",
|
|
540
|
+
only_cc: true
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Special Boolean Logic
|
|
545
|
+
|
|
546
|
+
The migration correctly implements the legacy approval script logic inversion:
|
|
547
|
+
|
|
548
|
+
- `only_cc: true` → `show_cc: true, show_handler: false`
|
|
549
|
+
- **Legacy behavior:** Show 抄送 (CC), hide 经办人 (Handler)
|
|
550
|
+
- **Logic inversion:** `only_cc` controls whether to hide the handler input
|
|
551
|
+
|
|
552
|
+
- `only_handler: true` → `show_cc: false, show_handler: true`
|
|
553
|
+
- **Legacy behavior:** Hide 抄送 (CC), show 经办人 (Handler)
|
|
554
|
+
- **Logic inversion:** `only_handler` controls whether to hide the CC input
|
|
555
|
+
|
|
556
|
+
- Default (neither specified): `show_cc: true, show_handler: true`
|
|
557
|
+
- **Legacy behavior:** Show both input boxes
|
|
558
|
+
- Both parameters default to `true` when not specified
|
|
559
|
+
|
|
560
|
+
This follows the "老版本意见栏脚本逻辑规范 (Legacy Approval Script Specs)" exactly.
|
|
561
|
+
|
|
562
|
+
### Image Sign Detection
|
|
563
|
+
|
|
564
|
+
- `show_image_sign: true` when:
|
|
565
|
+
- Pattern contains `signature.traces` keyword
|
|
566
|
+
- `yijianlan.image_sign === true`
|
|
567
|
+
|
|
568
|
+
This maps the legacy electronic signature (电子签章) control to the new format.
|
|
569
|
+
|
|
570
|
+
### Legacy Parameter Support
|
|
571
|
+
|
|
572
|
+
The migration supports all legacy `yijianlan` parameters:
|
|
573
|
+
|
|
574
|
+
| Legacy Parameter | Type | Purpose | New Mapping |
|
|
575
|
+
|-----------------|------|---------|-------------|
|
|
576
|
+
| `step` | String | Step name (required) | `steps[].name` |
|
|
577
|
+
| `default` | String | Default comment text | `steps[].default` |
|
|
578
|
+
| `only_cc` | Boolean | Show only CC box | `show_cc: true, show_handler: false` |
|
|
579
|
+
| `only_handler` | Boolean | Show only handler box | `show_cc: false, show_handler: true` |
|
|
580
|
+
| `image_sign` | Boolean | Enable signature | `show_image_sign: true` |
|
|
581
|
+
|
|
582
|
+
## Transformation Scope
|
|
583
|
+
|
|
584
|
+
### What Gets Transformed
|
|
585
|
+
|
|
586
|
+
**current.fields and historys[].fields:**
|
|
587
|
+
- Fields with `formula` or `default_value` matching approval patterns
|
|
588
|
+
- Transform to `type: "steedos-field"`
|
|
589
|
+
- Only first-level fields (no nested fields)
|
|
590
|
+
|
|
591
|
+
**amis_schema:**
|
|
592
|
+
- Root-level `amis_schema` only (NOT `current.amis_schema` or `historys[*].amis_schema`)
|
|
593
|
+
- First-level `body` array nodes with `value` matching approval patterns
|
|
594
|
+
- Transform to `type: "sfield-approvalcomments"`
|
|
595
|
+
- Preserves all original properties (id, className, required, etc.)
|
|
596
|
+
|
|
597
|
+
**_amisField:**
|
|
598
|
+
- Renamed to `__approval_comments_origin__amisField`
|
|
599
|
+
- Original removed via `$unset`
|
|
600
|
+
|
|
601
|
+
### What Does NOT Get Transformed
|
|
602
|
+
|
|
603
|
+
- `current.amis_schema` (not processed)
|
|
604
|
+
- `historys[*].amis_schema` (not processed)
|
|
605
|
+
- Nested fields within `current.fields` or `historys.fields`
|
|
606
|
+
- Nested body nodes within amis_schema body array
|
|
607
|
+
- Fields without matching patterns
|
|
608
|
+
- Fields already transformed (with `__approval_comments_transformed: true`)
|
|
609
|
+
- **Fields with empty step names** (e.g., `{traces.}`, `${signature.traces.}`, `{yijianlan:{step:''}}`)
|
|
610
|
+
- These are automatically detected and skipped
|
|
611
|
+
- Only fields with valid, non-empty step names are transformed
|
|
612
|
+
|
|
613
|
+
### Important: Forms Without amis_schema
|
|
614
|
+
|
|
615
|
+
Some older forms may not have the `amis_schema` property in the database.
|
|
616
|
+
|
|
617
|
+
**How Migration Handles This:**
|
|
618
|
+
1. Migration checks `if (form.amis_schema)` before processing
|
|
619
|
+
2. Forms without `amis_schema` property are skipped during migration
|
|
620
|
+
3. Only `current.fields` and `historys.fields` are transformed for these forms
|
|
621
|
+
4. `amis_schema` will be generated later when designer opens the form
|
|
622
|
+
|
|
623
|
+
**Form Designer Fix:**
|
|
624
|
+
The form designer endpoint `/api/workflow/form_design` (implemented in `flow_form_design.ejs`) now recognizes the new approval_comments field format in the `getFieldEditTpl()` function (line 274).
|
|
625
|
+
|
|
626
|
+
When forms don't have `amis_schema`, the designer auto-generates it from `current.fields` at line 172 (`if (!schema)` block). The fix ensures migrated approval_comments fields generate proper `sfield-approvalcomments` AMIS nodes instead of incomplete fields.
|
|
627
|
+
|
|
628
|
+
**Result:**
|
|
629
|
+
- Execution order doesn't matter (migration first or designer first - both work)
|
|
630
|
+
- Designer generates correct `sfield-approvalcomments` nodes from migrated fields
|
|
631
|
+
- Consistency maintained between `current.fields` and `amis_schema`
|
|
632
|
+
- No manual intervention needed
|
|
633
|
+
|
|
634
|
+
## Pattern Matching Details
|
|
635
|
+
|
|
636
|
+
### Valid Patterns (Will Transform)
|
|
637
|
+
- `{traces.步骤名}` ✓
|
|
638
|
+
- `${traces.部门审批}` ✓
|
|
639
|
+
- `{signature.traces.技术审核}` ✓
|
|
640
|
+
- `${signature.traces.经理审批}` ✓
|
|
641
|
+
- `{yijianlan:{step:'部门审核'}}` ✓
|
|
642
|
+
|
|
643
|
+
### Invalid Patterns (Will Skip)
|
|
644
|
+
- `{traces.}` ✗ (empty step name)
|
|
645
|
+
- `${traces.}` ✗ (empty step name)
|
|
646
|
+
- `{signature.traces.}` ✗ (empty step name)
|
|
647
|
+
- `${signature.traces.}` ✗ (empty step name)
|
|
648
|
+
- `{yijianlan:{step:''}}` ✗ (empty step name)
|
|
649
|
+
- `{yijianlan:{}}` ✗ (no step property)
|
|
650
|
+
|
|
651
|
+
The regex patterns require at least one character for the step name. Empty step names are automatically filtered out during the `extractStepName()` process.
|
|
652
|
+
|
|
653
|
+
## Error Handling
|
|
654
|
+
|
|
655
|
+
Errors are collected and returned in the response:
|
|
656
|
+
|
|
657
|
+
```json
|
|
658
|
+
{
|
|
659
|
+
"totalForms": 10,
|
|
660
|
+
"totalFields": 23,
|
|
661
|
+
"errors": [
|
|
662
|
+
{
|
|
663
|
+
"formId": "form-123",
|
|
664
|
+
"formName": "申请表",
|
|
665
|
+
"error": "Error message"
|
|
666
|
+
}
|
|
667
|
+
]
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
## Best Practices
|
|
672
|
+
|
|
673
|
+
1. **Always test with dry run first:**
|
|
674
|
+
```bash
|
|
675
|
+
GET /api/workflow/migrateApprovalCommentsField?dryRun=true
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
2. **Backup your database before running the transformation**
|
|
679
|
+
- This is critical as the transformation modifies both field configurations and amis_schema JSON
|
|
680
|
+
|
|
681
|
+
3. **Use appropriate batch sizes:**
|
|
682
|
+
- Small databases: 500 (default)
|
|
683
|
+
- Large databases with many forms: 100-200
|
|
684
|
+
- Very large databases: Consider running during off-peak hours
|
|
685
|
+
|
|
686
|
+
4. **Monitor the errors array in the response** to identify any problematic forms
|
|
687
|
+
- Check for JSON parsing errors in amis_schema
|
|
688
|
+
- Verify pattern matching results
|
|
689
|
+
|
|
690
|
+
5. **Keep the rollback option available** until you've verified the transformation works correctly
|
|
691
|
+
- Test in a staging environment first
|
|
692
|
+
- Verify both field and amis_schema transformations
|
|
693
|
+
|
|
694
|
+
6. **Verify transformed forms in the UI**
|
|
695
|
+
- Check that approval comments fields render correctly
|
|
696
|
+
- Verify amis form designer still works with transformed schema
|
|
697
|
+
|
|
698
|
+
7. **Note the differences:**
|
|
699
|
+
- Fields transform to `type: "steedos-field"`
|
|
700
|
+
- Amis nodes transform to `type: "sfield-approvalcomments"`
|
|
701
|
+
- Both get the same `config` structure but with appropriate `name` values
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
## Rollback Safety Principles
|
|
705
|
+
|
|
706
|
+
The rollback logic implements four inviolable principles (四大不可逾越原则):
|
|
707
|
+
|
|
708
|
+
### 1. 幂等性 (Idempotency)
|
|
709
|
+
Only processes fields with `__approval_comments_transformed: true` marker.
|
|
710
|
+
- **Benefit**: Safe to run multiple times
|
|
711
|
+
- **Guarantee**: No duplicate processing
|
|
712
|
+
- **Use Case**: Can retry safely if first attempt partially fails
|
|
713
|
+
|
|
714
|
+
### 2. 零丢失 (Zero Loss)
|
|
715
|
+
Prioritizes stored original values for perfect restoration.
|
|
716
|
+
- **Primary**: Restores from `__original_type` (stored during migration)
|
|
717
|
+
- **Fallback**: Uses 'input' for backward compatibility with legacy data
|
|
718
|
+
- **Benefit**: Perfect type restoration (textarea, select, date, etc.)
|
|
719
|
+
- **Guarantee**: No data loss, even for complex field types
|
|
720
|
+
|
|
721
|
+
Example:
|
|
722
|
+
```javascript
|
|
723
|
+
// Migration stores:
|
|
724
|
+
{ type: 'textarea', __original_type: 'textarea', ... }
|
|
725
|
+
|
|
726
|
+
// Rollback restores:
|
|
727
|
+
{ type: 'textarea', ... } // NOT hardcoded to 'input'
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### 3. 全覆盖 (Full Coverage)
|
|
731
|
+
Recursively processes all historys array items.
|
|
732
|
+
- **Coverage**: `current.fields`, `historys[0].fields`, `historys[1].fields`, ..., `historys[n].fields`
|
|
733
|
+
- **Benefit**: No field versions are missed
|
|
734
|
+
- **Guarantee**: Complete rollback across all form versions
|
|
735
|
+
|
|
736
|
+
### 4. 静默失败 (Silent Failure)
|
|
737
|
+
Field-level error handling with detailed reporting.
|
|
738
|
+
- **Isolation**: Individual field errors don't stop batch processing
|
|
739
|
+
- **Reporting**: All errors recorded with field identification
|
|
740
|
+
- **Benefit**: Maximum data recovery even with problematic fields
|
|
741
|
+
- **Guarantee**: Batch processing always completes
|
|
742
|
+
|
|
743
|
+
Error example:
|
|
744
|
+
```json
|
|
745
|
+
{
|
|
746
|
+
"formId": "form-123",
|
|
747
|
+
"formName": "申请表",
|
|
748
|
+
"fieldPath": "current.fields.5",
|
|
749
|
+
"fieldCode": "approval_field",
|
|
750
|
+
"error": "Field data format mismatch"
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
These principles ensure the rollback operation is:
|
|
755
|
+
- ✅ Safe (Idempotent)
|
|
756
|
+
- ✅ Complete (Full Coverage)
|
|
757
|
+
- ✅ Accurate (Zero Loss)
|
|
758
|
+
- ✅ Robust (Silent Failure)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
## Monitoring
|
|
762
|
+
|
|
763
|
+
Check the server logs for detailed information:
|
|
764
|
+
- MongoDB connection status
|
|
765
|
+
- Processing progress
|
|
766
|
+
- Detailed error messages
|
|
767
|
+
- Performance metrics
|
|
768
|
+
|
|
769
|
+
## Limitations
|
|
770
|
+
|
|
771
|
+
1. **First-level fields only**: Does not process nested fields within fields
|
|
772
|
+
2. **First-level body nodes only**: Does not process nested body arrays within amis_schema
|
|
773
|
+
3. **Pattern matching**: Only matches the specific patterns listed above
|
|
774
|
+
4. **Specific amis_schema only**: Only processes root `amis_schema`, not `current.amis_schema` or `historys[*].amis_schema`
|
|
775
|
+
5. **Non-reversible data**: If you manually modify transformed fields, rollback may not work correctly
|
|
776
|
+
6. **MongoDB only**: Designed specifically for MongoDB databases
|
|
777
|
+
|
|
778
|
+
## Troubleshooting
|
|
779
|
+
|
|
780
|
+
### Issue: Memory errors during migration/rollback
|
|
781
|
+
|
|
782
|
+
**Symptoms**: Process crashes, out-of-memory errors, or very slow performance
|
|
783
|
+
|
|
784
|
+
**Solution**:
|
|
785
|
+
|
|
786
|
+
1. **Reduce batchSize** to a smaller value (e.g., 200 or 100)
|
|
787
|
+
2. **Process specific flows** using the `fid` parameter if needed
|
|
788
|
+
```bash
|
|
789
|
+
# Example: Use conservative batch size
|
|
790
|
+
curl -X GET "http://localhost:5000/api/workflow/migrateApprovalCommentsField?batchSize=100"
|
|
791
|
+
```
|
|
792
|
+
4. **Run during off-peak hours** when server has more available resources
|
|
793
|
+
5. **Process by flow** using the `fid` parameter to handle one flow at a time:
|
|
794
|
+
```bash
|
|
795
|
+
curl -X GET "http://localhost:5000/api/workflow/migrateApprovalCommentsField?fid=flow_123&batchSize=50"
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
### Issue: Migration is too slow
|
|
799
|
+
|
|
800
|
+
**Symptoms**: Taking too long to process all forms
|
|
801
|
+
|
|
802
|
+
**Solution**:
|
|
803
|
+
|
|
804
|
+
1. **Increase batchSize** to a larger value (e.g., 500 or 1000)
|
|
805
|
+
2. **Process during off-peak hours** for better performance
|
|
806
|
+
```bash
|
|
807
|
+
curl -X GET "http://localhost:5000/api/workflow/migrateApprovalCommentsField?batchSize=500"
|
|
808
|
+
```
|
|
809
|
+
4. **For very large databases** (>10,000 forms): Consider using aggressive alternative (1000)
|
|
810
|
+
|
|
811
|
+
### Issue: Some fields not transformed
|
|
812
|
+
|
|
813
|
+
**Solution**:
|
|
814
|
+
- For fields: Check if `formula` or `default_value` matches one of the supported patterns
|
|
815
|
+
- For amis_schema: Check if node's `value` property matches the patterns
|
|
816
|
+
- Ensure field is not already transformed (no `__approval_comments_transformed: true`)
|
|
817
|
+
- **Check for empty step names**: Patterns like `{traces.}` with nothing after the dot will be automatically skipped
|
|
818
|
+
- Valid: `{traces.步骤名}`
|
|
819
|
+
- Invalid: `{traces.}` (empty step name - will be skipped)
|
|
820
|
+
|
|
821
|
+
### Issue: Field has traces pattern but not transforming
|
|
822
|
+
|
|
823
|
+
**Solution**:
|
|
824
|
+
- Verify the step name is not empty: `{traces.XXX}` where XXX must have at least one character
|
|
825
|
+
- Check the exact pattern in the formula/default_value property
|
|
826
|
+
- Common issue: `{traces.}` with empty step name will be intentionally skipped
|
|
827
|
+
- Use dry run mode to see which fields are being processed
|
|
828
|
+
|
|
829
|
+
### Issue: amis_schema parsing errors
|
|
830
|
+
|
|
831
|
+
**Solution**:
|
|
832
|
+
- Check server logs for detailed error messages
|
|
833
|
+
- Verify the amis_schema is valid JSON
|
|
834
|
+
- Ensure the schema has a `body` array property
|
|
835
|
+
- Check that body nodes have valid structure
|
|
836
|
+
|
|
837
|
+
### Issue: yijianlan parsing errors
|
|
838
|
+
|
|
839
|
+
**Solution**: Check server logs for detailed error messages. The yijianlan format must follow the expected structure: `{yijianlan:{key:'value'}}`
|
|
840
|
+
|
|
841
|
+
### Issue: Rollback doesn't work
|
|
842
|
+
|
|
843
|
+
**Solution**:
|
|
844
|
+
- Ensure the field has the `__approval_comments_transformed: true` marker
|
|
845
|
+
- Check if backup properties exist (`__approval_comments_origin__amisField`, `__approval_comments_origin__amis_schema`)
|
|
846
|
+
- Manual modifications may prevent proper rollback
|
|
847
|
+
- Verify no database connection issues
|
|
848
|
+
|
|
849
|
+
### Issue: amis form designer not working after transformation
|
|
850
|
+
|
|
851
|
+
**Solution**:
|
|
852
|
+
- Verify the transformed amis_schema is valid JSON
|
|
853
|
+
- Check that all required properties (id, type, config) are present
|
|
854
|
+
- Try rollback and re-run transformation if needed
|