@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.
@@ -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