lan-low-code-cli 2.8.1 → 2.9.0
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/README.md +30 -0
- package/dist/commands/operation.js +48 -10
- package/dist/generator/flow/compiler.js +5 -0
- package/dist/generator/flow/mapping.js +258 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/lan-low-code-cli/SKILL.md +1 -1
- package/skills/lan-low-code-cli/SKILL.zh-CN.md +1 -1
- package/skills/lan-low-code-cli/references/cli-contract.md +8 -0
- package/skills/lan-low-code-cli/references/cli-contract.zh-CN.md +8 -0
package/README.md
CHANGED
|
@@ -373,6 +373,36 @@ llc flow delete --id <flowId> --yes
|
|
|
373
373
|
- `AddDataNode` / `AddOrUpdateNode`:分类表单或未填 `commitType` 时豁免 `flow`
|
|
374
374
|
- 校验失败时输出 `field` 与修复建议,`flow validate` / `flow compile` 退出码为 `1`,不会写入环境
|
|
375
375
|
|
|
376
|
+
**参数映射(`TransformNode` / `ExecuteNode` 等)**:
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
{
|
|
380
|
+
"id": "N4",
|
|
381
|
+
"type": "TransformNode",
|
|
382
|
+
"name": "转换",
|
|
383
|
+
"config": {
|
|
384
|
+
"outParam": {
|
|
385
|
+
"fd_title": { "type": "string", "description": "标题" },
|
|
386
|
+
"fd_amount": { "type": "number" }
|
|
387
|
+
},
|
|
388
|
+
"mapping": {
|
|
389
|
+
"fd_title": "$N2.fd_title",
|
|
390
|
+
"fd_amount": "$N2.fd_amount"
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
- `outParam` 可写字段映射对象(自动转成 `{id:'responseBody',type:'object',properties:{...}}` 的 JSON Schema 字符串),
|
|
397
|
+
也可直接写完整 Schema 或 Schema 字符串;编译器写入 `outParamType=1`
|
|
398
|
+
- `mapping` 的 `$<节点id>.<路径>` 自动转成
|
|
399
|
+
`{sourceType:'node',sourcePath:'node.N2.fd_title',targetPath:'fd_title'}`;`$context.<路径>` 转为上下文映射
|
|
400
|
+
- 编译器同时把出参 Schema 写入 `extraAttr.context_outputParam`,供下游映射与 IDE 显示
|
|
401
|
+
- 未写 `mapping` 时,若上游节点已提供 `context.context_outputParam`,按同名字段自动推断,并给出
|
|
402
|
+
`MAPPING_AUTO_INFERRED` 提示
|
|
403
|
+
- 常量或表达式请用 `{ "paramMappingWay": "formula", "valueFormula": "..." }`;
|
|
404
|
+
`ExecuteNode` / `InterfaceNode` / `SubActionNode` / `FlowBotsNode` 用同一 `mapping` 生成 `inputParamMapping`
|
|
405
|
+
|
|
376
406
|
**边界与已知限制**:
|
|
377
407
|
|
|
378
408
|
- `AddDataNode` / `AddOrUpdateNode` 的 `fields` 是平台真实结构
|
|
@@ -291,9 +291,11 @@ function buildCreatePayload(opts, flow) {
|
|
|
291
291
|
fdModelName: FLOW_MODEL_NAME,
|
|
292
292
|
fdCode: flow.fdCode,
|
|
293
293
|
};
|
|
294
|
-
const
|
|
295
|
-
if (
|
|
296
|
-
config.incParams = incParams;
|
|
294
|
+
const builtInc = buildIncParams(opts.incParams);
|
|
295
|
+
if (builtInc) {
|
|
296
|
+
config.incParams = builtInc.incParams;
|
|
297
|
+
config.formIncParams = builtInc.formIncParams;
|
|
298
|
+
}
|
|
297
299
|
const active = flow.fdProcessActive ?? {};
|
|
298
300
|
if (active.fdFormId) {
|
|
299
301
|
// 动作流绑定了表单:按钮按当前文档 id/ids 传参
|
|
@@ -343,7 +345,17 @@ function assertTriggerableFlow(flow) {
|
|
|
343
345
|
throw new Error(`动作流 ${flow.fdName ?? flow.fdId} 未开启「操作触发」,请在 trigger.fdRunType 中包含 operate 后重新发布`);
|
|
344
346
|
}
|
|
345
347
|
}
|
|
346
|
-
/**
|
|
348
|
+
/**
|
|
349
|
+
* 编译动作流入参。
|
|
350
|
+
*
|
|
351
|
+
* 平台落库结构(addOperationModal/content.tsx:533-576):
|
|
352
|
+
* fdConfig.incParams = [{ batchNo: 'B001' }, { repeat: 2 }]
|
|
353
|
+
* fdConfig.formIncParams = [{ field: { name: 'batchNo' } }, ...]
|
|
354
|
+
* 入参支持三种写法:
|
|
355
|
+
* 对象 { "batchNo": "B001", "repeat": 2 }
|
|
356
|
+
* 数组 [{ "batchNo": "B001" }]
|
|
357
|
+
* 数组 [{ "field": { "name": "batchNo" }, "value": { "val": "B001" } }]
|
|
358
|
+
*/
|
|
347
359
|
function buildIncParams(input) {
|
|
348
360
|
if (input === undefined || input === null || input === '')
|
|
349
361
|
return undefined;
|
|
@@ -355,9 +367,33 @@ function buildIncParams(input) {
|
|
|
355
367
|
catch (err) {
|
|
356
368
|
throw new Error(`--inc-params 不是合法 JSON:${err.message}`);
|
|
357
369
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
370
|
+
const entries = [];
|
|
371
|
+
const pushEntry = (name, value) => {
|
|
372
|
+
entries.push({ [name]: value });
|
|
373
|
+
};
|
|
374
|
+
if (Array.isArray(parsed)) {
|
|
375
|
+
for (const item of parsed) {
|
|
376
|
+
if (!item || typeof item !== 'object')
|
|
377
|
+
continue;
|
|
378
|
+
const record = item;
|
|
379
|
+
const field = record.field;
|
|
380
|
+
if (field && typeof field.name === 'string') {
|
|
381
|
+
const value = record.value;
|
|
382
|
+
pushEntry(field.name, value && 'val' in value ? value.val : record.value);
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
Object.entries(record).forEach(([name, value]) => pushEntry(name, value));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
else if (parsed && typeof parsed === 'object') {
|
|
389
|
+
Object.entries(parsed).forEach(([name, value]) => pushEntry(name, value));
|
|
390
|
+
}
|
|
391
|
+
if (entries.length === 0)
|
|
392
|
+
return undefined;
|
|
393
|
+
return {
|
|
394
|
+
incParams: entries,
|
|
395
|
+
formIncParams: entries.map((entry) => ({ field: { name: Object.keys(entry)[0] } })),
|
|
396
|
+
};
|
|
361
397
|
}
|
|
362
398
|
/**
|
|
363
399
|
* 基于现有操作构造 update 请求体。
|
|
@@ -440,9 +476,11 @@ function buildUpdatePayload(current, opts, flow) {
|
|
|
440
476
|
fdModelName: FLOW_MODEL_NAME,
|
|
441
477
|
fdCode: flow.fdCode,
|
|
442
478
|
};
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
445
|
-
config.incParams = incParams;
|
|
479
|
+
const builtInc = buildIncParams(opts.incParams);
|
|
480
|
+
if (builtInc) {
|
|
481
|
+
config.incParams = builtInc.incParams;
|
|
482
|
+
config.formIncParams = builtInc.formIncParams;
|
|
483
|
+
}
|
|
446
484
|
}
|
|
447
485
|
else {
|
|
448
486
|
const dialogId = opts.dialogId !== undefined
|
|
@@ -20,6 +20,7 @@ exports.normalizeGraph = normalizeGraph;
|
|
|
20
20
|
exports.compileFlow = compileFlow;
|
|
21
21
|
const layout_1 = require("./layout");
|
|
22
22
|
Object.defineProperty(exports, "NODE_SIZE", { enumerable: true, get: function () { return layout_1.NODE_SIZE; } });
|
|
23
|
+
const mapping_1 = require("./mapping");
|
|
23
24
|
const schema_1 = require("./schema");
|
|
24
25
|
const validate_1 = require("./validate");
|
|
25
26
|
Object.defineProperty(exports, "hasErrors", { enumerable: true, get: function () { return validate_1.hasErrors; } });
|
|
@@ -104,6 +105,10 @@ function normalizeGraph(spec, mode, hints = {}) {
|
|
|
104
105
|
if (business.length === 0 && edges.length === 0)
|
|
105
106
|
addEdge({ from: startId, to: endId });
|
|
106
107
|
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
108
|
+
// 在必填校验与节点实例化之前统一补齐参数映射(TransformNode 等)
|
|
109
|
+
for (const node of nodes) {
|
|
110
|
+
(0, mapping_1.prepareNodeConfig)(node, diagnostics, { edges, byId });
|
|
111
|
+
}
|
|
107
112
|
const ranks = { [startId]: 0 };
|
|
108
113
|
const branchTargets = new Set();
|
|
109
114
|
for (const edge of edges) {
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 参数映射编译器
|
|
4
|
+
*
|
|
5
|
+
* 平台里映射的持久化结构只有两种(`configuration/controls/paramMapping/index.tsx:100-135`):
|
|
6
|
+
* point: { paramMappingWay:'point', pointMappings:[{ sourceType, sourcePath, targetPath }] }
|
|
7
|
+
* formula: { paramMappingWay:'formula', valueFormula:'<JSON 字符串>' }
|
|
8
|
+
*
|
|
9
|
+
* spec 里允许更友好的写法(编译后会被删除,不落库):
|
|
10
|
+
* "config": {
|
|
11
|
+
* "outParam": { "fd_title": { "type": "string", "description": "标题" } },
|
|
12
|
+
* "mapping": { "fd_title": "$N2.fd_title", "fd_amount": "$N2.fd_amount" }
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* - `outParam` 也接受完整 JSON Schema(含 type/properties)或 JSON 字符串;
|
|
16
|
+
* - `mapping` 的值支持 `$<节点id>.<路径>` 与 `$context.<路径>`;
|
|
17
|
+
* - 也接受 `{ paramMappingWay:'formula', valueFormula:'...' }` 或 pointMappings 数组直接透传。
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.prepareNodeConfig = prepareNodeConfig;
|
|
21
|
+
const JSON_TYPES = new Set(['string', 'number', 'integer', 'boolean', 'array', 'object']);
|
|
22
|
+
const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
23
|
+
const NODE_REF = /^\$([0-9a-zA-Z_]{2,32})\.(.+)$/;
|
|
24
|
+
const CONTEXT_REF = /^\$context\.(.+)$/;
|
|
25
|
+
function targetPathOf(key) {
|
|
26
|
+
const index = key.indexOf('.');
|
|
27
|
+
return index >= 0 ? key.slice(index + 1) : key;
|
|
28
|
+
}
|
|
29
|
+
/** 字段映射 → JSON Schema(`{id:'responseBody', type:'object', properties:{...}}`) */
|
|
30
|
+
function buildSchemaFromFields(fields) {
|
|
31
|
+
const properties = {};
|
|
32
|
+
for (const [name, raw] of Object.entries(fields)) {
|
|
33
|
+
if (isRecord(raw)) {
|
|
34
|
+
properties[name] = { id: name, required: false, ...raw };
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (typeof raw === 'string' && JSON_TYPES.has(raw)) {
|
|
38
|
+
properties[name] = { id: name, type: raw, required: false, description: '' };
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
properties[name] = {
|
|
42
|
+
id: name,
|
|
43
|
+
type: 'string',
|
|
44
|
+
required: false,
|
|
45
|
+
...(typeof raw === 'string' && raw ? { description: raw } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { id: 'responseBody', type: 'object', description: '', properties };
|
|
49
|
+
}
|
|
50
|
+
/** 统一 outParam:字符串原样保留,对象转成 JSON Schema 字符串 */
|
|
51
|
+
function normalizeOutParam(value) {
|
|
52
|
+
if (typeof value === 'string') {
|
|
53
|
+
const trimmed = value.trim();
|
|
54
|
+
if (!trimmed)
|
|
55
|
+
return {};
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(trimmed);
|
|
58
|
+
return isRecord(parsed) ? { text: trimmed, schema: parsed } : { text: value };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return { text: value };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (isRecord(value)) {
|
|
65
|
+
const isSchema = 'type' in value || 'properties' in value || '$ref' in value;
|
|
66
|
+
const schema = isSchema
|
|
67
|
+
? { id: 'responseBody', type: 'object', description: '', ...value }
|
|
68
|
+
: buildSchemaFromFields(value);
|
|
69
|
+
return { text: JSON.stringify(schema), schema };
|
|
70
|
+
}
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
function parseMappingSource(expr) {
|
|
74
|
+
if (typeof expr !== 'string')
|
|
75
|
+
return undefined;
|
|
76
|
+
const value = expr.trim();
|
|
77
|
+
const contextMatch = CONTEXT_REF.exec(value);
|
|
78
|
+
if (contextMatch) {
|
|
79
|
+
return { sourceType: 'context', sourcePath: `context.${contextMatch[1]}` };
|
|
80
|
+
}
|
|
81
|
+
const nodeMatch = NODE_REF.exec(value);
|
|
82
|
+
if (nodeMatch) {
|
|
83
|
+
return { sourceType: 'node', sourcePath: `node.${nodeMatch[1]}.${nodeMatch[2]}` };
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
/** `{ 目标字段: 表达式 }` → pointMappings */
|
|
88
|
+
function buildPointMapping(mapping, diagnostics, nodeId, field) {
|
|
89
|
+
const pointMappings = [];
|
|
90
|
+
for (const [target, expr] of Object.entries(mapping)) {
|
|
91
|
+
if (target === 'paramMappingWay' || target === 'pointMappings' || target === 'valueFormula')
|
|
92
|
+
continue;
|
|
93
|
+
const source = parseMappingSource(expr);
|
|
94
|
+
if (!source) {
|
|
95
|
+
diagnostics.push({
|
|
96
|
+
level: 'warn',
|
|
97
|
+
code: 'MAPPING_VALUE_UNSUPPORTED',
|
|
98
|
+
message: `节点 ${nodeId} 的映射 ${target} 不是 $节点.路径 / $context.路径 形式,已跳过`,
|
|
99
|
+
nodeId,
|
|
100
|
+
field,
|
|
101
|
+
hint: '常量或表达式请改用 { "paramMappingWay": "formula", "valueFormula": "..." }',
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
pointMappings.push({ sourceType: source.sourceType, sourcePath: source.sourcePath, targetPath: targetPathOf(target) });
|
|
106
|
+
}
|
|
107
|
+
if (pointMappings.length === 0)
|
|
108
|
+
return undefined;
|
|
109
|
+
return { paramMappingWay: 'point', pointMappings };
|
|
110
|
+
}
|
|
111
|
+
/** 把 spec 的 mapping 统一成平台结构 */
|
|
112
|
+
function buildMapping(input, diagnostics, nodeId, field) {
|
|
113
|
+
if (typeof input === 'string') {
|
|
114
|
+
const value = input.trim();
|
|
115
|
+
if (!value)
|
|
116
|
+
return undefined;
|
|
117
|
+
if (value.startsWith('{'))
|
|
118
|
+
return { paramMappingWay: 'formula', valueFormula: value };
|
|
119
|
+
diagnostics.push({
|
|
120
|
+
level: 'error', code: 'MAPPING_FORMULA_INVALID',
|
|
121
|
+
message: `节点 ${nodeId} 的 ${field} 字符串必须是 valueFormula 的 JSON`, nodeId, field,
|
|
122
|
+
});
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(input)) {
|
|
126
|
+
return input.length > 0 ? { paramMappingWay: 'point', pointMappings: input } : undefined;
|
|
127
|
+
}
|
|
128
|
+
if (!isRecord(input))
|
|
129
|
+
return undefined;
|
|
130
|
+
if (input.paramMappingWay === 'formula') {
|
|
131
|
+
if (typeof input.valueFormula === 'string' && input.valueFormula.trim()) {
|
|
132
|
+
return { paramMappingWay: 'formula', valueFormula: input.valueFormula };
|
|
133
|
+
}
|
|
134
|
+
if (input.valueFormula !== undefined) {
|
|
135
|
+
return { paramMappingWay: 'formula', valueFormula: JSON.stringify(input.valueFormula) };
|
|
136
|
+
}
|
|
137
|
+
diagnostics.push({
|
|
138
|
+
level: 'error', code: 'MAPPING_FORMULA_REQUIRED',
|
|
139
|
+
message: `节点 ${nodeId} 的 ${field} 声明 paramMappingWay=formula 但缺少 valueFormula`, nodeId, field,
|
|
140
|
+
});
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
return buildPointMapping(input, diagnostics, nodeId, field);
|
|
144
|
+
}
|
|
145
|
+
/** 平台要求 valueFormula 是字符串;对象写法统一序列化 */
|
|
146
|
+
function normalizeMappingValue(value) {
|
|
147
|
+
if (!isRecord(value))
|
|
148
|
+
return value;
|
|
149
|
+
if (value.valueFormula !== undefined && typeof value.valueFormula !== 'string') {
|
|
150
|
+
return { ...value, valueFormula: JSON.stringify(value.valueFormula) };
|
|
151
|
+
}
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
/** 上游已有 `context_outputParam` 时,按同名字段自动补齐 point 映射 */
|
|
155
|
+
function inferFromUpstream(node, outputNames, graph) {
|
|
156
|
+
for (const edge of graph.edges.filter((item) => item.to === node.id)) {
|
|
157
|
+
const upstream = graph.byId.get(edge.from);
|
|
158
|
+
const upstreamContext = upstream?.context;
|
|
159
|
+
const schema = upstreamContext?.context_outputParam;
|
|
160
|
+
if (!isRecord(schema))
|
|
161
|
+
continue;
|
|
162
|
+
const properties = isRecord(schema.properties) ? schema.properties : undefined;
|
|
163
|
+
if (!properties)
|
|
164
|
+
continue;
|
|
165
|
+
const pointMappings = outputNames
|
|
166
|
+
.filter((name) => name in properties)
|
|
167
|
+
.map((name) => ({
|
|
168
|
+
sourceType: 'node',
|
|
169
|
+
sourcePath: `node.${edge.from}.${name}`,
|
|
170
|
+
targetPath: name,
|
|
171
|
+
}));
|
|
172
|
+
if (pointMappings.length > 0)
|
|
173
|
+
return { pointMappings, upstreamId: edge.from };
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 在归一化阶段就地补齐节点配置(校验与实例化都会读取同一份 config)。
|
|
179
|
+
* 只处理参数映射相关的 nodeType,其余节点原样返回。
|
|
180
|
+
*/
|
|
181
|
+
function prepareNodeConfig(node, diagnostics, graph) {
|
|
182
|
+
const config = { ...(node.config ?? {}) };
|
|
183
|
+
node.config = config;
|
|
184
|
+
if (node.type === 'TransformNode') {
|
|
185
|
+
const normalized = normalizeOutParam(config.outParam);
|
|
186
|
+
if (normalized.text) {
|
|
187
|
+
config.outParam = normalized.text;
|
|
188
|
+
if (config.outParamType === undefined)
|
|
189
|
+
config.outParamType = 1;
|
|
190
|
+
if (normalized.schema) {
|
|
191
|
+
// IDE 的 onParamChange 会把出参 Schema 写入 context_outputParam(transformNode.tsx:64-67)
|
|
192
|
+
node.context = { ...(node.context ?? {}), context_outputParam: normalized.schema };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if (config.outParam !== undefined) {
|
|
196
|
+
diagnostics.push({
|
|
197
|
+
level: 'error', code: 'OUT_PARAM_INVALID',
|
|
198
|
+
message: `节点 ${node.id} 的 config.outParam 需为 JSON Schema 字符串或字段映射对象`,
|
|
199
|
+
nodeId: node.id, field: 'outParam',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (config.outputParamMapping !== undefined) {
|
|
203
|
+
config.outputParamMapping = normalizeMappingValue(config.outputParamMapping);
|
|
204
|
+
}
|
|
205
|
+
else if (config.mapping !== undefined) {
|
|
206
|
+
const generated = buildMapping(config.mapping, diagnostics, node.id, 'mapping');
|
|
207
|
+
if (generated)
|
|
208
|
+
config.outputParamMapping = generated;
|
|
209
|
+
}
|
|
210
|
+
if (config.outputParamMapping === undefined && normalized.schema && isRecord(normalized.schema.properties) && graph) {
|
|
211
|
+
const names = Object.keys(normalized.schema.properties);
|
|
212
|
+
const inferred = inferFromUpstream(node, names, graph);
|
|
213
|
+
if (inferred) {
|
|
214
|
+
config.outputParamMapping = { paramMappingWay: 'point', pointMappings: inferred.pointMappings };
|
|
215
|
+
diagnostics.push({
|
|
216
|
+
level: 'warn', code: 'MAPPING_AUTO_INFERRED',
|
|
217
|
+
message: `节点 ${node.id} 未提供 mapping,已按上游 ${inferred.upstreamId} 的同名字段自动生成 ${inferred.pointMappings.length} 条映射`,
|
|
218
|
+
nodeId: node.id, field: 'outputParamMapping',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
delete config.mapping;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (node.type === 'EndNode') {
|
|
226
|
+
const normalized = normalizeOutParam(config.outParam);
|
|
227
|
+
if (normalized.text) {
|
|
228
|
+
config.outParam = normalized.text;
|
|
229
|
+
// 主动触发的结束节点 outParamType=0(endNode.tsx:46)
|
|
230
|
+
if (config.outParamType === undefined)
|
|
231
|
+
config.outParamType = 0;
|
|
232
|
+
if (normalized.schema) {
|
|
233
|
+
node.context = { ...(node.context ?? {}), context_outputParam: normalized.schema };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (config.outputParamMapping !== undefined) {
|
|
237
|
+
config.outputParamMapping = normalizeMappingValue(config.outputParamMapping);
|
|
238
|
+
}
|
|
239
|
+
else if (config.mapping !== undefined) {
|
|
240
|
+
const generated = buildMapping(config.mapping, diagnostics, node.id, 'mapping');
|
|
241
|
+
if (generated)
|
|
242
|
+
config.outputParamMapping = generated;
|
|
243
|
+
}
|
|
244
|
+
delete config.mapping;
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (['ExecuteNode', 'InterfaceNode', 'SubActionNode', 'FlowBotsNode'].includes(node.type)) {
|
|
248
|
+
if (config.inputParamMapping !== undefined) {
|
|
249
|
+
config.inputParamMapping = normalizeMappingValue(config.inputParamMapping);
|
|
250
|
+
}
|
|
251
|
+
else if (config.mapping !== undefined) {
|
|
252
|
+
const generated = buildMapping(config.mapping, diagnostics, node.id, 'mapping');
|
|
253
|
+
if (generated)
|
|
254
|
+
config.inputParamMapping = generated;
|
|
255
|
+
}
|
|
256
|
+
delete config.mapping;
|
|
257
|
+
}
|
|
258
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ const locale_1 = require("./commands/locale");
|
|
|
12
12
|
const operation_1 = require("./commands/operation");
|
|
13
13
|
const template_1 = require("./commands/template");
|
|
14
14
|
const logger_1 = require("./utils/logger");
|
|
15
|
-
const VERSION = '2.
|
|
15
|
+
const VERSION = '2.9.0';
|
|
16
16
|
const program = new commander_1.Command();
|
|
17
17
|
exports.program = program;
|
|
18
18
|
program
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lan-low-code-cli
|
|
3
|
-
version: 2.
|
|
3
|
+
version: 2.9.0
|
|
4
4
|
author: Pig George
|
|
5
5
|
description: Operate the Landray low-code form engine through the `llc` CLI. Use when the user asks to create, inspect, modify, publish, or delete low-code apps and forms; generate field configs; configure layouts (grid/tabs), field permissions, options, i18n, or control actions (onChange/onLoad); or run pre-publish validation. Also use for troubleshooting form loading/rendering errors.
|
|
6
6
|
tags:
|
|
@@ -296,6 +296,14 @@ Application-scoped action flows. Every write command needs `--yes` and an explic
|
|
|
296
296
|
`loopDefine.type`; `MessageNode` validates `notifyType`/`subject`/`templateCode`/`linkPage`/`linkUrl`
|
|
297
297
|
by `subjectType` and `messageLink.linkType`; `AddDataNode`/`AddOrUpdateNode` skip `flow` for
|
|
298
298
|
category forms or when `commitType` is absent. Diagnostics carry `field` and `hint`; errors exit `1`.
|
|
299
|
+
- **Parameter mapping shorthand**: `TransformNode` accepts `config.outParam` as a field map
|
|
300
|
+
(`{"fd_title":{"type":"string","description":"标题"}}`) or a full JSON Schema / Schema string, plus
|
|
301
|
+
`config.mapping` as `{"targetPath":"$<nodeId>.<path>"}` (also `$context.<path>`). The compiler emits
|
|
302
|
+
`outParamType=1`, `outParam` (Schema string), `outputParamMapping` (`pointMappings`) and copies the
|
|
303
|
+
Schema into `extraAttr.context_outputParam`. When `mapping` is absent it is inferred from an upstream
|
|
304
|
+
`context.context_outputParam` by matching field names (`MAPPING_AUTO_INFERRED` warning). Constants or
|
|
305
|
+
expressions require `{"paramMappingWay":"formula","valueFormula":"..."}`. `ExecuteNode` /
|
|
306
|
+
`InterfaceNode` / `SubActionNode` / `FlowBotsNode` reuse `config.mapping` for `inputParamMapping`.
|
|
299
307
|
- Publishing derives `fdProcessActive` on the backend from `startNodeDef`; `fdStatus=1` means enabled.
|
|
300
308
|
`flow logs` endpoints vary by deployment (`ModelingProcessLog/findAll` vs
|
|
301
309
|
`sysModelingLog/findAllWithAppId`); use `--endpoint` if the defaults fail.
|
|
@@ -282,6 +282,14 @@ llc operation detach --form-id <fdId> --operation-id <operationId> #
|
|
|
282
282
|
校验 `notifyType` / `subject` / `templateCode` / `linkPage` / `linkUrl`;
|
|
283
283
|
`AddDataNode` / `AddOrUpdateNode` 在分类表单或未填 `commitType` 时豁免 `flow`。
|
|
284
284
|
诊断会带 `field` 与 `hint`,有错误时退出码为 `1`。
|
|
285
|
+
- **参数映射简写**:`TransformNode` 的 `config.outParam` 可写字段映射对象
|
|
286
|
+
(`{"fd_title":{"type":"string","description":"标题"}}`),也可写完整 JSON Schema 或 Schema 字符串;
|
|
287
|
+
`config.mapping` 支持 `{"目标字段":"$<节点id>.<路径>"}`(也支持 `$context.<路径>`)。编译器会输出
|
|
288
|
+
`outParamType=1`、`outParam`(Schema 字符串)、`outputParamMapping`(`pointMappings`),并把 Schema
|
|
289
|
+
写入 `extraAttr.context_outputParam`。未写 `mapping` 时,会按上游 `context.context_outputParam`
|
|
290
|
+
的同名字段自动推断,并给出 `MAPPING_AUTO_INFERRED` 提示。常量或表达式需改用
|
|
291
|
+
`{"paramMappingWay":"formula","valueFormula":"..."}`。`ExecuteNode` / `InterfaceNode` /
|
|
292
|
+
`SubActionNode` / `FlowBotsNode` 复用 `config.mapping` 生成 `inputParamMapping`。
|
|
285
293
|
- 发布时后端从 `startNodeDef` 派生 `fdProcessActive`;`fdStatus=1` 表示已启用。
|
|
286
294
|
`flow logs` 端点因部署而异(`ModelingProcessLog/findAll` 与
|
|
287
295
|
`sysModelingLog/findAllWithAppId`);默认都失败时用 `--endpoint` 指定。
|