@steedos-labs/plugin-workflow 3.0.51 → 3.0.53
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/designer/dist/amis-renderer/amis-renderer.js +1 -1
- package/designer/dist/assets/{index-CeMQUt8Y.js → index-3PKde6SC.js} +150 -148
- package/designer/dist/assets/{index-Bs9qBPga.css → index-xR8ApdWL.css} +1 -1
- package/designer/dist/index.html +2 -2
- package/main/default/manager/handlers_manager.js +6 -4
- package/main/default/manager/import.js +3 -3
- package/main/default/manager/uuflow_manager.js +21 -1
- package/main/default/routes/am.router.js +24 -18
- package/main/default/routes/api_workflow_ai_form_design.router.js +25 -1
- package/main/default/routes/api_workflow_ai_form_design_stream.router.js +16 -3
- package/main/default/routes/api_workflow_next_step.router.js +1 -1
- package/main/default/routes/flow_form_design.ejs +2 -2
- package/main/default/test/FORMULA_SCAN_GUIDE.md +3 -0
- package/main/default/test/reports/formula-scan/SCAN_HISTORY.md +57 -0
- package/main/default/test/reports/formula-scan/scan-result-v10-20260327.json +31543 -0
- package/main/default/test/reports/formula-scan/scan-result-v9-20260327.json +31723 -0
- package/main/default/test/scan_production_formulas.js +25 -5
- package/main/default/triggers/amis_form_design.trigger.js +18 -17
- package/package.json +1 -1
- package/public/amis-renderer/amis-renderer.js +1 -1
|
@@ -183,7 +183,7 @@ router.post('/am/forms', async function (req, res) {
|
|
|
183
183
|
});
|
|
184
184
|
let companyId = currentSpaceUser.company_id;
|
|
185
185
|
let newForm = {
|
|
186
|
-
_id: form["id"],
|
|
186
|
+
_id: form["id"] || form["_id"],
|
|
187
187
|
name: form["name"],
|
|
188
188
|
state: "disabled",
|
|
189
189
|
is_deleted: false,
|
|
@@ -204,7 +204,7 @@ router.post('/am/forms', async function (req, res) {
|
|
|
204
204
|
newForm.company_id = companyId;
|
|
205
205
|
}
|
|
206
206
|
let current = {
|
|
207
|
-
_id: form["current"]["id"],
|
|
207
|
+
_id: form["current"]["id"] || form["current"]["_id"],
|
|
208
208
|
name_forumla: name_forumla,
|
|
209
209
|
form: newForm._id,
|
|
210
210
|
_rev: 1,
|
|
@@ -274,7 +274,7 @@ router.post('/am/forms', async function (req, res) {
|
|
|
274
274
|
let flow = {
|
|
275
275
|
_id: _makeNewID(),
|
|
276
276
|
space: form["space"],
|
|
277
|
-
form: form["id"],
|
|
277
|
+
form: form["id"] || form["_id"],
|
|
278
278
|
name_formula: "",
|
|
279
279
|
code_formula: "",
|
|
280
280
|
is_valid: true,
|
|
@@ -323,7 +323,7 @@ router.post('/am/forms', async function (req, res) {
|
|
|
323
323
|
steps: await designerManager.makeSteps(userId, newForm.current.fields, language),
|
|
324
324
|
_rev: 1,
|
|
325
325
|
flow: flow._id,
|
|
326
|
-
form_version: form["current"]["id"],
|
|
326
|
+
form_version: form["current"]["id"] || form["current"]["_id"],
|
|
327
327
|
created: now,
|
|
328
328
|
created_by: userId,
|
|
329
329
|
modified: now,
|
|
@@ -359,7 +359,7 @@ router.put('/am/forms', async function (req, res) {
|
|
|
359
359
|
// 执行者的身份校验
|
|
360
360
|
await designerManager.checkSpaceUserBeforeUpdate(form['space'], userId, req.user.roles)
|
|
361
361
|
// 更新表单
|
|
362
|
-
await designerManager.updateForm(form["id"], form, updatedForms, updatedFlows, userId)
|
|
362
|
+
await designerManager.updateForm(form["id"] || form["_id"], form, updatedForms, updatedFlows, userId)
|
|
363
363
|
}
|
|
364
364
|
res.send({
|
|
365
365
|
"ChangeSet": {
|
|
@@ -370,6 +370,7 @@ router.put('/am/forms', async function (req, res) {
|
|
|
370
370
|
}
|
|
371
371
|
});
|
|
372
372
|
} catch (error) {
|
|
373
|
+
console.log(error)
|
|
373
374
|
res.status(500).send(error.message)
|
|
374
375
|
}
|
|
375
376
|
})
|
|
@@ -394,7 +395,7 @@ router.delete('/am/forms', async function (req, res) {
|
|
|
394
395
|
let now = new Date();
|
|
395
396
|
for (let i = 0; i < data['Forms'].length; i++) {
|
|
396
397
|
let form = data['Forms'][i];
|
|
397
|
-
let f = await formCollection.findOne({_id: form["id"]});
|
|
398
|
+
let f = await formCollection.findOne({_id: form["id"] || form["_id"]});
|
|
398
399
|
// 执行者的身份校验
|
|
399
400
|
await designerManager.checkSpaceUserBeforeUpdate(f.space, userId, req.user.roles)
|
|
400
401
|
|
|
@@ -404,7 +405,7 @@ router.delete('/am/forms', async function (req, res) {
|
|
|
404
405
|
// 删除表单对应流程
|
|
405
406
|
let flows = await flowCollection.find({
|
|
406
407
|
space: spaceId,
|
|
407
|
-
form: form["id"]
|
|
408
|
+
form: form["id"] || form["_id"]
|
|
408
409
|
}).toArray();
|
|
409
410
|
for (let flow_fd of flows) {
|
|
410
411
|
let deleted_flow = Object.assign({}, flow_fd);
|
|
@@ -419,7 +420,7 @@ router.delete('/am/forms', async function (req, res) {
|
|
|
419
420
|
if (recordCollection) {
|
|
420
421
|
let records = await recordCollection.find({
|
|
421
422
|
space: spaceId,
|
|
422
|
-
form: form["id"]
|
|
423
|
+
form: form["id"] || form["_id"]
|
|
423
424
|
}).toArray();
|
|
424
425
|
for (let record_fd of records) {
|
|
425
426
|
let deleted_record = Object.assign({}, record_fd);
|
|
@@ -434,7 +435,7 @@ router.delete('/am/forms', async function (req, res) {
|
|
|
434
435
|
// 删除表单对应申请单
|
|
435
436
|
let instances = await instanceCollection.find({
|
|
436
437
|
space: spaceId,
|
|
437
|
-
form: form["id"]
|
|
438
|
+
form: form["id"] || form["_id"]
|
|
438
439
|
}).toArray();
|
|
439
440
|
for (let instance_fd of instances) {
|
|
440
441
|
let deleted_instance = Object.assign({}, instance_fd);
|
|
@@ -446,7 +447,7 @@ router.delete('/am/forms', async function (req, res) {
|
|
|
446
447
|
}
|
|
447
448
|
|
|
448
449
|
// 删除instance_tasks
|
|
449
|
-
await instanceCollection.deleteMany({ form: form["id"] })
|
|
450
|
+
await instanceCollection.deleteMany({ form: form["id"] || form["_id"] })
|
|
450
451
|
|
|
451
452
|
// 删除表单
|
|
452
453
|
let deleted_form = Object.assign({}, f);
|
|
@@ -454,7 +455,7 @@ router.delete('/am/forms', async function (req, res) {
|
|
|
454
455
|
deleted_form['deleted_by'] = userId;
|
|
455
456
|
await dFormsCollection.insert(deleted_form);
|
|
456
457
|
deletedForms.push(deleted_form);
|
|
457
|
-
await formCollection.deleteOne({_id: form["id"]})
|
|
458
|
+
await formCollection.deleteOne({_id: form["id"] || form["_id"]})
|
|
458
459
|
}
|
|
459
460
|
|
|
460
461
|
res.send({
|
|
@@ -483,7 +484,7 @@ router.put('/am/flows', async function (req, res) {
|
|
|
483
484
|
let flowCome = data['Flows'][i];
|
|
484
485
|
let spaceId = flowCome["space"];
|
|
485
486
|
let formId = flowCome["form"];
|
|
486
|
-
let flowId = flowCome["id"];
|
|
487
|
+
let flowId = flowCome["id"] || flowCome["_id"];
|
|
487
488
|
let upgraded = flowCome['upgraded'];
|
|
488
489
|
delete flowCome['decription'];
|
|
489
490
|
let now = new Date();
|
|
@@ -501,7 +502,7 @@ router.put('/am/flows', async function (req, res) {
|
|
|
501
502
|
// 某步骤被删除后,删除同流程的“指定历史步骤”属性中被引用的步骤id(仅限于流程的最新版)
|
|
502
503
|
let clientStepIds = []
|
|
503
504
|
_.each(flowCome['current']['steps'], function (step) {
|
|
504
|
-
clientStepIds.push(step['id']);
|
|
505
|
+
clientStepIds.push(step['_id'] || step['id']);
|
|
505
506
|
})
|
|
506
507
|
|
|
507
508
|
_.each(flowCome['current']['steps'], function (step) {
|
|
@@ -524,12 +525,16 @@ router.put('/am/flows', async function (req, res) {
|
|
|
524
525
|
|
|
525
526
|
// 由于前台传的是id而非_id,故比较时将id转为_id
|
|
526
527
|
_.each(flowCome['current']['steps'], function (step) {
|
|
527
|
-
|
|
528
|
-
|
|
528
|
+
if (step['id']) {
|
|
529
|
+
step['_id'] = step['id'];
|
|
530
|
+
delete step['id'];
|
|
531
|
+
}
|
|
529
532
|
if (step['lines']) {
|
|
530
533
|
_.each(step['lines'], function (line) {
|
|
531
|
-
|
|
532
|
-
|
|
534
|
+
if (line['id']) {
|
|
535
|
+
line['_id'] = line['id'];
|
|
536
|
+
delete line['id'];
|
|
537
|
+
}
|
|
533
538
|
})
|
|
534
539
|
}
|
|
535
540
|
})
|
|
@@ -647,6 +652,7 @@ router.put('/am/flows', async function (req, res) {
|
|
|
647
652
|
}
|
|
648
653
|
});
|
|
649
654
|
} catch (error) {
|
|
655
|
+
console.log(error);
|
|
650
656
|
res.status(500).send(error.message)
|
|
651
657
|
}
|
|
652
658
|
})
|
|
@@ -664,7 +670,7 @@ router.put('/am/flows/state', async function (req, res) {
|
|
|
664
670
|
let flowCome = data['Flows'][i];
|
|
665
671
|
let spaceId = flowCome["space"];
|
|
666
672
|
let formId = flowCome["form"];
|
|
667
|
-
let flowId = flowCome["id"];
|
|
673
|
+
let flowId = flowCome["id"] || flowCome["_id"];
|
|
668
674
|
let now = new Date();
|
|
669
675
|
let flowUpdateObj = {
|
|
670
676
|
$set: {}
|
|
@@ -39,7 +39,7 @@ router.post('/am/ai/form-design', async function auth(req, res, next) {
|
|
|
39
39
|
const baseURL = process.env.WORKFLOW_AI_BASE_URL || 'https://api.openai.com/v1';
|
|
40
40
|
const model = process.env.WORKFLOW_AI_MODEL || 'gpt-4o';
|
|
41
41
|
|
|
42
|
-
const { fields, events, userRequest, images, instance_template, form_script, flow_events } = req.body;
|
|
42
|
+
const { fields, events, userRequest, images, instance_template, form_script, flow_events, name_forumla } = req.body;
|
|
43
43
|
|
|
44
44
|
if (!userRequest) {
|
|
45
45
|
return res.status(400).json({ error: '请输入需求描述' });
|
|
@@ -120,6 +120,7 @@ router.post('/am/ai/form-design', async function auth(req, res, next) {
|
|
|
120
120
|
- 示例: "\${RMB(assessed_value)}" —— 人民币大写(推荐)
|
|
121
121
|
- 示例: "\${TEXT(amount, \"rmb\")}" —— 等同于 RMB(),两者都可用,不要用 TEXT(v, "[DBNum2]...") 的 Excel 格式
|
|
122
122
|
- 注意:公式内字段名直接写,不加 \${} 嵌套,正确:"\${RMB(amount)}",错误:"\${RMB(\${amount})}"
|
|
123
|
+
- ⚠️ 子表字段引用规则:在主表公式中对子表(table)的子字段使用聚合函数时,直接使用子字段名,不需要加子表名前缀。系统会自动从子表数据中提取该列进行聚合。正确:"\${SUM(amount)}"(amount 是子表的子字段名),错误:"\${SUM(items.amount)}"(不要用 子表名.子字段名 的写法)。示例:子表 "items" 有子字段 "unit_price" 和 "quantity",主表求合计公式写 "\${SUM(unit_price)}",不要写 "\${SUM(items.unit_price)}"。
|
|
123
124
|
|
|
124
125
|
- section 类型(分组标题)的额外字段:
|
|
125
126
|
- colorScheme: 字符串,预设配色方案名称。可选值:"indigo"(靛蓝), "emerald"(翠绿), "amber"(琥珀), "rose"(玫红), "sky"(天蓝), "violet"(紫罗兰), "slate"(石板灰), "orange"(橙色), "teal"(青色)。设置后分组标题会显示对应的主题色
|
|
@@ -401,9 +402,29 @@ if (field.name === 'quantity' || field.name === 'unit_price') {
|
|
|
401
402
|
"onValueChange": "脚本代码或空字符串",
|
|
402
403
|
"onSubmit": "脚本代码或空字符串"
|
|
403
404
|
},
|
|
405
|
+
"nameFormula": "标题公式(仅升级场景,将旧版标题公式中的字段引用更新为新版字段 name)",
|
|
404
406
|
"migrationLog": ["迁移记录,仅升级场景需要"]
|
|
405
407
|
}
|
|
406
408
|
|
|
409
|
+
### nameFormula(标题公式,升级场景)
|
|
410
|
+
当用户提供了旧版标题公式(name_forumla)时,需要将其中的字段引用更新为新版字段 name。
|
|
411
|
+
- 旧版标题公式格式:用 {字段名} 引用字段值,如 "{申请人} 的 {类型} 申请"
|
|
412
|
+
- 升级时**只替换 {旧字段名} → {新字段name}**,如 "{applicant_name} 的 {request_type} 申请"
|
|
413
|
+
- ⚠️ **公式中花括号外的所有字符必须原样保留**,包括但不限于:双引号 "、单引号 '、中文引号""''、括号、标点符号、空格等。只有 {} 内的字段名需要替换,其他一切文本保持不变
|
|
414
|
+
- 示例:旧版 \`"项目" + {项目名称}\` → 新版 \`"项目" + {project_name}\`(注意引号 " 和加号 + 必须保留)
|
|
415
|
+
- 示例:旧版 \`{申请人}"提交的"{类型}"申请"\` → 新版 \`{applicant_name}"提交的"{request_type}"申请"\`(注意引号必须保留)
|
|
416
|
+
- 示例:旧版 \`{项目名称}({申请日期})\` → 新版 \`{project_name}({apply_date})\`(注意中文括号必须保留)
|
|
417
|
+
- 如果用户未提供旧版标题公式,则 nameFormula 字段可省略或返回空字符串
|
|
418
|
+
|
|
419
|
+
#### name_forumla 运行时格式规范
|
|
420
|
+
nameFormula 是一个 JS 表达式,运行时被包装为 \`return <nameFormula>\`,其中 \`{fieldCode}\` 会被替换为 \`(values.fieldCode || '')\`,\`{applicant.xxx}\` 会被替换为 \`(applicant.xxx || '')\`。
|
|
421
|
+
**规则:所有非 {xxx} 的文本(包括中文、符号、空格)必须用引号包裹,多段用 + 拼接。**
|
|
422
|
+
- 正确:\`"合同名称" + {project_name}\`
|
|
423
|
+
- 正确:\`{applicant_name} + "-" + {project_name}\`
|
|
424
|
+
- 错误:\`合同名称{project_name}\` — JS 语法错误
|
|
425
|
+
- 错误:\`合同名称 + {project_name}\` — JS 语法错误
|
|
426
|
+
- 错误:\`{project_name} 审批单\` — JS 语法错误
|
|
427
|
+
|
|
407
428
|
### migrationLog(迁移记录)
|
|
408
429
|
当用户提供了 flow_events、form_script、instance_template 中包含业务逻辑(如校验、赋值、显隐控制等脚本代码)时,必须在 migrationLog 数组中为每条被迁移的逻辑记录一条说明,格式:
|
|
409
430
|
"<来源>: <原逻辑简述> → <迁移到的具体位置>"
|
|
@@ -442,6 +463,9 @@ if (field.name === 'quantity' || field.name === 'unit_price') {
|
|
|
442
463
|
if (flow_events) {
|
|
443
464
|
extraSections += `\n\n## 流程事件 (flow_events)\n\`\`\`javascript\n${flow_events}\n\`\`\``;
|
|
444
465
|
}
|
|
466
|
+
if (name_forumla) {
|
|
467
|
+
extraSections += `\n\n## 旧版标题公式 (name_forumla)\n${name_forumla}\n\n说明:标题公式用 {字段名} 引用字段值,升级时需将其中的 {旧字段名} 替换为 {新版字段name},输出到 nameFormula 字段。**只替换 {} 花括号内的字段名,花括号外的所有文本(包括引号 "、单引号 '、中文引号""''、括号、其他标点符号和空格等)必须原样保留,禁止增删改。**`;
|
|
468
|
+
}
|
|
445
469
|
|
|
446
470
|
const userContent = `## 当前表单字段
|
|
447
471
|
${fieldsJson}
|
|
@@ -90,7 +90,7 @@ router.post('/am/ai/form-design-stream', async function auth(req, res, next) {
|
|
|
90
90
|
extraSections += `\n\n## 流程事件 (flow_events)\n\`\`\`javascript\n${flow_events}\n\`\`\``;
|
|
91
91
|
}
|
|
92
92
|
if (name_forumla) {
|
|
93
|
-
extraSections += `\n\n## 旧版标题公式 (name_forumla)\n${name_forumla}\n\n说明:标题公式用 {字段名} 引用字段值,升级时需将其中的 {旧字段名} 替换为 {新版字段name},输出到 nameFormula
|
|
93
|
+
extraSections += `\n\n## 旧版标题公式 (name_forumla)\n${name_forumla}\n\n说明:标题公式用 {字段名} 引用字段值,升级时需将其中的 {旧字段名} 替换为 {新版字段name},输出到 nameFormula 字段。**只替换 {} 花括号内的字段名,花括号外的所有文本(包括引号 \"、单引号 '、中文引号""''、括号、其他标点符号和空格等)必须原样保留,禁止增删改。**`;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const userContent = `## 当前表单字段\n${fieldsJson}\n\n## 当前事件脚本\n${eventsJson}${extraSections}\n\n## 用户需求\n${userRequest}\n\n请返回修改后的完整 JSON 对象(包含 tableColumns、fields 和 events)。`;
|
|
@@ -476,6 +476,7 @@ function buildSystemPrompt() {
|
|
|
476
476
|
- 示例: "\${RMB(assessed_value)}" —— 人民币大写(推荐)
|
|
477
477
|
- 示例: "\${TEXT(amount, \\"rmb\\")}" —— 等同于 RMB(),两者都可用,不要用 TEXT(v, "[DBNum2]...") 的 Excel 格式
|
|
478
478
|
- 注意:公式内字段名直接写,不加 \${} 嵌套,正确:"\${RMB(amount)}",错误:"\${RMB(\${amount})}"
|
|
479
|
+
- ⚠️ 子表字段引用规则:在主表公式中对子表(table)的子字段使用聚合函数时,直接使用子字段名,不需要加子表名前缀。系统会自动从子表数据中提取该列进行聚合。正确:"\${SUM(amount)}"(amount 是子表的子字段名),错误:"\${SUM(items.amount)}"(不要用 子表名.子字段名 的写法)。示例:子表 "items" 有子字段 "unit_price" 和 "quantity",主表求合计公式写 "\${SUM(unit_price)}",不要写 "\${SUM(items.unit_price)}"。
|
|
479
480
|
|
|
480
481
|
- section 类型(分组标题)的额外字段:
|
|
481
482
|
- colorScheme: 字符串,预设配色方案名称。可选值:"indigo"(靛蓝), "emerald"(翠绿), "amber"(琥珀), "rose"(玫红), "sky"(天蓝), "violet"(紫罗兰), "slate"(石板灰), "orange"(橙色), "teal"(青色)。设置后分组标题会显示对应的主题色
|
|
@@ -779,10 +780,22 @@ if (field.name === 'quantity' || field.name === 'unit_price') {
|
|
|
779
780
|
### nameFormula(标题公式,升级场景)
|
|
780
781
|
当用户提供了旧版标题公式(name_forumla)时,需要将其中的字段引用更新为新版字段 name。
|
|
781
782
|
- 旧版标题公式格式:用 {字段名} 引用字段值,如 "{申请人} 的 {类型} 申请"
|
|
782
|
-
-
|
|
783
|
-
-
|
|
783
|
+
- 升级时**只替换 {旧字段名} → {新字段name}**,如 "{applicant_name} 的 {request_type} 申请"
|
|
784
|
+
- ⚠️ **公式中花括号外的所有字符必须原样保留**,包括但不限于:双引号 "、单引号 '、中文引号""''、括号、标点符号、空格等。只有 {} 内的字段名需要替换,其他一切文本保持不变
|
|
785
|
+
- 示例:旧版 \`"项目" + {项目名称}\` → 新版 \`"项目" + {project_name}\`(注意引号 " 和加号 + 必须保留)
|
|
786
|
+
- 示例:旧版 \`{申请人}"提交的"{类型}"申请"\` → 新版 \`{applicant_name}"提交的"{request_type}"申请"\`(注意引号必须保留)
|
|
787
|
+
- 示例:旧版 \`{项目名称}({申请日期})\` → 新版 \`{project_name}({apply_date})\`(注意中文括号必须保留)
|
|
784
788
|
- 如果用户未提供旧版标题公式,则 nameFormula 字段可省略或返回空字符串
|
|
785
789
|
|
|
790
|
+
#### name_forumla 运行时格式规范
|
|
791
|
+
nameFormula 是一个 JS 表达式,运行时被包装为 \`return <nameFormula>\`,其中 \`{fieldCode}\` 会被替换为 \`(values.fieldCode || '')\`,\`{applicant.xxx}\` 会被替换为 \`(applicant.xxx || '')\`。
|
|
792
|
+
**规则:所有非 {xxx} 的文本(包括中文、符号、空格)必须用引号包裹,多段用 + 拼接。**
|
|
793
|
+
- 正确:\`"合同名称" + {project_name}\`
|
|
794
|
+
- 正确:\`{applicant_name} + "-" + {project_name}\`
|
|
795
|
+
- 错误:\`合同名称{project_name}\` — JS 语法错误
|
|
796
|
+
- 错误:\`合同名称 + {project_name}\` — JS 语法错误
|
|
797
|
+
- 错误:\`{project_name} 审批单\` — JS 语法错误
|
|
798
|
+
|
|
786
799
|
### migrationLog(迁移记录)
|
|
787
800
|
当用户提供了 flow_events、form_script、instance_template 中包含业务逻辑(如校验、赋值、显隐控制等脚本代码)时,必须在 migrationLog 数组中为每条被迁移的逻辑记录一条说明,格式:
|
|
788
801
|
"<来源>: <原逻辑简述> → <迁移到的具体位置>"
|
|
@@ -310,7 +310,7 @@ const calcSteps = async function(instance, flow, flowVersionId, formFields, valu
|
|
|
310
310
|
|
|
311
311
|
_steps.push(step);
|
|
312
312
|
try {
|
|
313
|
-
var nextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values, null,
|
|
313
|
+
var nextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values, null, true, instance.submitter);
|
|
314
314
|
const recursiveResults = await Promise.all(nextSteps.map(async (nextStep) => {
|
|
315
315
|
try {
|
|
316
316
|
if (!_.includes(track, nextStep._id)) {
|
|
@@ -628,7 +628,7 @@
|
|
|
628
628
|
for (const sField of field.fields) {
|
|
629
629
|
if (sField.type != "hidden") {
|
|
630
630
|
sField.permission = field.permission
|
|
631
|
-
const column = await getFieldEditTpl(sField,
|
|
631
|
+
const column = await getFieldEditTpl(sField, null);
|
|
632
632
|
tpl.columns.push(column);
|
|
633
633
|
}
|
|
634
634
|
}
|
|
@@ -641,7 +641,7 @@
|
|
|
641
641
|
tpl.body = []
|
|
642
642
|
if (field.fields) {
|
|
643
643
|
for (let i = 0; i < field.fields.length; i++) {
|
|
644
|
-
let fieldsItem = await getFieldEditTpl(field.fields[i],
|
|
644
|
+
let fieldsItem = await getFieldEditTpl(field.fields[i], tableFieldMap)
|
|
645
645
|
tpl.body.push(fieldsItem)
|
|
646
646
|
}
|
|
647
647
|
}
|
|
@@ -205,9 +205,12 @@ reports/formula-scan/
|
|
|
205
205
|
| E3 | ERROR | 转换结果中含裸 `{`(非合法 `${...}` 表达式) |
|
|
206
206
|
| E4 | ERROR | mapFormula 抛出异常 |
|
|
207
207
|
| E5 | ERROR | 聚合函数引用子表字段但转换结果不含对应 ARRAYMAP(子表识别失败) |
|
|
208
|
+
| E6 | ERROR | section 嵌套公式字段设计器兼容性:引擎转换结果与设计器传参不一致 |
|
|
208
209
|
| W1 | WARN | 公式含除法且除数是字段引用(除零风险) |
|
|
209
210
|
| W2 | WARN | 同表单内不同字段 getSafeCode 后结果相同(字段名冲突) |
|
|
210
211
|
|
|
212
|
+
> **E6 说明**:检测 `flow_form_design.ejs` 的 section 子字段是否正确传递 `tableFieldMap`。模拟设计器行为(传 `true`)并与正确结果对比,不一致则报 ERROR。详见 SCAN_HISTORY v9→v10。
|
|
213
|
+
|
|
211
214
|
---
|
|
212
215
|
|
|
213
216
|
## 报告结构说明
|
|
@@ -197,3 +197,60 @@ v8 与 v7 对比完全一致(0 ERROR),说明:
|
|
|
197
197
|
| KNOWN_ISSUE | 4 | 4 |
|
|
198
198
|
| WARN | 206 | 206 |
|
|
199
199
|
| PASS | 1949 | 1949 |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## v9 → v10:Bug 9 — 设计器 section 子字段未传递 tableFieldMap
|
|
204
|
+
|
|
205
|
+
- **扫描日期**: 2026-03-27
|
|
206
|
+
- **修正前报告**: `scan-result-v9-20260327.json`(6 ERROR, 4 KNOWN_ISSUE, 206 WARN)
|
|
207
|
+
- **修正后报告**: `scan-result-v10-20260327.json`(0 ERROR, 4 KNOWN_ISSUE, 206 WARN)
|
|
208
|
+
- **关联 Issue**: [#538](https://github.com/steedos/steedos-plugins/issues/538)
|
|
209
|
+
- **修改文件**: `flow_form_design.ejs`
|
|
210
|
+
|
|
211
|
+
### Bug 9 根因
|
|
212
|
+
|
|
213
|
+
`flow_form_design.ejs` 的 `getFieldEditTpl` 函数中:
|
|
214
|
+
- **section case(第 644 行)**:调用 `getFieldEditTpl(field.fields[i], true)` — 传递布尔值 `true` 而非 `tableFieldMap`
|
|
215
|
+
- **table case(第 631 行)**:调用 `getFieldEditTpl(sField, true)` — 同样传递 `true`
|
|
216
|
+
|
|
217
|
+
在 `_mapFormula` 内部,`tableFieldMap` 为 `true` 时,`true[fieldCode]` 永远返回 `undefined`,导致子表字段识别失败,走了非子表路径(`getSafeCode` 而非 `ARRAYMAP`)。
|
|
218
|
+
|
|
219
|
+
> **为什么之前的扫描器没发现**:扫描器自己通过 `getTableFieldMap(fields)` 构建正确的 `tableFieldMap` 传给 `mapFormula`,测试的是公式引擎本身。但设计器的调用链在 section 递归时传参错误,属于**调用者 bug**而非引擎 bug。
|
|
220
|
+
|
|
221
|
+
### 修复内容
|
|
222
|
+
|
|
223
|
+
- section case:`getFieldEditTpl(field.fields[i], true)` → `getFieldEditTpl(field.fields[i], tableFieldMap)`
|
|
224
|
+
- table case:`getFieldEditTpl(sField, true)` → `getFieldEditTpl(sField, null)`(子表内字段不需要 tableFieldMap)
|
|
225
|
+
|
|
226
|
+
### 检测方法(E6 规则,一次性诊断)
|
|
227
|
+
|
|
228
|
+
为发现此类问题,临时添加 E6 规则到扫描器:
|
|
229
|
+
- 追踪字段是否在 section 内(`inSection` 标志)
|
|
230
|
+
- 对 section 内的公式字段,模拟设计器行为调用 `mapFormula(formula, true)`
|
|
231
|
+
- 比较与正确结果是否一致,不一致则报 ERROR
|
|
232
|
+
|
|
233
|
+
E6 是一次性诊断规则 — 发现问题后修复设计器,然后移除 E6(因为它测试的是调用者行为而非引擎行为)。但 `inSection` 追踪保留在 `collectFormulaFields` 中,供未来诊断使用。
|
|
234
|
+
|
|
235
|
+
### 修复了 6 个 E6 ERROR
|
|
236
|
+
|
|
237
|
+
| # | 表单名称 | formId | 字段 | 原始公式 | 版本 |
|
|
238
|
+
|---|---------|--------|------|---------|------|
|
|
239
|
+
| 1 | 【公司本部】抚顺石化公司车辆维修审批单 | `9a40a1a2049014ff074a4961` | 配件金额 | `{数量}*{单价}` | history |
|
|
240
|
+
| 2 | 【乙烯部】抚顺石化公司车辆维修审批单 | `2a36450a56ecd64af98506ba` | 配件金额 | `{数量}*{单价}` | history |
|
|
241
|
+
| 3 | 【公司办公室】抚顺石化公司车辆维修费用结算审批单 | `2c3a2c7cbbf618d95a40376b` | 配件金额 | `{数量}*{单价}` | history |
|
|
242
|
+
| 4 | 抚顺石化公司物资采购概预算调整审批单 | `cRJknoGMcLa2raizW` | 拟调整概预算金额合计 | `sum({拟调整概预算金额})` | **current** |
|
|
243
|
+
| 5 | 抚顺石化公司物资采购概预算调整审批单 | `cRJknoGMcLa2raizW` | 拟调整概预算总金额 | `sum({拟调整概预算金额})` | history |
|
|
244
|
+
| 6 | 抚顺石化公司化工三剂采购概预算调整审批单 | `17d3b669d8880cd32b89ebfb` | 拟调整概预算金额合计 | `sum({拟调整概预算金额(元)})` | **current** |
|
|
245
|
+
|
|
246
|
+
> **影响面**: 4 个 history + 2 个 **current**(#4 和 #6 影响在用表单的设计器预览)
|
|
247
|
+
|
|
248
|
+
### 扫描统计变化
|
|
249
|
+
|
|
250
|
+
| 指标 | v8 | v9(修正前) | v10(修正后) |
|
|
251
|
+
|------|-----|------------|-------------|
|
|
252
|
+
| ERROR | 0 | 6 | 0 |
|
|
253
|
+
| KNOWN_ISSUE | 4 | 4 | 4 |
|
|
254
|
+
| WARN | 206 | 206 | 206 |
|
|
255
|
+
| PASS | 1949 | 1943 | 1949 |
|
|
256
|
+
| current ERROR | 0 | 2 | 0 |
|