@steedos-labs/plugin-workflow 3.0.46 → 3.0.47
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.css +1 -1
- package/designer/dist/amis-renderer/amis-renderer.js +1 -1
- package/designer/dist/assets/index-CH5Rzd1r.css +1 -0
- package/designer/dist/assets/index-Z3wTJs2h.js +952 -0
- package/designer/dist/index.html +2 -2
- package/main/default/manager/import.js +20 -2
- package/main/default/objects/instances/buttons/instance_delete.button.yml +1 -1
- package/main/default/objects/instances/buttons/instance_related.button.yml +4 -1
- package/main/default/routes/api_workflow_ai_form_design.router.js +5 -2
- package/main/default/routes/api_workflow_ai_form_design_stream.router.js +5 -2
- package/main/default/routes/flow_form_design.ejs +8 -0
- package/main/default/routes/flow_form_design.router.js +1 -1
- package/main/default/test/FORMULA_SCAN_GUIDE.md +258 -0
- package/main/default/test/prompt-formula-analyze.md +59 -0
- package/main/default/test/prompt-formula-fix.md +75 -0
- package/main/default/test/reports/formula-scan/.gitkeep +0 -0
- package/main/default/test/reports/formula-scan/SCAN_HISTORY.md +165 -0
- package/main/default/test/reports/formula-scan/scan-result-v6-20260324.json +31846 -0
- package/main/default/test/reports/formula-scan/scan-result-v7-20260324.json +31543 -0
- package/main/default/test/run_approval_comments_upgrade.js +135 -0
- package/main/default/test/scan_production_formulas.js +573 -0
- package/main/default/test/test_formula_compat.js +38 -0
- package/main/default/utils/designerManager.js +1 -0
- package/main/default/utils/formula-compat.js +8 -0
- package/package.json +1 -1
- package/public/amis-renderer/amis-renderer.css +1 -1
- package/public/amis-renderer/amis-renderer.js +1 -1
- package/public/workflow/index.css +4 -0
- package/designer/dist/assets/index-BNKlgpz1.js +0 -943
- package/designer/dist/assets/index-D7JdeS9f.css +0 -1
package/designer/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="shortcut icon" type="image/svg+xml" href="/images/logo.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>designer</title>
|
|
8
|
-
<script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/api/workflow/designer-v2/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-Z3wTJs2h.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/api/workflow/designer-v2/assets/index-CH5Rzd1r.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -94,7 +94,7 @@ async function upgradeForm(formId, form, currentUserId, spaceId) {
|
|
|
94
94
|
pass = recordsCount > 0;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
if (pass === true
|
|
97
|
+
if (pass === true) {
|
|
98
98
|
formUpdateObj.$push = { 'historys': ff["current"] };
|
|
99
99
|
current._id = _makeNewID();
|
|
100
100
|
current._rev = ff["current"]["_rev"] + 1;
|
|
@@ -124,6 +124,22 @@ async function upgradeForm(formId, form, currentUserId, spaceId) {
|
|
|
124
124
|
current.form_script = form["current"]["form_script"];
|
|
125
125
|
current.name_forumla = form["current"]["name_forumla"];
|
|
126
126
|
|
|
127
|
+
current.style = form["current"]["style"];
|
|
128
|
+
current.mode = form["current"]["mode"];
|
|
129
|
+
current.wizard_mode = form["current"]["wizard_mode"];
|
|
130
|
+
current.amis_schema = form["current"]["amis_schema"]
|
|
131
|
+
|
|
132
|
+
current.version = form["current"]["version"];
|
|
133
|
+
current.viewMode = form["current"]["viewMode"];
|
|
134
|
+
current.tableColumns = form["current"]["tableColumns"];
|
|
135
|
+
|
|
136
|
+
current.events = form["current"]["events"];
|
|
137
|
+
|
|
138
|
+
current.formTitle = form["current"]["formTitle"];
|
|
139
|
+
current.tableTitleColor = form["current"]["tableTitleColor"];
|
|
140
|
+
current.tableBorderColor = form["current"]["tableBorderColor"];
|
|
141
|
+
current.tableShowOuterBorder = form["current"]["tableShowOuterBorder"];
|
|
142
|
+
|
|
127
143
|
formUpdateObj.$set = {
|
|
128
144
|
'current': current,
|
|
129
145
|
'name': form["name"],
|
|
@@ -232,6 +248,7 @@ async function upgradeFlow(flowCome, userId, flowId) {
|
|
|
232
248
|
updateObj.$set.modified = now;
|
|
233
249
|
updateObj.$set.modified_by = userId;
|
|
234
250
|
updateObj.$set.events = flowCome['events'] || '';
|
|
251
|
+
updateObj.$set.upgraded = flowCome.upgraded || false;
|
|
235
252
|
|
|
236
253
|
const form = await formCollection.findOne({ _id: flow.form }, { projection: { category: 1 } });
|
|
237
254
|
updateObj.$set.category = form['category'];
|
|
@@ -409,9 +426,10 @@ async function workflow(uid, spaceId, form, enabled, company_id, options = {}) {
|
|
|
409
426
|
form.modified_by = uid;
|
|
410
427
|
form.historys = [];
|
|
411
428
|
|
|
412
|
-
const fields = addFieldName(form.current?.fields || []);
|
|
429
|
+
const fields = form.current.version === 'v2' ? form.current?.fields || [] : addFieldName(form.current?.fields || []);
|
|
413
430
|
|
|
414
431
|
form.current = {
|
|
432
|
+
...form.current,
|
|
415
433
|
_id: _makeNewID(),
|
|
416
434
|
_rev: 1,
|
|
417
435
|
form: form_id,
|
|
@@ -35,6 +35,9 @@ amis_schema: |-
|
|
|
35
35
|
"rowClassNameExpr": "<%= data.__selected === true ? 'hidden' : '' %>",
|
|
36
36
|
"perPage": 20
|
|
37
37
|
},
|
|
38
|
+
"searchable_default": {
|
|
39
|
+
"submit_date": "${[STARTOF(DATEMODIFY(NOW(), -180, 'day'), 'day'),ENDOF(NOW(), 'day')]}"
|
|
40
|
+
},
|
|
38
41
|
"amis": {
|
|
39
42
|
"id": "u:f0273e374d19",
|
|
40
43
|
"embed": true,
|
|
@@ -47,7 +50,7 @@ amis_schema: |-
|
|
|
47
50
|
"source": {
|
|
48
51
|
"method": "post",
|
|
49
52
|
"url": "${context.rootUrl}/graphql",
|
|
50
|
-
"requestAdaptor": "var
|
|
53
|
+
"requestAdaptor": "var searchableDefaultConfig = {\"submit_date\": \"${[STARTOF(DATEMODIFY(NOW(), -180, 'day'), 'day'),ENDOF(NOW(), 'day')]}\"};\nvar filterFormValues = JSON.parse(JSON.stringify(searchableDefaultConfig));\nif (_.isObject(filterFormValues)) {\n _.each(filterFormValues, function(v, k) {\n var isAmisFormulaValue = typeof v === \"string\" && v.indexOf(\"${\") > -1;\n if (isAmisFormulaValue) {\n filterFormValues[k] = AmisCore.evaluate(v, context);\n }\n });\n var fields = api.data.$self.uiSchema && api.data.$self.uiSchema.fields;\n filterFormValues = SteedosUI.getSearchFilterFormValues(filterFormValues, fields);\n}\nvar selfData = JSON.parse(JSON.stringify(api.data.$self));\nvar searchableFilter = SteedosUI.getSearchFilter(Object.assign({}, filterFormValues, selfData)) || []; const { pageNo, pageSize, keywords = '' } = api.data;\nvar keywordsFilters = SteedosUI.getKeywordsSearchFilter(api.data.$self.__keywords_lookup__related_instances__to__instances, [\"name\", \"flow_name\",\"submitter\",\"submit_date\"]);\nif (keywordsFilters && keywordsFilters.length > 0) {\n searchableFilter.push(keywordsFilters);\n}\nconsole.log(\"===\", JSON.stringify(api.data));\napi.data = {\n query: `\n query{\n rows: instances__getRelatedInstances(keywords: \"${keywords}\", top: ${pageSize || 20}, skip: ${(pageNo - 1) * pageSize}, filters: ${JSON.stringify(searchableFilter)}){\n _id,\n name,\n flow_name,\n submit_date,\n submitter\n _display:_ui{\n submit_date,\n submitter\n }\n },\n count: instances__getRelatedInstances__count(filters: ${JSON.stringify(searchableFilter)}, keywords: \"${keywords}\")\n }\n `\n};\nreturn api;",
|
|
51
54
|
"headers": {
|
|
52
55
|
"Authorization": "Bearer ${context.tenantId},${context.authToken}"
|
|
53
56
|
},
|
|
@@ -88,7 +88,7 @@ router.post('/am/ai/form-design', async function auth(req, res, next) {
|
|
|
88
88
|
- select/multiSelect/radio 类型:
|
|
89
89
|
- options: 数组,选项列表,格式 [{ "label": "显示文本", "value": "值" }]
|
|
90
90
|
|
|
91
|
-
- number
|
|
91
|
+
- number 类型(纯手工输入的数字字段,**不含公式**;如果字段有公式/自动计算,必须用 formula 类型):
|
|
92
92
|
- min: 数字,最小值
|
|
93
93
|
- max: 数字,最大值
|
|
94
94
|
- precision: 数字,小数位数
|
|
@@ -98,8 +98,10 @@ router.post('/am/ai/form-design', async function auth(req, res, next) {
|
|
|
98
98
|
- maxLength: 数字,最大字符数
|
|
99
99
|
- minLength: 数字,最小字符数
|
|
100
100
|
|
|
101
|
-
- formula
|
|
101
|
+
- formula 类型(凡是有公式/自动计算的字段,都必须用此类型,禁止用 number+formula 组合):
|
|
102
102
|
- formula: 字符串,公式表达式。语法:用 \${} 包裹表达式,内部直接使用字段名(不需要额外括号)。没有 \${} 的部分作为纯文本。
|
|
103
|
+
- valueType: 字符串,公式计算结果的值类型。可选值:"number"(数值,默认)、"string"(字符串,如 CONCAT/RMB/TEXT 等返回文本的公式)、"date"(日期)、"dateTime"(日期时间)、"boolean"(布尔)。根据公式计算结果的实际数据类型设置。示例:算术运算/SUM/ROUND 等→ "number";CONCAT/RMB/TEXT 等→ "string";DATE/TODAY 等→ "date"
|
|
104
|
+
- precision: 数字,小数位数(仅当 valueType 为 "number" 时有效,如金额合计字段设 precision: 2)
|
|
103
105
|
- ⚠️ 严格限制:只能使用下方列出的函数,禁止使用任何 Excel 函数、JavaScript 函数或其他未列出的函数(如 NETWORKDAYS、WORKDAY、EDATE、SUMIF、VLOOKUP、parseFloat、Math.floor 等一律不可用)。如果某个功能无法用已有函数实现,用 IF/AND/OR 的组合或算术运算代替,不要臆造函数名。
|
|
104
106
|
- 支持的函数(完整列表,共 36 个,仅此列表内的函数可用):
|
|
105
107
|
- 聚合:SUM(...) 求和,AVG/AVERAGE(...) 平均,MAX(...) 最大,MIN(...) 最小,COUNT(...) 计数(空/0不计)
|
|
@@ -248,6 +250,7 @@ if (field.name === 'quantity' || field.name === 'unit_price') {
|
|
|
248
250
|
9. **grid 类型字段必须同时返回 gridRows、gridCols、gridData、children 四个属性**,缺少任何一个都会导致网格渲染为空白。gridData 中 cellType 为 "input" 的单元格必须有 fieldId,且该 fieldId 需在 children 数组中有对应的子字段
|
|
249
251
|
10. 修改已有 grid 字段时,必须保留其完整的 gridData 和 children 数据,不要丢弃未被修改的子字段。如果用户未要求改动 grid 内容,应原样返回 grid 的所有属性
|
|
250
252
|
11. 每个字段的类型特有属性必须完整返回:number 类型的 min/max/precision/thousandSeparator、text/textarea 的 maxLength/minLength、lookup 的 reference_to/lookupLabelField/lookupFillRules、member/org 的 pickerFillRules、reference 的 reference_to/displayFields 等。不要遗漏这些属性
|
|
253
|
+
12. ⚠️ **type 与 formula 属性的严格对应**:只要字段包含公式表达式(formula 属性),其 type 就**必须**是 "formula",绝对不能是 "number"、"text" 或其他类型。"number" 类型仅用于用户手工输入数字的字段(无公式)。错误示例:{ type: "number", formula: "${a + b}" },正确示例:{ type: "formula", formula: "${a + b}", precision: 2 }。grid/table 的 children 子字段同样适用此规则
|
|
251
254
|
|
|
252
255
|
## 额外待转换的数据(升级场景)
|
|
253
256
|
|
|
@@ -444,7 +444,7 @@ function buildSystemPrompt() {
|
|
|
444
444
|
- select/multiSelect/radio 类型:
|
|
445
445
|
- options: 数组,选项列表,格式 [{ "label": "显示文本", "value": "值" }]
|
|
446
446
|
|
|
447
|
-
- number
|
|
447
|
+
- number 类型(纯手工输入的数字字段,**不含公式**;如果字段有公式/自动计算,必须用 formula 类型):
|
|
448
448
|
- min: 数字,最小值
|
|
449
449
|
- max: 数字,最大值
|
|
450
450
|
- precision: 数字,小数位数
|
|
@@ -454,8 +454,10 @@ function buildSystemPrompt() {
|
|
|
454
454
|
- maxLength: 数字,最大字符数
|
|
455
455
|
- minLength: 数字,最小字符数
|
|
456
456
|
|
|
457
|
-
- formula
|
|
457
|
+
- formula 类型(凡是有公式/自动计算的字段,都必须用此类型,禁止用 number+formula 组合):
|
|
458
458
|
- formula: 字符串,公式表达式。语法:用 \${} 包裹表达式,内部直接使用字段名(不需要额外括号)。没有 \${} 的部分作为纯文本。
|
|
459
|
+
- valueType: 字符串,公式计算结果的值类型。可选值:"number"(数值,默认)、"string"(字符串,如 CONCAT/RMB/TEXT 等返回文本的公式)、"date"(日期)、"dateTime"(日期时间)、"boolean"(布尔)。根据公式计算结果的实际数据类型设置。示例:算术运算/SUM/ROUND 等→ "number";CONCAT/RMB/TEXT 等→ "string";DATE/TODAY 等→ "date"
|
|
460
|
+
- precision: 数字,小数位数(仅当 valueType 为 "number" 时有效,如金额合计字段设 precision: 2)
|
|
459
461
|
- ⚠️ 严格限制:只能使用下方列出的函数,禁止使用任何 Excel 函数、JavaScript 函数或其他未列出的函数(如 NETWORKDAYS、WORKDAY、EDATE、SUMIF、VLOOKUP、parseFloat、Math.floor 等一律不可用)。如果某个功能无法用已有函数实现,用 IF/AND/OR 的组合或算术运算代替,不要臆造函数名。
|
|
460
462
|
- 支持的函数(完整列表,共 36 个,仅此列表内的函数可用):
|
|
461
463
|
- 聚合:SUM(...) 求和,AVG/AVERAGE(...) 平均,MAX(...) 最大,MIN(...) 最小,COUNT(...) 计数(空/0不计)
|
|
@@ -608,6 +610,7 @@ if (field.name === 'quantity' || field.name === 'unit_price') {
|
|
|
608
610
|
6. 如果用户只要求修改脚本/事件,则 fields 保持原样不变
|
|
609
611
|
7. 如果用户只要求修改字段,则 events 保持原样不变
|
|
610
612
|
8. 返回完整的 fields 数组和 events 对象
|
|
613
|
+
9. ⚠️ **type 与 formula 属性的严格对应**:只要字段包含公式表达式(formula 属性),其 type 就**必须**是 "formula",绝对不能是 "number"、"text" 或其他类型。"number" 类型仅用于用户手工输入数字的字段(无公式)。错误示例:{ type: "number", formula: "\${a + b}" },正确示例:{ type: "formula", formula: "\${a + b}", valueType: "number", precision: 2 }。grid/table 的 children 子字段同样适用此规则
|
|
611
614
|
|
|
612
615
|
## 额外待转换的数据(升级场景)
|
|
613
616
|
|
|
@@ -296,6 +296,14 @@
|
|
|
296
296
|
if (formula.trim() === '{now}') return '${NOW()}';
|
|
297
297
|
|
|
298
298
|
let newFormula = formula;
|
|
299
|
+
|
|
300
|
+
// 预处理:修正生产数据中的非标准聚合函数语法
|
|
301
|
+
// B: 全角括号 sum({x}) → sum({x})
|
|
302
|
+
newFormula = newFormula.replace(/(sum|average|count|max|min|numToRMB)\s*(/ig, '$1(');
|
|
303
|
+
newFormula = newFormula.replace(/\})/g, '})');
|
|
304
|
+
// C: 缺失括号 sum{x} → sum({x})
|
|
305
|
+
newFormula = newFormula.replace(/(sum|average|count|max|min|numToRMB)\{([^{}]*)\}/ig, '$1({$2})');
|
|
306
|
+
|
|
299
307
|
const isFunction = newFormula.match(/(sum|average|count|max|min|numToRMB)\s*\(/i);
|
|
300
308
|
const hasFieldRef = newFormula.match(/\{[^{}]+\}/);
|
|
301
309
|
const isOperator = newFormula.match(/[\+\-\*\/]/) && hasFieldRef && newFormula.indexOf("}.") < 0;
|
|
@@ -39,7 +39,7 @@ router.get('/api/workflow/form_design', auth.requireAuthentication, async functi
|
|
|
39
39
|
const data = {
|
|
40
40
|
builderHost,
|
|
41
41
|
assetUrls,
|
|
42
|
-
rootUrl:
|
|
42
|
+
rootUrl: '',
|
|
43
43
|
tenantId: userSession.spaceId,
|
|
44
44
|
userId: userSession.userId,
|
|
45
45
|
authToken: userSession.authToken,
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# 生产公式扫描操作指南
|
|
2
|
+
|
|
3
|
+
本指南介绍如何使用 `scan_production_formulas.js` 扫描生产数据库中的工作流表单公式,主动发现 `mapFormula` 未兼容的公式语法,并完成后续 AI 辅助评估与修复。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 前置条件
|
|
8
|
+
|
|
9
|
+
- 本地已安装 Node.js(>=14)
|
|
10
|
+
- 本地 MongoDB 可用(`mongod` 正在运行)
|
|
11
|
+
- 已将生产 `forms` 集合数据还原到本地数据库:
|
|
12
|
+
```bash
|
|
13
|
+
mongorestore --db <dbname> --collection forms <bson文件路径>
|
|
14
|
+
# 示例
|
|
15
|
+
mongorestore --db prod_backup_20260323 --collection forms ./dump/steedos/forms.bson
|
|
16
|
+
```
|
|
17
|
+
- 本地已安装 `mongodb` npm 包(若未安装):
|
|
18
|
+
```bash
|
|
19
|
+
cd steedos-packages/plugin-workflow
|
|
20
|
+
npm install mongodb --no-save
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 完整操作流程
|
|
26
|
+
|
|
27
|
+
### 步骤 0:执行审批意见字段升级(前置步骤)
|
|
28
|
+
|
|
29
|
+
备份还原的 forms 库中可能包含老版本的审批意见公式字段(如 `{yijianlan:{step:'部门经理审核',default:'已阅'}}`)。这些字段需要先执行升级转换为 `steedos-field` 类型,否则会被公式扫描误报为 E3 错误。
|
|
30
|
+
|
|
31
|
+
> **说明**:如果目标库来自已执行过"审批意见字段升级"的生产环境,可跳过此步骤。
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd steedos-packages/plugin-workflow
|
|
35
|
+
|
|
36
|
+
# 先 dry run 查看影响范围(不修改数据库)
|
|
37
|
+
FORMULA_SCAN_MONGO_URL=mongodb://127.0.0.1:27017/<dbname> \
|
|
38
|
+
DRY_RUN=true \
|
|
39
|
+
node main/default/test/run_approval_comments_upgrade.js
|
|
40
|
+
|
|
41
|
+
# 确认无误后正式执行
|
|
42
|
+
FORMULA_SCAN_MONGO_URL=mongodb://127.0.0.1:27017/<dbname> \
|
|
43
|
+
node main/default/test/run_approval_comments_upgrade.js
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
输出示例:
|
|
47
|
+
```
|
|
48
|
+
审批意见字段升级完成
|
|
49
|
+
扫描表单数: 1595
|
|
50
|
+
升级表单数: 29
|
|
51
|
+
升级字段数: 1157
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
升级脚本复用了 `src/rests/migrateApprovalCommentsField.js` 中的转换逻辑(与"审批意见字段升级控制台"UI 一致),会将 3 种审批意见公式字段转换为 `steedos-field` 类型并清除原有 formula 属性:
|
|
55
|
+
|
|
56
|
+
| 模式 | 示例 |
|
|
57
|
+
|------|------|
|
|
58
|
+
| `{yijianlan:{...}}` | `{yijianlan:{step:'部门经理审核',default:'已阅'}}` |
|
|
59
|
+
| `{signature.traces.步骤名}` | `{signature.traces.部门经理审核}` |
|
|
60
|
+
| `{traces.步骤名}` | `{traces.总经理审批}` |
|
|
61
|
+
|
|
62
|
+
### 步骤 1:运行扫描脚本
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cd steedos-packages/plugin-workflow
|
|
66
|
+
|
|
67
|
+
# 基本用法
|
|
68
|
+
FORMULA_SCAN_MONGO_URL=mongodb://127.0.0.1:27017/<dbname> \
|
|
69
|
+
node main/default/test/scan_production_formulas.js
|
|
70
|
+
|
|
71
|
+
# 带标识标签(推荐,便于区分多次扫描结果)
|
|
72
|
+
FORMULA_SCAN_MONGO_URL=mongodb://127.0.0.1:27017/<dbname> \
|
|
73
|
+
SCAN_LABEL=v1 \
|
|
74
|
+
node main/default/test/scan_production_formulas.js
|
|
75
|
+
|
|
76
|
+
# 也可使用 MONGO_URL 回退(若已设置)
|
|
77
|
+
MONGO_URL=mongodb://127.0.0.1:27017/<dbname> \
|
|
78
|
+
node main/default/test/scan_production_formulas.js
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
扫描完成后,控制台显示如下统计:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
扫描完成
|
|
85
|
+
总表单数: 1595
|
|
86
|
+
含公式字段的表单: 706
|
|
87
|
+
公式字段总数: 2157
|
|
88
|
+
- formula 字段: 2043
|
|
89
|
+
- default_value 公式字段: 114
|
|
90
|
+
- 来自 current 版本: 1655
|
|
91
|
+
- 来自 history 版本: 502
|
|
92
|
+
|
|
93
|
+
检测结果:
|
|
94
|
+
ERROR: 4 条(涉及 4 个表单,current: 0,history: 4)
|
|
95
|
+
WARN: 206 条(涉及 114 个表单)
|
|
96
|
+
PASS: 1949 条
|
|
97
|
+
|
|
98
|
+
报告已输出到: main/default/test/reports/formula-scan/scan-result-v1-20260324.json
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
报告文件保存在:
|
|
102
|
+
```
|
|
103
|
+
main/default/test/reports/formula-scan/scan-result-{SCAN_LABEL}-{YYYYMMDD}.json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### 步骤 2:AI 辅助评估(分类问题根因)
|
|
109
|
+
|
|
110
|
+
1. 打开生成的 JSON 报告,复制 `errors` 数组内容
|
|
111
|
+
2. 打开 `main/default/test/prompt-formula-analyze.md`,按其中说明将 errors 填入占位符
|
|
112
|
+
3. 将拼好的提示词发送给 AI(如 GitHub Copilot Chat / Claude / GPT-4o)
|
|
113
|
+
4. AI 将对 issues 按根因分组,给出修复策略和优先级
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### 步骤 3:AI 辅助修复
|
|
118
|
+
|
|
119
|
+
1. 根据 AI 评估结果,确认需要修复的分组
|
|
120
|
+
2. 打开 `main/default/test/prompt-formula-fix.md`,按其中说明将需修复的分组填入占位符
|
|
121
|
+
3. 将拼好的提示词发送给 AI
|
|
122
|
+
4. AI 将生成:先写测试 → 改代码 → 三处同步 → 运行测试 的完整修复方案
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### 步骤 4:执行修复并验证
|
|
127
|
+
|
|
128
|
+
按 AI 给出的方案完成修复后,运行两套测试验证:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# plugins 侧回归测试
|
|
132
|
+
cd steedos-packages/plugin-workflow
|
|
133
|
+
node main/default/test/test_formula_compat.js
|
|
134
|
+
|
|
135
|
+
# 重新扫描(使用新版本号),确认 ERROR 数量归零
|
|
136
|
+
FORMULA_SCAN_MONGO_URL=mongodb://127.0.0.1:27017/<dbname> \
|
|
137
|
+
SCAN_LABEL=v<N+1> \
|
|
138
|
+
node main/default/test/scan_production_formulas.js
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> widgets 侧单元测试需在 `steedos-widgets` 仓库执行:
|
|
142
|
+
> ```bash
|
|
143
|
+
> cd packages/@steedos-widgets/amis-lib
|
|
144
|
+
> npx jest --verbose
|
|
145
|
+
> ```
|
|
146
|
+
|
|
147
|
+
### 步骤 5:记录修正过程
|
|
148
|
+
|
|
149
|
+
每次修复完成后,需要留下完整的修正记录:
|
|
150
|
+
|
|
151
|
+
#### 5.1 给修正前的扫描报告添加 `processedResult`
|
|
152
|
+
|
|
153
|
+
在修正前的 JSON 报告(如 `scan-result-v6-*.json`)的每个 `errors` 条目中,添加 `processedResult` 字段:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"level": "ERROR",
|
|
158
|
+
"ruleId": "E1",
|
|
159
|
+
"formId": "...",
|
|
160
|
+
"originalFormula": "sum{金额}",
|
|
161
|
+
"processedResult": {
|
|
162
|
+
"action": "CODE_FIX",
|
|
163
|
+
"bugId": "Bug-8",
|
|
164
|
+
"bugTitle": "聚合函数缺失括号预处理",
|
|
165
|
+
"fixDescription": "sum{金额} 缺少括号,预处理补全为 sum({金额}) 后可正常转换",
|
|
166
|
+
"fixedInVersion": "v7",
|
|
167
|
+
"fixCommit": "3abea58",
|
|
168
|
+
"fixedFiles": ["formula-compat.js", "formula-utils.js"]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`processedResult.action` 取值:
|
|
174
|
+
- `CODE_FIX` — 通过修改公式转换代码解决
|
|
175
|
+
- `KNOWN_ISSUE` — 经分析确认不需要代码修正(附 `category` 和 `resolution`)
|
|
176
|
+
|
|
177
|
+
#### 5.2 更新 SCAN_HISTORY.md
|
|
178
|
+
|
|
179
|
+
在 `reports/formula-scan/SCAN_HISTORY.md` 中追加本次修正记录,包括:
|
|
180
|
+
- 修正前/后的报告文件名
|
|
181
|
+
- Bug ID 和修改的文件列表
|
|
182
|
+
- 修复的每个 ERROR 的表单名称、formId、字段、原始公式
|
|
183
|
+
- 归类为 knownIssues 的记录及原因
|
|
184
|
+
- 扫描统计对比表
|
|
185
|
+
|
|
186
|
+
#### 5.3 提交产物
|
|
187
|
+
|
|
188
|
+
每次扫描修正周期应提交到 git 的文件:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
reports/formula-scan/
|
|
192
|
+
scan-result-v<N>-YYYYMMDD.json # 修正前(errors 带 processedResult)
|
|
193
|
+
scan-result-v<N+1>-YYYYMMDD.json # 修正后(errors 为空)
|
|
194
|
+
SCAN_HISTORY.md # 追加修正记录
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 检测规则说明
|
|
200
|
+
|
|
201
|
+
| 规则 | 级别 | 触发条件 |
|
|
202
|
+
|------|------|----------|
|
|
203
|
+
| E1 | ERROR | mapFormula 返回 null 但公式含 `{字段名}` 引用 |
|
|
204
|
+
| E2 | ERROR | 转换结果中仍含 `[` 或 `]` |
|
|
205
|
+
| E3 | ERROR | 转换结果中含裸 `{`(非合法 `${...}` 表达式) |
|
|
206
|
+
| E4 | ERROR | mapFormula 抛出异常 |
|
|
207
|
+
| W1 | WARN | 公式含除法且除数是字段引用(除零风险) |
|
|
208
|
+
| W2 | WARN | 同表单内不同字段 getSafeCode 后结果相同(字段名冲突) |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 报告结构说明
|
|
213
|
+
|
|
214
|
+
JSON 报告包含以下顶层字段:
|
|
215
|
+
|
|
216
|
+
| 字段 | 说明 |
|
|
217
|
+
|------|------|
|
|
218
|
+
| `scanDate` | 扫描日期(YYYY-MM-DD) |
|
|
219
|
+
| `mongoUrl` | 已脱敏的连接串 |
|
|
220
|
+
| `summary` | 统计汇总(含 bySource、byVersion、byStatus、errorsByVersion、knownIssueCount) |
|
|
221
|
+
| `errors` | 所有 ERROR 级别问题详情(需要代码修正的公式问题) |
|
|
222
|
+
| `knownIssues` | 经人工审核确认不需要代码修正的问题(附 category 和 resolution) |
|
|
223
|
+
| `warnings` | 所有 WARN 级别问题详情(公式能工作但有风险) |
|
|
224
|
+
| `allFormulas` | 所有含公式字段的完整清单(含 PASS/ERROR/WARN) |
|
|
225
|
+
|
|
226
|
+
**`errors` vs `knownIssues` vs `warnings` 的区别:**
|
|
227
|
+
|
|
228
|
+
| | ERROR | KNOWN_ISSUE | WARN |
|
|
229
|
+
|--|--|--|--|
|
|
230
|
+
| 含义 | 公式**无法正常工作**,需代码修正 | 公式无法转换但**无需代码修正** | 公式**能工作但有风险** |
|
|
231
|
+
| 分类 | — | KNOWN_LIMITATION / BAD_SOURCE_DATA | — |
|
|
232
|
+
| 处理 | AI 辅助分析和修复 | 记录原因后归档 | 通常可批量忽略 |
|
|
233
|
+
| 典型规则 | E1、E2、E3、E4 | E1、E3(经审核) | W1、W2 |
|
|
234
|
+
|
|
235
|
+
`allFormulas` 可用于基线对比:两次扫描结果 diff,快速定位新增问题。
|
|
236
|
+
|
|
237
|
+
`summary.errorsByVersion` 标识 ERROR 来自 current 还是 history 版本,帮助判断优先级——current 版本的 ERROR 影响新审批单,history 版本的 ERROR 只影响已有旧审批单。
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 常见问题
|
|
242
|
+
|
|
243
|
+
**Q:脚本报错 `未找到 mongodb 模块`**
|
|
244
|
+
```bash
|
|
245
|
+
npm install mongodb --no-save
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Q:连接失败 `Authentication failed`**
|
|
249
|
+
在连接串中加入用户名密码:
|
|
250
|
+
```
|
|
251
|
+
mongodb://username:password@127.0.0.1:27017/<dbname>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Q:forms 集合为空**
|
|
255
|
+
确认 mongorestore 时指定了正确的 `--db` 和 `--collection` 参数,以及 bson 文件路径正确。
|
|
256
|
+
|
|
257
|
+
**Q:WARN 数量很多,是否需要全部修复?**
|
|
258
|
+
W1(除零风险)是运行时风险,建议优先修复;W2(字段名冲突)通常无实际影响,可按业务判断。
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# AI 提示词:生产公式问题分类评估
|
|
2
|
+
|
|
3
|
+
## 角色设定
|
|
4
|
+
|
|
5
|
+
你是 Steedos 工作流公式转换专家,精通老版本工作流表单公式语法与 amis 框架表达式的转换机制。
|
|
6
|
+
|
|
7
|
+
## 背景
|
|
8
|
+
|
|
9
|
+
Steedos 审批流程(workflow)表单使用老版本 `{字段名}` 语法定义公式,在运行时由 `mapFormula` 函数转换为 amis 框架的 `${表达式}` 语法。
|
|
10
|
+
|
|
11
|
+
转换规则摘要:
|
|
12
|
+
|
|
13
|
+
| 场景 | 输入示例 | 输出示例 |
|
|
14
|
+
|------|----------|----------|
|
|
15
|
+
| 简单字段引用 | `{月末里程数}` | `${月末里程数}` |
|
|
16
|
+
| 时间函数 | `{now}` | `${NOW()}` |
|
|
17
|
+
| 运算表达式 | `{月末里程数} - {月初里程数}` | `${月末里程数 - 月初里程数}` |
|
|
18
|
+
| 聚合函数 | `sum({出票金额})` | `${SUM(出票金额)}` |
|
|
19
|
+
| 子表聚合 | `sum({出票金额})` (子表字段) | `${SUM(ARRAYMAP(表格, item => item['出票金额']))}` |
|
|
20
|
+
| 上下文变量 | `{applicant}.name` | `${applicant.name}` |
|
|
21
|
+
| 对象字段展开 | `{合同}.name` | `${合同__expand.name}` |
|
|
22
|
+
| 特殊字符字段名 | `{油量(本地)}` | `${油量_本地}` |
|
|
23
|
+
| 方括号分组 | `[{a}+{b}]/{c}` | `${(a+b)/c}` |
|
|
24
|
+
| 大写金额 | `numToRMB({金额})` | `${UPPERMONEY(金额)}` |
|
|
25
|
+
|
|
26
|
+
`getSafeCode` 替换规则(字段名特殊字符转义):
|
|
27
|
+
|
|
28
|
+
| 字符 | 替换为 |
|
|
29
|
+
|------|--------|
|
|
30
|
+
| `(` 全角左括号 | `_` |
|
|
31
|
+
| `)` 全角右括号 | (删除) |
|
|
32
|
+
| `(` 半角左括号 | `_` |
|
|
33
|
+
| `)` 半角右括号 | (删除) |
|
|
34
|
+
| `、` 顿号 | `_` |
|
|
35
|
+
| `,` 全角逗号 | `_` |
|
|
36
|
+
| `%` | `_` |
|
|
37
|
+
| `=` | `_` |
|
|
38
|
+
| `:` 全角冒号 | `_` |
|
|
39
|
+
| `/` | `_` |
|
|
40
|
+
| `-` | `_` |
|
|
41
|
+
|
|
42
|
+
## 任务
|
|
43
|
+
|
|
44
|
+
以下是扫描脚本发现的公式 ERROR 列表(`errors` 数组):
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{在此粘贴 scan-result.json 文件中 errors 数组的内容}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
请对上述问题执行分组分析,输出结构化评估报告,每个分组包含:
|
|
51
|
+
|
|
52
|
+
1. **根因标签**(例如:方括号未转换、字段名含特殊字符、除零风险等)
|
|
53
|
+
2. **影响范围**(涉及 ERROR/WARN 数量、表单数量)
|
|
54
|
+
3. **代表性示例**(列出 1-3 个典型 originalFormula 和 convertedResult)
|
|
55
|
+
4. **修复策略**(代码层面需要改什么)
|
|
56
|
+
5. **优先级**(Critical / High / Medium / Low)
|
|
57
|
+
6. **风险评估**(修复后是否可能影响现有正常公式)
|
|
58
|
+
|
|
59
|
+
最后给出**总体建议**:哪些分组应优先修复,哪些可以延后,哪些是数据问题无需改代码。
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# AI 提示词:工作流公式转换代码修复
|
|
2
|
+
|
|
3
|
+
## 角色设定
|
|
4
|
+
|
|
5
|
+
你是 Steedos 工作流公式转换开发者,负责修复 `mapFormula` 函数中未兼容的公式语法。
|
|
6
|
+
|
|
7
|
+
## 需要修复的问题分组
|
|
8
|
+
|
|
9
|
+
以下是经 AI 评估确认、需要修复的问题分组列表:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
{在此粘贴 AI 评估确认后需要修复的分组列表}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 参考资料
|
|
16
|
+
|
|
17
|
+
修复前请先阅读以下背景文件,了解公式转换机制和三处同步要求:
|
|
18
|
+
|
|
19
|
+
- `.github/instructions/workflow-formula.instructions.md`(steedos-plugins 仓库)
|
|
20
|
+
- `steedos-packages/plugin-workflow/main/default/utils/formula-compat.js`(当前实现)
|
|
21
|
+
- `steedos-packages/plugin-workflow/main/default/test/test_formula_compat.js`(现有测试)
|
|
22
|
+
|
|
23
|
+
## 修复要求(5 步流程)
|
|
24
|
+
|
|
25
|
+
### 步骤 1:先写测试
|
|
26
|
+
|
|
27
|
+
在 `steedos-packages/plugin-workflow/main/default/test/test_formula_compat.js` 中为每个问题分组添加失败测试用例,确认测试在修复前为红色(失败),修复后为绿色(通过)。
|
|
28
|
+
|
|
29
|
+
### 步骤 2:改代码
|
|
30
|
+
|
|
31
|
+
修改 `mapFormula`(或 `getSafeCode`)实现,使新增测试通过,同时保证原有测试不退化。
|
|
32
|
+
|
|
33
|
+
### 步骤 3:三处同步
|
|
34
|
+
|
|
35
|
+
公式转换逻辑存在于三个位置,**必须同时更新**:
|
|
36
|
+
|
|
37
|
+
1. **steedos-widgets 仓库**(主版本,ESM):
|
|
38
|
+
`packages/@steedos-widgets/amis-lib/src/workflow/formula-utils.js`
|
|
39
|
+
|
|
40
|
+
2. **steedos-plugins 仓库**(CommonJS 镜像):
|
|
41
|
+
`steedos-packages/plugin-workflow/main/default/utils/formula-compat.js`
|
|
42
|
+
|
|
43
|
+
3. **steedos-plugins 仓库**(内联副本,EJS 模板):
|
|
44
|
+
`steedos-packages/plugin-workflow/main/default/routes/flow_form_design.ejs`
|
|
45
|
+
(内联函数 `_getSafeCode` 和 `_mapFormula`,从第 281 行起)
|
|
46
|
+
|
|
47
|
+
三处代码逻辑必须完全一致,否则设计器预览与运行时渲染会出现差异。
|
|
48
|
+
|
|
49
|
+
### 步骤 4:运行测试
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# plugins 侧回归测试
|
|
53
|
+
cd steedos-packages/plugin-workflow
|
|
54
|
+
node main/default/test/test_formula_compat.js
|
|
55
|
+
|
|
56
|
+
# widgets 侧单元测试(需在 steedos-widgets 仓库执行)
|
|
57
|
+
cd packages/@steedos-widgets/amis-lib
|
|
58
|
+
npx jest --verbose
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
所有测试必须通过,才能提交 PR。
|
|
62
|
+
|
|
63
|
+
### 步骤 5:同步 plugins 侧测试
|
|
64
|
+
|
|
65
|
+
若 widgets 侧新增了 Jest 测试用例,在 `steedos-packages/plugin-workflow/main/default/test/test_formula_compat.js` 中添加等价测试用例,保持两侧测试覆盖一致。
|
|
66
|
+
|
|
67
|
+
## 输出格式
|
|
68
|
+
|
|
69
|
+
请输出:
|
|
70
|
+
|
|
71
|
+
1. **新增测试用例代码**(`test_formula_compat.js` 中添加的内容)
|
|
72
|
+
2. **formula-compat.js 的 diff**(精确的代码改动)
|
|
73
|
+
3. **flow_form_design.ejs 的 diff**(对应的内联函数改动)
|
|
74
|
+
4. **formula-utils.js 的 diff**(steedos-widgets 仓库)
|
|
75
|
+
5. **验证说明**:如何手动验证修复效果(可用的测试公式示例)
|
|
File without changes
|