n8n-nodes-kingsoft-airscript 0.3.2 → 2.0.3
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.
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.KingsoftAirscript = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const API_CONFIG = {
|
|
6
|
+
BASE_URL: 'https://www.kdocs.cn',
|
|
7
|
+
ENDPOINTS: {
|
|
8
|
+
SYNC_TASK: '/api/v3/ide/file/:file_id/script/:script_id/sync_task',
|
|
9
|
+
ASYNC_TASK: '/api/v3/ide/file/:file_id/script/:script_id/task',
|
|
10
|
+
GET_TASK: '/api/v3/script/task'
|
|
11
|
+
}
|
|
12
|
+
};
|
|
5
13
|
class KingsoftAirscript {
|
|
6
14
|
constructor() {
|
|
7
15
|
this.description = {
|
|
@@ -11,18 +19,76 @@ class KingsoftAirscript {
|
|
|
11
19
|
group: ['transform'],
|
|
12
20
|
version: 3,
|
|
13
21
|
subtitle: '={{$parameter["operation"]}}',
|
|
14
|
-
description:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
description: `
|
|
23
|
+
<div style="padding: 16px; background-color: #f8f9fa; border-radius: 8px; margin-bottom: 16px;">
|
|
24
|
+
<h3>📖 金山 AirScript 执行器</h3>
|
|
25
|
+
<p>一个功能强大的通用执行器,支持同步/异步执行脚本、自动分批处理和智能参数生成。</p>
|
|
26
|
+
|
|
27
|
+
<h4>🚀 主要功能</h4>
|
|
28
|
+
<ul>
|
|
29
|
+
<li>支持同步/异步执行脚本</li>
|
|
30
|
+
<li>自动分批处理大量数据</li>
|
|
31
|
+
<li>智能参数模板生成</li>
|
|
32
|
+
<li>完善的错误处理和重试机制</li>
|
|
33
|
+
<li>支持附件、公式、级联字段等复杂数据类型</li>
|
|
34
|
+
</ul>
|
|
35
|
+
|
|
36
|
+
<h4>💡 使用指南</h4>
|
|
37
|
+
<ol>
|
|
38
|
+
<li><strong>选择操作类型</strong>:同步执行/异步执行/获取任务状态</li>
|
|
39
|
+
<li><strong>输入 ID</strong>:通过 Webhook 链接或手动输入 File ID 和 Script ID</li>
|
|
40
|
+
<li><strong>配置参数</strong>:
|
|
41
|
+
<ul>
|
|
42
|
+
<li>选择 <strong>参数模板</strong> 快速生成参数结构</li>
|
|
43
|
+
<li>或在 <strong>脚本参数 (Argv)</strong> 中手动输入 JSON</li>
|
|
44
|
+
</ul>
|
|
45
|
+
</li>
|
|
46
|
+
<li><strong>高级选项</strong>:配置超时、重试、分批等高级设置</li>
|
|
47
|
+
<li><strong>执行工作流</strong>:运行工作流执行脚本</li>
|
|
48
|
+
</ol>
|
|
49
|
+
|
|
50
|
+
<h4>📚 示例库</h4>
|
|
51
|
+
<p>参数模板按功能分类:</p>
|
|
52
|
+
|
|
53
|
+
<h5>🔄 数据录入类</h5>
|
|
54
|
+
<ul>
|
|
55
|
+
<li>基本数据录入:向指定数据表中录入新数据</li>
|
|
56
|
+
<li>使用 N8N 表达式:在 N8N 工作流中动态处理输入数据</li>
|
|
57
|
+
<li>处理附件字段:录入包含图片等附件的数据</li>
|
|
58
|
+
<li>处理级联字段:录入包含级联字段的数据</li>
|
|
59
|
+
</ul>
|
|
60
|
+
|
|
61
|
+
<h5>🔄 数据更新类</h5>
|
|
62
|
+
<ul>
|
|
63
|
+
<li>使用复合唯一键:使用多个字段作为复合唯一键进行更新</li>
|
|
64
|
+
<li>批量迁移数据:将数据从一个表迁移到另一个表</li>
|
|
65
|
+
</ul>
|
|
66
|
+
|
|
67
|
+
<h5>🔄 结构配置类</h5>
|
|
68
|
+
<ul>
|
|
69
|
+
<li>设置公式字段:向表中设置公式字段的表达式</li>
|
|
70
|
+
<li>设置级联选项:配置级联字段的选项结构</li>
|
|
71
|
+
<li>设置关联属性:配置关联字段的属性</li>
|
|
72
|
+
<li>智能创建表格:根据数据结构智能创建表格和字段</li>
|
|
73
|
+
</ul>
|
|
74
|
+
|
|
75
|
+
<h4>❓ 常见问题</h4>
|
|
76
|
+
<ul>
|
|
77
|
+
<li><strong>Q: 节点不出现在 n8n 界面中?</strong><br>A: 请确保运行 <code>npm run build</code> 并正确链接节点,或手动复制构建文件到 <code>%APPDATA%\n8n\nodes</code> 目录。</li>
|
|
78
|
+
<li><strong>Q: 如何获取 Webhook 链接?</strong><br>A: 在金山文档脚本编辑器中,点击 "发布" → "Webhook",复制生成的完整链接。</li>
|
|
79
|
+
<li><strong>Q: 分批处理如何工作?</strong><br>A: 开启 "自动分批执行" 后,节点会根据 "分批字段路径" 从 argv 中提取数组,按批次大小分割并并发执行。</li>
|
|
80
|
+
<li><strong>Q: 如何处理附件字段?</strong><br>A: 使用 "处理附件字段" 模板,在 data 数组中包含 base64 编码的附件数据。</li>
|
|
81
|
+
<li><strong>Q: 如何使用 N8N 表达式?</strong><br>A: 使用 "使用 N8N 表达式" 模板,或在手动输入中使用 <code>{{ JSON.stringify( $input.all().map(i => i.json) ) }}</code> 格式。</li>
|
|
82
|
+
</ul>
|
|
83
|
+
|
|
84
|
+
<h4>🔗 更多资源</h4>
|
|
85
|
+
<p>访问 <a href="https://www.kdocs.cn/l/cho73TDGgMPP" target="_blank">官方指南 & 脚本库</a> 获取更多帮助和示例脚本。</p>
|
|
86
|
+
</div>
|
|
87
|
+
`,
|
|
88
|
+
defaults: { name: 'Kingsoft AirScript' },
|
|
18
89
|
inputs: ['main'],
|
|
19
90
|
outputs: ['main'],
|
|
20
|
-
credentials: [
|
|
21
|
-
{
|
|
22
|
-
name: 'kingsoftAirscriptApi',
|
|
23
|
-
required: true,
|
|
24
|
-
},
|
|
25
|
-
],
|
|
91
|
+
credentials: [{ name: 'kingsoftAirscriptApi', required: true }],
|
|
26
92
|
properties: [
|
|
27
93
|
{
|
|
28
94
|
displayName: '操作',
|
|
@@ -42,21 +108,11 @@ class KingsoftAirscript {
|
|
|
42
108
|
type: 'options',
|
|
43
109
|
noDataExpression: true,
|
|
44
110
|
displayOptions: {
|
|
45
|
-
show: {
|
|
46
|
-
operation: ['runScriptSync', 'runScriptAsync'],
|
|
47
|
-
},
|
|
111
|
+
show: { operation: ['runScriptSync', 'runScriptAsync'] },
|
|
48
112
|
},
|
|
49
113
|
options: [
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
value: 'url',
|
|
53
|
-
description: '粘贴完整的链接,最简单快捷',
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: '手动输入 ID',
|
|
57
|
-
value: 'manual',
|
|
58
|
-
description: '分别填写 File ID 和 Script ID',
|
|
59
|
-
},
|
|
114
|
+
{ name: '通过 Webhook 链接输入', value: 'url', description: '粘贴完整的链接,最简单快捷' },
|
|
115
|
+
{ name: '手动输入 ID', value: 'manual', description: '分别填写 File ID 和 Script ID' },
|
|
60
116
|
],
|
|
61
117
|
default: 'url',
|
|
62
118
|
},
|
|
@@ -110,11 +166,7 @@ class KingsoftAirscript {
|
|
|
110
166
|
placeholder: '添加可选参数',
|
|
111
167
|
default: {},
|
|
112
168
|
description: '设置可选的 Context 参数,会附加到请求中',
|
|
113
|
-
displayOptions: {
|
|
114
|
-
show: {
|
|
115
|
-
operation: ['runScriptSync', 'runScriptAsync'],
|
|
116
|
-
},
|
|
117
|
-
},
|
|
169
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
|
|
118
170
|
options: [
|
|
119
171
|
{ displayName: '表名 (Sheet Name)', name: 'sheetName', type: 'string', default: '', description: '对应 Context.sheet_name' },
|
|
120
172
|
{ displayName: '范围 (Range)', name: 'range', type: 'string', default: '', description: '对应 Context.range, 例如 "$B$156"' },
|
|
@@ -123,21 +175,126 @@ class KingsoftAirscript {
|
|
|
123
175
|
{ displayName: '选区 (DB Selection)', name: 'dbSelection', type: 'string', default: '', description: '对应 Context.db_selection' },
|
|
124
176
|
],
|
|
125
177
|
},
|
|
178
|
+
{
|
|
179
|
+
displayName: '参数模板',
|
|
180
|
+
name: 'argvTemplate',
|
|
181
|
+
type: 'options',
|
|
182
|
+
noDataExpression: true,
|
|
183
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
|
|
184
|
+
options: [
|
|
185
|
+
{ name: '处理附件字段', value: 'attachment', description: '录入包含图片等附件的数据' },
|
|
186
|
+
{ name: '处理级联字段', value: 'cascade', description: '录入包含级联字段的数据' },
|
|
187
|
+
{ name: '基本数据录入', value: 'basicInput', description: '向指定数据表中录入新数据' },
|
|
188
|
+
{ name: '批量迁移数据', value: 'migrate', description: '将数据从一个表迁移到另一个表' },
|
|
189
|
+
{ name: '设置公式字段', value: 'formula', description: '向表中设置公式字段的表达式' },
|
|
190
|
+
{ name: '设置关联属性', value: 'setLinkProperties', description: '配置关联字段的属性' },
|
|
191
|
+
{ name: '设置级联选项', value: 'setCascadeOptions', description: '配置级联字段的选项结构' },
|
|
192
|
+
{ name: '使用 N8N 表达式', value: 'n8nExpression', description: '在 N8N 工作流中动态处理输入数据' },
|
|
193
|
+
{ name: '使用复合唯一键', value: 'compositeKey', description: '使用多个字段作为复合唯一键进行更新' },
|
|
194
|
+
{ name: '智能创建表格', value: 'createSheet', description: '根据数据结构智能创建表格和字段' },
|
|
195
|
+
{ name: '自定义 (手动输入)', value: 'custom', description: '手动输入 JSON 格式参数' },
|
|
196
|
+
],
|
|
197
|
+
default: 'custom',
|
|
198
|
+
description: '选择预设模板后,会显示对应的 Argv JSON 输入框(可编辑)',
|
|
199
|
+
},
|
|
126
200
|
{
|
|
127
201
|
displayName: '脚本参数 (Argv)',
|
|
128
|
-
name: '
|
|
202
|
+
name: 'argv_custom',
|
|
129
203
|
type: 'json',
|
|
130
204
|
default: '{\n "message": "Hello from n8n!"\n}',
|
|
131
205
|
required: true,
|
|
132
|
-
displayOptions: {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
206
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['custom'] } },
|
|
207
|
+
description: '自定义 JSON 参数',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
displayName: '脚本参数 (Argv)',
|
|
211
|
+
name: 'argv_basicInput',
|
|
212
|
+
type: 'json',
|
|
213
|
+
default: `{\n "sheet_name": "客户表",\n "覆盖模式": false,\n "data": [\n {\n "客户名称": "张三",\n "联系方式": "13800138000",\n "邮箱": "zhangsan@example.com"\n }\n ]\n}`,
|
|
214
|
+
required: true,
|
|
215
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['basicInput'] } },
|
|
216
|
+
description: '基本数据录入模板参数(可编辑)',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
displayName: '脚本参数 (Argv)',
|
|
220
|
+
name: 'argv_attachment',
|
|
221
|
+
type: 'json',
|
|
222
|
+
default: `{\n "sheet_name": "产品表",\n "覆盖模式": true,\n "唯一键": "产品ID",\n "data": [\n {\n "产品ID": "P001",\n "产品名称": "XXX沙发",\n "图片": "https://example.com/image1.jpg",\n "描述": "舒适的三人沙发"\n }\n ]\n}`,
|
|
223
|
+
required: true,
|
|
224
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['attachment'] } },
|
|
225
|
+
description: '处理附件字段模板参数(可编辑)',
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
displayName: '脚本参数 (Argv)',
|
|
229
|
+
name: 'argv_cascade',
|
|
230
|
+
type: 'json',
|
|
231
|
+
default: `{\n "sheet_name": "订单表",\n "覆盖模式": true,\n "唯一键": "订单ID",\n "data": [\n {\n "订单ID": "O001",\n "客户": "张三",\n "地区": "广东省",\n "城市": "深圳市",\n "产品": "XXX沙发"\n }\n ]\n}`,
|
|
232
|
+
required: true,
|
|
233
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['cascade'] } },
|
|
234
|
+
description: '处理级联字段模板参数(可编辑)',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
displayName: '脚本参数 (Argv)',
|
|
238
|
+
name: 'argv_migrate',
|
|
239
|
+
type: 'json',
|
|
240
|
+
default: `{\n "源表名称": "旧客户表",\n "目标表名称": "新客户表",\n "映射关系": {\n "旧ID": "客户ID",\n "姓名": "客户名称",\n "电话": "联系方式"\n },\n "过滤条件": "状态 = '活跃'"\n}`,
|
|
241
|
+
required: true,
|
|
242
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['migrate'] } },
|
|
243
|
+
description: '批量迁移数据模板参数(可编辑)',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
displayName: '脚本参数 (Argv)',
|
|
247
|
+
name: 'argv_formula',
|
|
248
|
+
type: 'json',
|
|
249
|
+
default: `{\n "sheet_name": "销售表",\n "字段配置": [\n {\n "字段名称": "销售额",\n "字段类型": "公式",\n "表达式": "单价 * 数量"\n }\n ]\n}`,
|
|
250
|
+
required: true,
|
|
251
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['formula'] } },
|
|
252
|
+
description: '设置公式字段模板参数(可编辑)',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
displayName: '脚本参数 (Argv)',
|
|
256
|
+
name: 'argv_setLinkProperties',
|
|
257
|
+
type: 'json',
|
|
258
|
+
default: `{\n "sheet_name": "订单表",\n "关联字段": "客户ID",\n "目标表": "客户表",\n "显示字段": ["客户名称", "联系方式"]\n}`,
|
|
259
|
+
required: true,
|
|
260
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['setLinkProperties'] } },
|
|
261
|
+
description: '设置关联属性模板参数(可编辑)',
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
displayName: '脚本参数 (Argv)',
|
|
265
|
+
name: 'argv_setCascadeOptions',
|
|
266
|
+
type: 'json',
|
|
267
|
+
default: `{\n "sheet_name": "地区表",\n "级联字段": "地区",\n "选项结构": {\n "广东省": ["深圳市", "广州市", "东莞市"],\n "浙江省": ["杭州市", "宁波市", "温州市"]\n }\n}`,
|
|
268
|
+
required: true,
|
|
269
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['setCascadeOptions'] } },
|
|
270
|
+
description: '设置级联选项模板参数(可编辑)',
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
displayName: '脚本参数 (Argv)',
|
|
274
|
+
name: 'argv_n8nExpression',
|
|
275
|
+
type: 'json',
|
|
276
|
+
default: `{\n "sheet_name": "动态表",\n "覆盖模式": true,\n "唯一键": "ID",\n "data": [\n {\n "ID": "={{ $json.id }}",\n "名称": "={{ $json.name }}",\n "值": "={{ $json.value }}"\n }\n ]\n}`,
|
|
277
|
+
required: true,
|
|
278
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['n8nExpression'] } },
|
|
279
|
+
description: '使用 N8N 表达式模板参数(可编辑)',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
displayName: '脚本参数 (Argv)',
|
|
283
|
+
name: 'argv_compositeKey',
|
|
284
|
+
type: 'json',
|
|
285
|
+
default: `{\n "sheet_name": "库存表",\n "覆盖模式": true,\n "唯一键": ["产品ID", "仓库ID"],\n "data": [\n {\n "产品ID": "P001",\n "仓库ID": "W001",\n "库存数量": 100,\n "最后更新": "2024-01-01"\n }\n ]\n}`,
|
|
286
|
+
required: true,
|
|
287
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['compositeKey'] } },
|
|
288
|
+
description: '使用复合唯一键模板参数(可编辑)',
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
displayName: '脚本参数 (Argv)',
|
|
292
|
+
name: 'argv_createSheet',
|
|
293
|
+
type: 'json',
|
|
294
|
+
default: `{\n "sheet_name": "新表格",\n "字段定义": [\n {\n "名称": "ID",\n "类型": "文本",\n "唯一": true\n },\n {\n "名称": "名称",\n "类型": "文本"\n },\n {\n "名称": "数值",\n "类型": "数字"\n },\n {\n "名称": "日期",\n "类型": "日期"\n }\n ],\n "数据": [\n {\n "ID": "1",\n "名称": "测试1",\n "数值": 100,\n "日期": "2024-01-01"\n }\n ]\n}`,
|
|
295
|
+
required: true,
|
|
296
|
+
displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['createSheet'] } },
|
|
297
|
+
description: '智能创建表格模板参数(可编辑)',
|
|
141
298
|
},
|
|
142
299
|
{
|
|
143
300
|
displayName: '任务 ID (Task ID)',
|
|
@@ -145,13 +302,7 @@ class KingsoftAirscript {
|
|
|
145
302
|
type: 'string',
|
|
146
303
|
default: '',
|
|
147
304
|
required: true,
|
|
148
|
-
displayOptions: {
|
|
149
|
-
show: {
|
|
150
|
-
operation: [
|
|
151
|
-
'getTaskStatus',
|
|
152
|
-
],
|
|
153
|
-
},
|
|
154
|
-
},
|
|
305
|
+
displayOptions: { show: { operation: ['getTaskStatus'] } },
|
|
155
306
|
description: '从异步执行脚本操作中获取到的任务 ID',
|
|
156
307
|
},
|
|
157
308
|
{
|
|
@@ -167,7 +318,15 @@ class KingsoftAirscript {
|
|
|
167
318
|
name: 'a_parallelExecution',
|
|
168
319
|
type: 'boolean',
|
|
169
320
|
default: false,
|
|
170
|
-
description: 'Whether
|
|
321
|
+
description: 'Whether enabled, the node will process all input items in parallel',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
displayName: '查询任务超时(秒)',
|
|
325
|
+
name: 'i_statusTimeoutSeconds',
|
|
326
|
+
type: 'number',
|
|
327
|
+
default: 30,
|
|
328
|
+
typeOptions: { minValue: 3 },
|
|
329
|
+
description: '查询 /api/v3/script/task 的 HTTP 超时(秒)。',
|
|
171
330
|
},
|
|
172
331
|
{
|
|
173
332
|
displayName: '超时时间 (秒)',
|
|
@@ -176,15 +335,60 @@ class KingsoftAirscript {
|
|
|
176
335
|
default: 300,
|
|
177
336
|
typeOptions: { minValue: 5 },
|
|
178
337
|
displayOptions: { show: { '/operation': ['runScriptAsync'], '/c_waitForCompletion': [true] } },
|
|
338
|
+
description: '异步等待完成模式下,最多等待的总时长(秒)。',
|
|
179
339
|
},
|
|
180
340
|
{
|
|
181
341
|
displayName: '等待执行完成',
|
|
182
342
|
name: 'c_waitForCompletion',
|
|
183
343
|
type: 'boolean',
|
|
184
344
|
default: false,
|
|
185
|
-
description: 'Whether
|
|
345
|
+
description: 'Whether enabled, when running async the node will poll until the task finishes',
|
|
186
346
|
displayOptions: { show: { '/operation': ['runScriptAsync'] } },
|
|
187
347
|
},
|
|
348
|
+
{
|
|
349
|
+
displayName: '调试模式(输出 _调试 字段)',
|
|
350
|
+
name: 'w_debug',
|
|
351
|
+
type: 'boolean',
|
|
352
|
+
default: false,
|
|
353
|
+
description: 'Whether enabled, include a `_debug` field with latency and retry details in the output',
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
displayName: '分批输出模式',
|
|
357
|
+
name: 'v_chunkOutputMode',
|
|
358
|
+
type: 'options',
|
|
359
|
+
default: 'split',
|
|
360
|
+
options: [
|
|
361
|
+
{ name: '逐批输出(推荐)', value: 'split' },
|
|
362
|
+
{ name: '合并为一个 Item(批次输出列表)', value: 'merge' },
|
|
363
|
+
],
|
|
364
|
+
displayOptions: { show: { '/r_autoChunkEnabled': [true] } },
|
|
365
|
+
description: '自动分批时的输出策略。',
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
displayName: '分批字段路径(相对 Argv)',
|
|
369
|
+
name: 's_chunkFieldPath',
|
|
370
|
+
type: 'string',
|
|
371
|
+
default: '写表行列表',
|
|
372
|
+
description: '例:写表行列表 或 data.写表行列表(从 argv 根开始)',
|
|
373
|
+
displayOptions: { show: { '/r_autoChunkEnabled': [true] } },
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
displayName: '分批最大并发数(同一 Item 内)',
|
|
377
|
+
name: 'u_chunkMaxConcurrency',
|
|
378
|
+
type: 'number',
|
|
379
|
+
default: 3,
|
|
380
|
+
typeOptions: { minValue: 1, maxValue: 20 },
|
|
381
|
+
displayOptions: { show: { '/r_autoChunkEnabled': [true] } },
|
|
382
|
+
description: '自动分批时,同一输入 item 内批次执行的最大并发数。',
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
displayName: '轮询抖动(毫秒)',
|
|
386
|
+
name: 'o_pollJitterMs',
|
|
387
|
+
type: 'number',
|
|
388
|
+
default: 300,
|
|
389
|
+
typeOptions: { minValue: 0 },
|
|
390
|
+
description: '在轮询间隔上叠加随机抖动,避免同一秒集中刷接口。',
|
|
391
|
+
},
|
|
188
392
|
{
|
|
189
393
|
displayName: '轮询间隔 (秒)',
|
|
190
394
|
name: 'd_pollIntervalSeconds',
|
|
@@ -192,6 +396,16 @@ class KingsoftAirscript {
|
|
|
192
396
|
default: 5,
|
|
193
397
|
typeOptions: { minValue: 1 },
|
|
194
398
|
displayOptions: { show: { '/operation': ['runScriptAsync'], '/c_waitForCompletion': [true] } },
|
|
399
|
+
description: '异步等待完成模式下的轮询间隔(秒)。',
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
displayName: '每批条数',
|
|
403
|
+
name: 't_chunkSize',
|
|
404
|
+
type: 'number',
|
|
405
|
+
default: 200,
|
|
406
|
+
typeOptions: { minValue: 1 },
|
|
407
|
+
displayOptions: { show: { '/r_autoChunkEnabled': [true] } },
|
|
408
|
+
description: '自动分批时,每批数组元素数量。',
|
|
195
409
|
},
|
|
196
410
|
{
|
|
197
411
|
displayName: '批处理数量',
|
|
@@ -200,6 +414,22 @@ class KingsoftAirscript {
|
|
|
200
414
|
default: 10,
|
|
201
415
|
typeOptions: { minValue: 1 },
|
|
202
416
|
displayOptions: { show: { '/a_parallelExecution': [true] } },
|
|
417
|
+
description: '并行执行开启时,每批并发处理的输入 item 数量。',
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
displayName: '请求超时(秒)',
|
|
421
|
+
name: 'h_requestTimeoutSeconds',
|
|
422
|
+
type: 'number',
|
|
423
|
+
default: 120,
|
|
424
|
+
typeOptions: { minValue: 5 },
|
|
425
|
+
description: '调用 sync_task/task 的 HTTP 超时(秒)。',
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
displayName: '失败判定:Finished 但 Error 不为空算失败',
|
|
429
|
+
name: 'n_failOnScriptError',
|
|
430
|
+
type: 'boolean',
|
|
431
|
+
default: true,
|
|
432
|
+
description: 'Whether enabled, treat `status=finished` with non-empty `error` as a failure',
|
|
203
433
|
},
|
|
204
434
|
{
|
|
205
435
|
displayName: '输出格式',
|
|
@@ -211,17 +441,55 @@ class KingsoftAirscript {
|
|
|
211
441
|
{ name: '完整响应', value: 'fullResponse' },
|
|
212
442
|
{ name: '仅结果', value: 'resultOnly' },
|
|
213
443
|
{ name: '拆分所有日志', value: 'splitLogs' },
|
|
214
|
-
{ name: '仅拆分错误日志', value: 'splitErrors' }
|
|
215
|
-
]
|
|
444
|
+
{ name: '仅拆分错误日志', value: 'splitErrors' },
|
|
445
|
+
],
|
|
216
446
|
},
|
|
217
447
|
{
|
|
218
448
|
displayName: '智能解析 Result',
|
|
219
449
|
name: 'g_parseResultString',
|
|
220
450
|
type: 'boolean',
|
|
221
451
|
default: false,
|
|
222
|
-
description: 'Whether
|
|
452
|
+
description: 'Whether enabled, if `result` is a JSON string it will be parsed automatically',
|
|
223
453
|
displayOptions: { show: { '/operation': ['runScriptSync', 'runScriptAsync'] } },
|
|
224
454
|
},
|
|
455
|
+
{
|
|
456
|
+
displayName: '重试初始等待(毫秒)',
|
|
457
|
+
name: 'k_retryInitialDelayMs',
|
|
458
|
+
type: 'number',
|
|
459
|
+
default: 800,
|
|
460
|
+
typeOptions: { minValue: 0 },
|
|
461
|
+
description: '重试的初始等待时间(毫秒)。',
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
displayName: '重试退避倍数',
|
|
465
|
+
name: 'l_retryBackoffFactor',
|
|
466
|
+
type: 'number',
|
|
467
|
+
default: 2,
|
|
468
|
+
typeOptions: { minValue: 1 },
|
|
469
|
+
description: '指数退避倍数(>=1)。',
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
displayName: '自动分批写入(按 Argv 内数组字段)',
|
|
473
|
+
name: 'r_autoChunkEnabled',
|
|
474
|
+
type: 'boolean',
|
|
475
|
+
default: false,
|
|
476
|
+
description: 'Whether enabled, split the array at the configured field path in argv and run the script in chunks',
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
displayName: '最大重试次数',
|
|
480
|
+
name: 'j_maxRetries',
|
|
481
|
+
type: 'number',
|
|
482
|
+
default: 2,
|
|
483
|
+
typeOptions: { minValue: 0, maxValue: 10 },
|
|
484
|
+
description: '对可恢复错误(超时/429/5xx 等)的最大重试次数。',
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
displayName: '尊重 Retry-After(429)',
|
|
488
|
+
name: 'm_honorRetryAfter',
|
|
489
|
+
type: 'boolean',
|
|
490
|
+
default: true,
|
|
491
|
+
description: 'Whether enabled, when receiving HTTP 429 with Retry-After header, wait accordingly before retrying',
|
|
492
|
+
},
|
|
225
493
|
],
|
|
226
494
|
},
|
|
227
495
|
],
|
|
@@ -229,12 +497,211 @@ class KingsoftAirscript {
|
|
|
229
497
|
};
|
|
230
498
|
}
|
|
231
499
|
async execute() {
|
|
232
|
-
var _a;
|
|
233
500
|
const 输入数据项列表 = this.getInputData();
|
|
234
|
-
const 凭证 = await this.getCredentials('kingsoftAirscriptApi');
|
|
235
|
-
const
|
|
501
|
+
const 凭证 = (await this.getCredentials('kingsoftAirscriptApi'));
|
|
502
|
+
const 等待毫秒 = async (ms) => {
|
|
503
|
+
await this.helpers.sleep(ms);
|
|
504
|
+
};
|
|
505
|
+
const 脱敏ID = (id) => {
|
|
506
|
+
const s = (id !== null && id !== void 0 ? id : '').toString();
|
|
507
|
+
if (s.length <= 10)
|
|
508
|
+
return s;
|
|
509
|
+
return `${s.slice(0, 4)}***${s.slice(-4)}`;
|
|
510
|
+
};
|
|
511
|
+
const 解析RetryAfter毫秒 = (v) => {
|
|
512
|
+
if (v === null || v === undefined)
|
|
513
|
+
return null;
|
|
514
|
+
const s = String(v).trim();
|
|
515
|
+
if (/^\d+$/.test(s))
|
|
516
|
+
return Number(s) * 1000;
|
|
517
|
+
const t = Date.parse(s);
|
|
518
|
+
if (Number.isFinite(t)) {
|
|
519
|
+
const diff = t - Date.now();
|
|
520
|
+
return diff > 0 ? diff : 0;
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
};
|
|
524
|
+
const 提取Http错误 = (e) => { var _a; return (_a = e) !== null && _a !== void 0 ? _a : {}; };
|
|
525
|
+
const 是IDataObject = (v) => {
|
|
526
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
527
|
+
};
|
|
528
|
+
const 提取错误消息 = (err) => {
|
|
529
|
+
var _a;
|
|
530
|
+
const data = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
531
|
+
if (是IDataObject(data)) {
|
|
532
|
+
const errorDetails = data.error_details;
|
|
533
|
+
if (是IDataObject(errorDetails)) {
|
|
534
|
+
const msg = errorDetails.msg;
|
|
535
|
+
if (typeof msg === 'string' && msg.trim())
|
|
536
|
+
return msg.trim();
|
|
537
|
+
}
|
|
538
|
+
const error = data.error;
|
|
539
|
+
if (typeof error === 'string' && error.trim())
|
|
540
|
+
return error.trim();
|
|
541
|
+
}
|
|
542
|
+
if (typeof err.message === 'string' && err.message.trim())
|
|
543
|
+
return err.message.trim();
|
|
544
|
+
return '未知错误';
|
|
545
|
+
};
|
|
546
|
+
const 判断是否可重试 = (e) => {
|
|
547
|
+
var _a;
|
|
548
|
+
const err = 提取Http错误(e);
|
|
549
|
+
const 错误码 = err.code;
|
|
550
|
+
const 状态码 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
551
|
+
return (['ETIMEDOUT', 'ECONNRESET', 'EAI_AGAIN', 'ECONNREFUSED', 'ECONNABORTED'].includes(String(错误码 !== null && 错误码 !== void 0 ? 错误码 : '')) ||
|
|
552
|
+
[408, 425, 429, 500, 502, 503, 504].includes(Number(状态码 !== null && 状态码 !== void 0 ? 状态码 : 0)));
|
|
553
|
+
};
|
|
554
|
+
const 带重试 = async (执行函数, 配置, 调试) => {
|
|
555
|
+
var _a, _b, _c, _d;
|
|
556
|
+
let 尝试 = 0;
|
|
557
|
+
while (true) {
|
|
558
|
+
try {
|
|
559
|
+
const res = await 执行函数();
|
|
560
|
+
if (调试)
|
|
561
|
+
调试.重试次数 = 尝试;
|
|
562
|
+
return res;
|
|
563
|
+
}
|
|
564
|
+
catch (e) {
|
|
565
|
+
const err = 提取Http错误(e);
|
|
566
|
+
const 状态码 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
567
|
+
if (!判断是否可重试(e) || 尝试 >= 配置.最大重试次数) {
|
|
568
|
+
if (调试)
|
|
569
|
+
调试.重试次数 = 尝试;
|
|
570
|
+
throw e;
|
|
571
|
+
}
|
|
572
|
+
let 等待 = Math.round(配置.初始等待毫秒 * Math.pow(配置.退避倍数, 尝试) + Math.random() * 200);
|
|
573
|
+
if (配置.尊重RetryAfter && 状态码 === 429) {
|
|
574
|
+
const headers = (_c = (_b = err.response) === null || _b === void 0 ? void 0 : _b.headers) !== null && _c !== void 0 ? _c : {};
|
|
575
|
+
const ra = (_d = headers['retry-after']) !== null && _d !== void 0 ? _d : headers['Retry-After'];
|
|
576
|
+
const raMs = 解析RetryAfter毫秒(ra);
|
|
577
|
+
if (raMs !== null)
|
|
578
|
+
等待 = Math.max(等待, raMs);
|
|
579
|
+
}
|
|
580
|
+
if (调试)
|
|
581
|
+
调试.最后等待毫秒 = 等待;
|
|
582
|
+
await 等待毫秒(等待);
|
|
583
|
+
尝试++;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
const 并发执行 = async (任务函数列表, 并发数) => {
|
|
588
|
+
const 结果 = new Array(任务函数列表.length);
|
|
589
|
+
let 下一个索引 = 0;
|
|
590
|
+
const 工人 = async () => {
|
|
591
|
+
while (true) {
|
|
592
|
+
const i = 下一个索引++;
|
|
593
|
+
if (i >= 任务函数列表.length)
|
|
594
|
+
break;
|
|
595
|
+
结果[i] = await 任务函数列表[i]();
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
const 工人数量 = Math.min(Math.max(1, 并发数), 任务函数列表.length);
|
|
599
|
+
await Promise.all(Array.from({ length: 工人数量 }, () => 工人()));
|
|
600
|
+
return 结果;
|
|
601
|
+
};
|
|
602
|
+
const 取路径值 = (obj, path) => {
|
|
603
|
+
if (!obj || !path)
|
|
604
|
+
return undefined;
|
|
605
|
+
const keys = String(path).split('.').filter(Boolean);
|
|
606
|
+
let cur = obj;
|
|
607
|
+
for (const k of keys) {
|
|
608
|
+
if (cur === null || cur === undefined || typeof cur !== 'object')
|
|
609
|
+
return undefined;
|
|
610
|
+
cur = cur[k];
|
|
611
|
+
}
|
|
612
|
+
return cur;
|
|
613
|
+
};
|
|
614
|
+
const 以不可变方式写入路径 = (obj, path, value) => {
|
|
615
|
+
const keys = String(path).split('.').filter(Boolean);
|
|
616
|
+
if (keys.length === 0)
|
|
617
|
+
return obj;
|
|
618
|
+
const 递归 = (cur, idx) => {
|
|
619
|
+
const k = keys[idx];
|
|
620
|
+
const isLast = idx === keys.length - 1;
|
|
621
|
+
const base = (cur && typeof cur === 'object') ? cur : {};
|
|
622
|
+
const clone = { ...base };
|
|
623
|
+
if (isLast) {
|
|
624
|
+
clone[k] = value;
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
clone[k] = 递归(base[k], idx + 1);
|
|
628
|
+
}
|
|
629
|
+
return clone;
|
|
630
|
+
};
|
|
631
|
+
return 递归(obj, 0);
|
|
632
|
+
};
|
|
633
|
+
const 读取高级选项 = (索引) => {
|
|
634
|
+
const raw = this.getNodeParameter('options', 索引, {});
|
|
635
|
+
const 取数 = (k, d) => {
|
|
636
|
+
const v = raw[k];
|
|
637
|
+
const n = typeof v === 'number' ? v : Number(v);
|
|
638
|
+
return Number.isFinite(n) ? n : d;
|
|
639
|
+
};
|
|
640
|
+
const 取布尔 = (k, d) => {
|
|
641
|
+
const v = raw[k];
|
|
642
|
+
return typeof v === 'boolean' ? v : d;
|
|
643
|
+
};
|
|
644
|
+
const 取字符串 = (k, d) => {
|
|
645
|
+
const v = raw[k];
|
|
646
|
+
return typeof v === 'string' ? v : d;
|
|
647
|
+
};
|
|
648
|
+
return {
|
|
649
|
+
a_parallelExecution: 取布尔('a_parallelExecution', false),
|
|
650
|
+
b_timeoutSeconds: 取数('b_timeoutSeconds', 300),
|
|
651
|
+
c_waitForCompletion: 取布尔('c_waitForCompletion', false),
|
|
652
|
+
d_pollIntervalSeconds: 取数('d_pollIntervalSeconds', 5),
|
|
653
|
+
e_batchSize: 取数('e_batchSize', 10),
|
|
654
|
+
f_outputFormat: 取字符串('f_outputFormat', 'fullResponse'),
|
|
655
|
+
g_parseResultString: 取布尔('g_parseResultString', false),
|
|
656
|
+
h_requestTimeoutSeconds: 取数('h_requestTimeoutSeconds', 120),
|
|
657
|
+
i_statusTimeoutSeconds: 取数('i_statusTimeoutSeconds', 30),
|
|
658
|
+
j_maxRetries: 取数('j_maxRetries', 2),
|
|
659
|
+
k_retryInitialDelayMs: 取数('k_retryInitialDelayMs', 800),
|
|
660
|
+
l_retryBackoffFactor: 取数('l_retryBackoffFactor', 2),
|
|
661
|
+
m_honorRetryAfter: 取布尔('m_honorRetryAfter', true),
|
|
662
|
+
n_failOnScriptError: 取布尔('n_failOnScriptError', true),
|
|
663
|
+
o_pollJitterMs: 取数('o_pollJitterMs', 300),
|
|
664
|
+
r_autoChunkEnabled: 取布尔('r_autoChunkEnabled', false),
|
|
665
|
+
s_chunkFieldPath: 取字符串('s_chunkFieldPath', '写表行列表'),
|
|
666
|
+
t_chunkSize: 取数('t_chunkSize', 200),
|
|
667
|
+
u_chunkMaxConcurrency: 取数('u_chunkMaxConcurrency', 3),
|
|
668
|
+
v_chunkOutputMode: 取字符串('v_chunkOutputMode', 'split'),
|
|
669
|
+
w_debug: 取布尔('w_debug', false),
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
const 提取脚本错误消息 = (响应) => {
|
|
673
|
+
var _a;
|
|
674
|
+
const error = String((_a = 响应.error) !== null && _a !== void 0 ? _a : '').trim();
|
|
675
|
+
if (error)
|
|
676
|
+
return error;
|
|
677
|
+
const details = 响应.error_details;
|
|
678
|
+
if (details && typeof details === 'object' && !Array.isArray(details)) {
|
|
679
|
+
const msg = details.msg;
|
|
680
|
+
if (typeof msg === 'string' && msg.trim())
|
|
681
|
+
return msg.trim();
|
|
682
|
+
}
|
|
683
|
+
return '';
|
|
684
|
+
};
|
|
685
|
+
const 校验脚本是否失败 = (响应, itemIndex, 开启) => {
|
|
686
|
+
if (!开启)
|
|
687
|
+
return;
|
|
688
|
+
const status = typeof 响应.status === 'string' ? 响应.status : '';
|
|
689
|
+
const errorMsg = 提取脚本错误消息(响应);
|
|
690
|
+
if (status === 'finished' && errorMsg) {
|
|
691
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `脚本执行失败:${errorMsg}`, { itemIndex });
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
const 提取TaskId = (响应) => {
|
|
695
|
+
var _a, _b;
|
|
696
|
+
const fromData = (_a = 响应.data) === null || _a === void 0 ? void 0 : _a.task_id;
|
|
697
|
+
const fromTop = 响应.task_id;
|
|
698
|
+
return String((_b = fromData !== null && fromData !== void 0 ? fromData : fromTop) !== null && _b !== void 0 ? _b : '').trim();
|
|
699
|
+
};
|
|
700
|
+
const 解析执行信息 = (itemIndex) => {
|
|
701
|
+
var _a;
|
|
236
702
|
const id输入模式 = this.getNodeParameter('idInputMode', itemIndex, 'url');
|
|
237
|
-
let 文件ID = ''
|
|
703
|
+
let 文件ID = '';
|
|
704
|
+
let 脚本ID = '';
|
|
238
705
|
if (id输入模式 === 'url') {
|
|
239
706
|
const webhook链接 = this.getNodeParameter('webhookUrl', itemIndex, '');
|
|
240
707
|
try {
|
|
@@ -250,7 +717,8 @@ class KingsoftAirscript {
|
|
|
250
717
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook 链接格式不正确,无法解析出 File ID 和 Script ID。', { itemIndex });
|
|
251
718
|
}
|
|
252
719
|
}
|
|
253
|
-
catch {
|
|
720
|
+
catch (e) {
|
|
721
|
+
void e;
|
|
254
722
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook 链接格式不正确,无法解析出 File ID 和 Script ID。', { itemIndex });
|
|
255
723
|
}
|
|
256
724
|
}
|
|
@@ -261,230 +729,391 @@ class KingsoftAirscript {
|
|
|
261
729
|
if (!文件ID || !脚本ID) {
|
|
262
730
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), '未能获取到有效的 File ID 和 Script ID。', { itemIndex });
|
|
263
731
|
}
|
|
264
|
-
const
|
|
732
|
+
const argvTemplate = this.getNodeParameter('argvTemplate', itemIndex, 'custom');
|
|
265
733
|
const 可选上下文参数 = this.getNodeParameter('contextParameters', itemIndex, {});
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
else {
|
|
284
|
-
argv参数 = {};
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
else if (typeof argv字符串 === 'string') {
|
|
734
|
+
const 模板到字段名 = {
|
|
735
|
+
custom: 'argv_custom',
|
|
736
|
+
basicInput: 'argv_basicInput',
|
|
737
|
+
attachment: 'argv_attachment',
|
|
738
|
+
cascade: 'argv_cascade',
|
|
739
|
+
migrate: 'argv_migrate',
|
|
740
|
+
formula: 'argv_formula',
|
|
741
|
+
setLinkProperties: 'argv_setLinkProperties',
|
|
742
|
+
setCascadeOptions: 'argv_setCascadeOptions',
|
|
743
|
+
n8nExpression: 'argv_n8nExpression',
|
|
744
|
+
compositeKey: 'argv_compositeKey',
|
|
745
|
+
createSheet: 'argv_createSheet',
|
|
746
|
+
};
|
|
747
|
+
const argv字段名 = (_a = 模板到字段名[argvTemplate]) !== null && _a !== void 0 ? _a : 'argv_custom';
|
|
748
|
+
const argv原始 = this.getNodeParameter(argv字段名, itemIndex, {});
|
|
749
|
+
let argv参数 = {};
|
|
750
|
+
if (typeof argv原始 === 'string') {
|
|
288
751
|
try {
|
|
289
|
-
argv参数 = JSON.parse(argv
|
|
752
|
+
argv参数 = JSON.parse(argv原始);
|
|
290
753
|
}
|
|
291
|
-
catch {
|
|
292
|
-
|
|
754
|
+
catch (e) {
|
|
755
|
+
void e;
|
|
756
|
+
argv参数 = { value: argv原始 };
|
|
293
757
|
}
|
|
294
758
|
}
|
|
295
|
-
else {
|
|
296
|
-
argv参数 =
|
|
759
|
+
else if (argv原始 && typeof argv原始 === 'object' && !Array.isArray(argv原始)) {
|
|
760
|
+
argv参数 = argv原始;
|
|
297
761
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
762
|
+
for (const key in argv参数) {
|
|
763
|
+
if (!Object.prototype.hasOwnProperty.call(argv参数, key))
|
|
764
|
+
continue;
|
|
765
|
+
const value = argv参数[key];
|
|
766
|
+
if (typeof value !== 'string')
|
|
767
|
+
continue;
|
|
768
|
+
const trimmed = value.trim();
|
|
769
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
770
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
771
|
+
try {
|
|
772
|
+
argv参数[key] = JSON.parse(trimmed);
|
|
773
|
+
}
|
|
774
|
+
catch (e) {
|
|
775
|
+
void e;
|
|
304
776
|
}
|
|
305
777
|
}
|
|
306
|
-
return obj;
|
|
307
|
-
}
|
|
308
|
-
if (typeof argv参数 === 'object' && argv参数 !== null) {
|
|
309
|
-
argv参数 = 字符串化嵌套对象(argv参数);
|
|
310
778
|
}
|
|
779
|
+
const 规范化字符串 = (v) => (typeof v === 'string' ? v.trim() : '');
|
|
780
|
+
const 从Argv取 = (snake, camel) => {
|
|
781
|
+
const v1 = 规范化字符串(argv参数[snake]);
|
|
782
|
+
if (v1)
|
|
783
|
+
return v1;
|
|
784
|
+
if (camel) {
|
|
785
|
+
const v2 = 规范化字符串(argv参数[camel]);
|
|
786
|
+
if (v2)
|
|
787
|
+
return v2;
|
|
788
|
+
}
|
|
789
|
+
return '';
|
|
790
|
+
};
|
|
791
|
+
const 从UI取 = (key) => 规范化字符串(可选上下文参数[key]);
|
|
792
|
+
const 最终_sheet_name = 从Argv取('sheet_name', 'sheetName') || 从UI取('sheetName');
|
|
793
|
+
const 最终_range = 从Argv取('range') || 从UI取('range');
|
|
794
|
+
const 最终_link_from = 从Argv取('link_from', 'linkFrom') || 从UI取('linkFrom');
|
|
795
|
+
const 最终_db_active_view = 从Argv取('db_active_view', 'dbActiveView') || 从UI取('dbActiveView');
|
|
796
|
+
const 最终_db_selection = 从Argv取('db_selection', 'dbSelection') || 从UI取('dbSelection');
|
|
797
|
+
if (最终_sheet_name)
|
|
798
|
+
argv参数.sheet_name = 最终_sheet_name;
|
|
799
|
+
if (最终_range)
|
|
800
|
+
argv参数.range = 最终_range;
|
|
801
|
+
if (最终_link_from)
|
|
802
|
+
argv参数.link_from = 最终_link_from;
|
|
803
|
+
if (最终_db_active_view)
|
|
804
|
+
argv参数.db_active_view = 最终_db_active_view;
|
|
805
|
+
if (最终_db_selection)
|
|
806
|
+
argv参数.db_selection = 最终_db_selection;
|
|
311
807
|
const 上下文 = { argv: argv参数 };
|
|
312
|
-
if (
|
|
313
|
-
上下文.sheet_name =
|
|
314
|
-
if (
|
|
315
|
-
上下文.range =
|
|
316
|
-
if (
|
|
317
|
-
上下文.link_from =
|
|
318
|
-
if (
|
|
319
|
-
上下文.db_active_view =
|
|
320
|
-
if (
|
|
321
|
-
上下文.db_selection =
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
808
|
+
if (最终_sheet_name)
|
|
809
|
+
上下文.sheet_name = 最终_sheet_name;
|
|
810
|
+
if (最终_range)
|
|
811
|
+
上下文.range = 最终_range;
|
|
812
|
+
if (最终_link_from)
|
|
813
|
+
上下文.link_from = 最终_link_from;
|
|
814
|
+
if (最终_db_active_view)
|
|
815
|
+
上下文.db_active_view = 最终_db_active_view;
|
|
816
|
+
if (最终_db_selection)
|
|
817
|
+
上下文.db_selection = 最终_db_selection;
|
|
818
|
+
return { 文件ID, 脚本ID, argv参数, 上下文 };
|
|
819
|
+
};
|
|
820
|
+
const 调用脚本接口 = async (参数) => {
|
|
821
|
+
var _a, _b;
|
|
822
|
+
const endpoint = 参数.类型 === 'sync' ? API_CONFIG.ENDPOINTS.SYNC_TASK : API_CONFIG.ENDPOINTS.ASYNC_TASK;
|
|
823
|
+
const 请求URL = `${API_CONFIG.BASE_URL}${endpoint}`
|
|
824
|
+
.replace(':file_id', 参数.文件ID)
|
|
825
|
+
.replace(':script_id', 参数.脚本ID);
|
|
826
|
+
const t0 = Date.now();
|
|
827
|
+
const 调试统计 = {};
|
|
828
|
+
const options = {
|
|
325
829
|
method: 'POST',
|
|
326
830
|
url: 请求URL,
|
|
327
|
-
headers: {
|
|
328
|
-
|
|
831
|
+
headers: {
|
|
832
|
+
'Content-Type': 'application/json',
|
|
833
|
+
'AirScript-Token': 凭证.apiToken,
|
|
834
|
+
},
|
|
835
|
+
body: { Context: 参数.上下文 },
|
|
329
836
|
json: true,
|
|
330
|
-
|
|
837
|
+
timeout: 参数.请求超时毫秒,
|
|
838
|
+
};
|
|
839
|
+
const res = (await 带重试(async () => (await this.helpers.httpRequest(options)), 参数.重试, 调试统计));
|
|
840
|
+
if (参数.调试) {
|
|
841
|
+
参数.调试['执行脚本'] = {
|
|
842
|
+
url: `/api/v3/ide/file/${脱敏ID(参数.文件ID)}/script/${脱敏ID(参数.脚本ID)}/${参数.类型 === 'sync' ? 'sync_task' : 'task'}`,
|
|
843
|
+
耗时毫秒: Date.now() - t0,
|
|
844
|
+
重试次数: (_a = 调试统计.重试次数) !== null && _a !== void 0 ? _a : 0,
|
|
845
|
+
最后等待毫秒: (_b = 调试统计.最后等待毫秒) !== null && _b !== void 0 ? _b : null,
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
return res;
|
|
331
849
|
};
|
|
332
|
-
const 获取任务状态 = async (
|
|
333
|
-
|
|
850
|
+
const 获取任务状态 = async (参数) => {
|
|
851
|
+
var _a, _b;
|
|
852
|
+
const 请求URL = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.GET_TASK}`;
|
|
853
|
+
const t0 = Date.now();
|
|
854
|
+
const 调试统计 = {};
|
|
855
|
+
const taskIdEncoded = encodeURIComponent(参数.taskId);
|
|
856
|
+
const options = {
|
|
334
857
|
method: 'GET',
|
|
335
|
-
url:
|
|
336
|
-
headers: { 'Content-Type': 'application/json' },
|
|
337
|
-
qs: { task_id:
|
|
858
|
+
url: 请求URL,
|
|
859
|
+
headers: { 'Content-Type': 'application/json', 'AirScript-Token': 凭证.apiToken },
|
|
860
|
+
qs: { task_id: taskIdEncoded },
|
|
338
861
|
json: true,
|
|
862
|
+
timeout: 参数.状态超时毫秒,
|
|
863
|
+
};
|
|
864
|
+
const res = (await 带重试(async () => (await this.helpers.httpRequest(options)), 参数.重试, 调试统计));
|
|
865
|
+
if (参数.调试) {
|
|
866
|
+
参数.调试['获取任务状态'] = {
|
|
867
|
+
task_id: 脱敏ID(参数.taskId),
|
|
868
|
+
耗时毫秒: Date.now() - t0,
|
|
869
|
+
重试次数: (_a = 调试统计.重试次数) !== null && _a !== void 0 ? _a : 0,
|
|
870
|
+
最后等待毫秒: (_b = 调试统计.最后等待毫秒) !== null && _b !== void 0 ? _b : null,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
return res;
|
|
874
|
+
};
|
|
875
|
+
const 等待异步任务完成 = async (参数) => {
|
|
876
|
+
const 截止时间 = Date.now() + 参数.总等待超时毫秒;
|
|
877
|
+
let 首次 = true;
|
|
878
|
+
let 轮询次数 = 0;
|
|
879
|
+
while (Date.now() < 截止时间) {
|
|
880
|
+
if (!首次) {
|
|
881
|
+
const 抖动 = 参数.轮询抖动毫秒 > 0 ? Math.floor(Math.random() * 参数.轮询抖动毫秒) : 0;
|
|
882
|
+
await 等待毫秒(参数.轮询间隔毫秒 + 抖动);
|
|
883
|
+
}
|
|
884
|
+
首次 = false;
|
|
885
|
+
const 状态响应 = await 获取任务状态({
|
|
886
|
+
taskId: 参数.taskId,
|
|
887
|
+
状态超时毫秒: 参数.状态超时毫秒,
|
|
888
|
+
重试: 参数.重试,
|
|
889
|
+
调试: 参数.调试,
|
|
890
|
+
});
|
|
891
|
+
const 状态 = typeof 状态响应.status === 'string' ? 状态响应.status : '';
|
|
892
|
+
if (状态 === 'finished') {
|
|
893
|
+
校验脚本是否失败(状态响应, 参数.itemIndex, 参数.finished但error算失败);
|
|
894
|
+
if (参数.调试)
|
|
895
|
+
参数.调试['轮询次数'] = (轮询次数 + 1);
|
|
896
|
+
return 状态响应;
|
|
897
|
+
}
|
|
898
|
+
轮询次数++;
|
|
899
|
+
}
|
|
900
|
+
if (参数.调试)
|
|
901
|
+
参数.调试['轮询次数'] = 轮询次数;
|
|
902
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `异步任务等待超时(${Math.round(参数.总等待超时毫秒 / 1000)}秒)`, {
|
|
903
|
+
itemIndex: 参数.itemIndex,
|
|
339
904
|
});
|
|
340
905
|
};
|
|
341
|
-
const
|
|
342
|
-
var _a, _b, _c, _d
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (操作类型 === 'runScriptSync') {
|
|
348
|
-
响应数据 = await 执行脚本(索引, 'sync');
|
|
906
|
+
const 格式化输出 = (响应数据, 索引, opt, 附加) => {
|
|
907
|
+
var _a, _b, _c, _d;
|
|
908
|
+
const data = ((_a = 响应数据.data) !== null && _a !== void 0 ? _a : undefined);
|
|
909
|
+
if (opt.g_parseResultString && data && typeof data.result === 'string') {
|
|
910
|
+
try {
|
|
911
|
+
data.result = JSON.parse(data.result);
|
|
349
912
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `异步任务等待超时(${超时时间 / 1000}秒)。`, { itemIndex: 索引 });
|
|
913
|
+
catch (e) {
|
|
914
|
+
void e;
|
|
915
|
+
data.result = data.result.replace(/\\n/g, '\n');
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
const out = [];
|
|
919
|
+
if (opt.f_outputFormat === 'resultOnly') {
|
|
920
|
+
out.push({
|
|
921
|
+
json: {
|
|
922
|
+
...(附加 !== null && 附加 !== void 0 ? 附加 : {}),
|
|
923
|
+
result: (_b = data === null || data === void 0 ? void 0 : data.result) !== null && _b !== void 0 ? _b : null,
|
|
924
|
+
status: ((_c = 响应数据.status) !== null && _c !== void 0 ? _c : null),
|
|
925
|
+
error: ((_d = 响应数据.error) !== null && _d !== void 0 ? _d : ''),
|
|
926
|
+
},
|
|
927
|
+
pairedItem: { item: 索引 },
|
|
928
|
+
});
|
|
929
|
+
return out;
|
|
930
|
+
}
|
|
931
|
+
if (opt.f_outputFormat === 'splitLogs') {
|
|
932
|
+
const logs = data === null || data === void 0 ? void 0 : data.logs;
|
|
933
|
+
if (Array.isArray(logs)) {
|
|
934
|
+
for (const 日志条目 of logs) {
|
|
935
|
+
if (日志条目 && typeof 日志条目 === 'object') {
|
|
936
|
+
out.push({ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), ...日志条目 }, pairedItem: { item: 索引 } });
|
|
375
937
|
}
|
|
376
938
|
}
|
|
377
939
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
catch {
|
|
390
|
-
if (data.result) {
|
|
391
|
-
data.result = data.result.replace(/\\n/g, '\n');
|
|
392
|
-
}
|
|
940
|
+
return out;
|
|
941
|
+
}
|
|
942
|
+
if (opt.f_outputFormat === 'splitErrors') {
|
|
943
|
+
const logs = data === null || data === void 0 ? void 0 : data.logs;
|
|
944
|
+
if (Array.isArray(logs)) {
|
|
945
|
+
for (const 日志条目 of logs) {
|
|
946
|
+
if (日志条目 && typeof 日志条目 === 'object') {
|
|
947
|
+
const level = 日志条目.level;
|
|
948
|
+
if (level === 'error')
|
|
949
|
+
out.push({ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), ...日志条目 }, pairedItem: { item: 索引 } });
|
|
393
950
|
}
|
|
394
951
|
}
|
|
395
|
-
switch (高级选项.f_outputFormat) {
|
|
396
|
-
case 'resultOnly':
|
|
397
|
-
单项的返回数据列表.push({ json: { result: (_d = data === null || data === void 0 ? void 0 : data.result) !== null && _d !== void 0 ? _d : null }, pairedItem: { item: 索引 } });
|
|
398
|
-
break;
|
|
399
|
-
case 'splitLogs':
|
|
400
|
-
if ((data === null || data === void 0 ? void 0 : data.logs) && Array.isArray(data.logs)) {
|
|
401
|
-
data.logs.forEach((日志条目) => 单项的返回数据列表.push({ json: 日志条目, pairedItem: { item: 索引 } }));
|
|
402
|
-
}
|
|
403
|
-
break;
|
|
404
|
-
case 'splitErrors':
|
|
405
|
-
if ((data === null || data === void 0 ? void 0 : data.logs) && Array.isArray(data.logs)) {
|
|
406
|
-
data.logs
|
|
407
|
-
.filter((log) => log.level === 'error')
|
|
408
|
-
.forEach((日志条目) => 单项的返回数据列表.push({ json: 日志条目, pairedItem: { item: 索引 } }));
|
|
409
|
-
}
|
|
410
|
-
break;
|
|
411
|
-
default:
|
|
412
|
-
单项的返回数据列表.push({ json: 响应数据, pairedItem: { item: 索引 } });
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
952
|
}
|
|
416
|
-
return
|
|
953
|
+
return out;
|
|
954
|
+
}
|
|
955
|
+
out.push({ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), ...响应数据 }, pairedItem: { item: 索引 } });
|
|
956
|
+
return out;
|
|
957
|
+
};
|
|
958
|
+
const 处理单个项目 = async (索引) => {
|
|
959
|
+
const 操作类型 = this.getNodeParameter('operation', 索引, 'runScriptSync');
|
|
960
|
+
const opt = 读取高级选项(索引);
|
|
961
|
+
const 重试 = {
|
|
962
|
+
最大重试次数: opt.j_maxRetries,
|
|
963
|
+
初始等待毫秒: opt.k_retryInitialDelayMs,
|
|
964
|
+
退避倍数: opt.l_retryBackoffFactor,
|
|
965
|
+
尊重RetryAfter: opt.m_honorRetryAfter,
|
|
966
|
+
};
|
|
967
|
+
const 调试字段 = opt.w_debug ? {} : undefined;
|
|
968
|
+
if (操作类型 === 'getTaskStatus') {
|
|
969
|
+
const taskId = this.getNodeParameter('taskId', 索引, '');
|
|
970
|
+
const 状态响应 = await 获取任务状态({
|
|
971
|
+
taskId,
|
|
972
|
+
状态超时毫秒: opt.i_statusTimeoutSeconds * 1000,
|
|
973
|
+
重试,
|
|
974
|
+
调试: 调试字段,
|
|
975
|
+
});
|
|
976
|
+
校验脚本是否失败(状态响应, 索引, opt.n_failOnScriptError);
|
|
977
|
+
const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
|
|
978
|
+
return 格式化输出(状态响应, 索引, opt, 附加);
|
|
417
979
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
980
|
+
const { 文件ID, 脚本ID, argv参数, 上下文 } = 解析执行信息(索引);
|
|
981
|
+
const 执行一次 = async (上下文入参, 调试对象) => {
|
|
982
|
+
if (操作类型 === 'runScriptSync') {
|
|
983
|
+
const 响应 = await 调用脚本接口({
|
|
984
|
+
类型: 'sync',
|
|
985
|
+
文件ID,
|
|
986
|
+
脚本ID,
|
|
987
|
+
上下文: 上下文入参,
|
|
988
|
+
请求超时毫秒: opt.h_requestTimeoutSeconds * 1000,
|
|
989
|
+
重试,
|
|
990
|
+
调试: 调试对象,
|
|
991
|
+
});
|
|
992
|
+
校验脚本是否失败(响应, 索引, opt.n_failOnScriptError);
|
|
993
|
+
return 响应;
|
|
994
|
+
}
|
|
995
|
+
if (操作类型 === 'runScriptAsync') {
|
|
996
|
+
const 初始响应 = await 调用脚本接口({
|
|
997
|
+
类型: 'async',
|
|
998
|
+
文件ID,
|
|
999
|
+
脚本ID,
|
|
1000
|
+
上下文: 上下文入参,
|
|
1001
|
+
请求超时毫秒: opt.h_requestTimeoutSeconds * 1000,
|
|
1002
|
+
重试,
|
|
1003
|
+
调试: 调试对象,
|
|
1004
|
+
});
|
|
1005
|
+
if (!opt.c_waitForCompletion) {
|
|
1006
|
+
return 初始响应;
|
|
424
1007
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
错误详情 = httpError.response.data;
|
|
429
|
-
错误消息 = ((_f = httpError.response.data.error_details) === null || _f === void 0 ? void 0 : _f.msg) ||
|
|
430
|
-
httpError.response.data.error ||
|
|
431
|
-
httpError.message ||
|
|
432
|
-
错误消息;
|
|
433
|
-
}
|
|
1008
|
+
const taskId = 提取TaskId(初始响应);
|
|
1009
|
+
if (!taskId) {
|
|
1010
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), '异步执行返回的响应中未找到 task_id(data.task_id 或 task_id)。', { itemIndex: 索引 });
|
|
434
1011
|
}
|
|
435
|
-
return
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
1012
|
+
return await 等待异步任务完成({
|
|
1013
|
+
itemIndex: 索引,
|
|
1014
|
+
taskId,
|
|
1015
|
+
轮询间隔毫秒: opt.d_pollIntervalSeconds * 1000,
|
|
1016
|
+
轮询抖动毫秒: opt.o_pollJitterMs,
|
|
1017
|
+
总等待超时毫秒: opt.b_timeoutSeconds * 1000,
|
|
1018
|
+
状态超时毫秒: opt.i_statusTimeoutSeconds * 1000,
|
|
1019
|
+
重试,
|
|
1020
|
+
finished但error算失败: opt.n_failOnScriptError,
|
|
1021
|
+
调试: 调试对象,
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `未知的操作类型: ${操作类型}`, { itemIndex: 索引 });
|
|
1025
|
+
};
|
|
1026
|
+
if (opt.r_autoChunkEnabled) {
|
|
1027
|
+
const 数组路径 = opt.s_chunkFieldPath;
|
|
1028
|
+
const 目标数组 = 取路径值(argv参数, 数组路径);
|
|
1029
|
+
if (!Array.isArray(目标数组)) {
|
|
1030
|
+
const 响应 = await 执行一次(上下文, 调试字段);
|
|
1031
|
+
const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
|
|
1032
|
+
return 格式化输出(响应, 索引, opt, 附加);
|
|
1033
|
+
}
|
|
1034
|
+
const 批次大小 = Math.max(1, opt.t_chunkSize);
|
|
1035
|
+
const 批次列表 = [];
|
|
1036
|
+
for (let i = 0; i < 目标数组.length; i += 批次大小) {
|
|
1037
|
+
批次列表.push(目标数组.slice(i, i + 批次大小));
|
|
1038
|
+
}
|
|
1039
|
+
if (批次列表.length === 0) {
|
|
1040
|
+
const 响应 = await 执行一次(上下文, 调试字段);
|
|
1041
|
+
const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
|
|
1042
|
+
return 格式化输出(响应, 索引, opt, 附加);
|
|
1043
|
+
}
|
|
1044
|
+
const 并发数 = Math.max(1, opt.u_chunkMaxConcurrency);
|
|
1045
|
+
const 任务列表 = 批次列表.map((批次, 批次索引) => async () => {
|
|
1046
|
+
const 批次调试对象 = opt.w_debug ? {} : undefined;
|
|
1047
|
+
const 批次上下文 = {
|
|
1048
|
+
...上下文,
|
|
1049
|
+
argv: 以不可变方式写入路径(argv参数, 数组路径, 批次),
|
|
1050
|
+
};
|
|
1051
|
+
const 响应 = await 执行一次(批次上下文, 批次调试对象);
|
|
1052
|
+
const 批次附加 = opt.w_debug
|
|
1053
|
+
? { _调试: { ...(批次调试对象 !== null && 批次调试对象 !== void 0 ? 批次调试对象 : {}), 批次索引 } }
|
|
1054
|
+
: { 批次索引 };
|
|
1055
|
+
return 格式化输出(响应, 索引, opt, 批次附加);
|
|
1056
|
+
});
|
|
1057
|
+
const 批次结果 = await 并发执行(任务列表, 并发数);
|
|
1058
|
+
if (opt.v_chunkOutputMode === 'merge') {
|
|
1059
|
+
const 合并结果 = {
|
|
1060
|
+
批次输出列表: 批次结果.flat().map(x => x.json),
|
|
1061
|
+
...(opt.w_debug ? { _调试: 调试字段 } : {}),
|
|
1062
|
+
};
|
|
1063
|
+
return [{ json: 合并结果, pairedItem: { item: 索引 } }];
|
|
1064
|
+
}
|
|
1065
|
+
else {
|
|
1066
|
+
return 批次结果.flat();
|
|
442
1067
|
}
|
|
443
|
-
|
|
1068
|
+
}
|
|
1069
|
+
else {
|
|
1070
|
+
const 响应 = await 执行一次(上下文, 调试字段);
|
|
1071
|
+
const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
|
|
1072
|
+
return 格式化输出(响应, 索引, opt, 附加);
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
const 安全执行单个项目 = async (itemIndex) => {
|
|
1076
|
+
var _a, _b, _c, _d, _e;
|
|
1077
|
+
try {
|
|
1078
|
+
return await 处理单个项目(itemIndex);
|
|
1079
|
+
}
|
|
1080
|
+
catch (e) {
|
|
1081
|
+
if (!this.continueOnFail())
|
|
1082
|
+
throw e;
|
|
1083
|
+
const err = 提取Http错误(e);
|
|
1084
|
+
return [
|
|
1085
|
+
{
|
|
1086
|
+
json: {
|
|
1087
|
+
error: 提取错误消息(err),
|
|
1088
|
+
code: (_a = err.code) !== null && _a !== void 0 ? _a : null,
|
|
1089
|
+
statusCode: (_c = (_b = err.response) === null || _b === void 0 ? void 0 : _b.status) !== null && _c !== void 0 ? _c : null,
|
|
1090
|
+
details: (_e = (_d = err.response) === null || _d === void 0 ? void 0 : _d.data) !== null && _e !== void 0 ? _e : null,
|
|
1091
|
+
},
|
|
1092
|
+
pairedItem: { item: itemIndex },
|
|
1093
|
+
},
|
|
1094
|
+
];
|
|
444
1095
|
}
|
|
445
1096
|
};
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
if (
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
1097
|
+
const opt = 读取高级选项(0);
|
|
1098
|
+
const 汇总输出 = [];
|
|
1099
|
+
if (opt.a_parallelExecution) {
|
|
1100
|
+
const 批处理数量 = Math.max(1, opt.e_batchSize);
|
|
1101
|
+
for (let i = 0; i < 输入数据项列表.length; i += 批处理数量) {
|
|
1102
|
+
const 索引列表 = 输入数据项列表.slice(i, i + 批处理数量).map((_, localIndex) => i + localIndex);
|
|
1103
|
+
const 任务函数列表 = 索引列表.map((idx) => () => 安全执行单个项目(idx));
|
|
1104
|
+
const 批次结果 = await 并发执行(任务函数列表, 批处理数量);
|
|
1105
|
+
for (const 单项结果 of 批次结果) {
|
|
1106
|
+
汇总输出.push(...单项结果);
|
|
1107
|
+
}
|
|
452
1108
|
}
|
|
453
1109
|
}
|
|
454
1110
|
else {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const 批处理Promises = 当前批次的索引范围.map(真实索引 => 处理单个项目(真实索引));
|
|
459
|
-
const 批处理结果 = await Promise.allSettled(批处理Promises);
|
|
460
|
-
批处理结果.forEach((result, 本地索引) => {
|
|
461
|
-
var _a, _b, _c, _d, _e, _f;
|
|
462
|
-
const 真实索引 = i + 本地索引;
|
|
463
|
-
if (result.status === 'fulfilled') {
|
|
464
|
-
返回数据列表.push(...result.value);
|
|
465
|
-
}
|
|
466
|
-
else {
|
|
467
|
-
if (!this.continueOnFail()) {
|
|
468
|
-
const originalError = result.reason;
|
|
469
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), originalError, { itemIndex: 真实索引 });
|
|
470
|
-
}
|
|
471
|
-
const typedReason = result.reason;
|
|
472
|
-
const 错误消息 = ((_c = (_b = (_a = typedReason.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error_details) === null || _c === void 0 ? void 0 : _c.msg) ||
|
|
473
|
-
((_e = (_d = typedReason.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.error) ||
|
|
474
|
-
typedReason.message ||
|
|
475
|
-
'未知错误';
|
|
476
|
-
返回数据列表.push({
|
|
477
|
-
json: {
|
|
478
|
-
error: 错误消息,
|
|
479
|
-
details: (_f = typedReason.response) === null || _f === void 0 ? void 0 : _f.data
|
|
480
|
-
},
|
|
481
|
-
pairedItem: { item: 真实索引 }
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
});
|
|
1111
|
+
for (let i = 0; i < 输入数据项列表.length; i++) {
|
|
1112
|
+
const 单项结果 = await 安全执行单个项目(i);
|
|
1113
|
+
汇总输出.push(...单项结果);
|
|
485
1114
|
}
|
|
486
1115
|
}
|
|
487
|
-
return [
|
|
1116
|
+
return [汇总输出];
|
|
488
1117
|
}
|
|
489
1118
|
}
|
|
490
1119
|
exports.KingsoftAirscript = KingsoftAirscript;
|