@steedos-labs/plugin-workflow 3.0.14 → 3.0.16
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_TEMPLATE.md +222 -0
- package/convert-templates.js +51 -0
- package/main/default/objects/flows/flows.object.yml +16 -0
- package/main/default/objects/instance_tasks/buttons/instance_new.button.yml +9 -9
- package/main/default/objects/instances/buttons/instance_delete_many.button.yml +1 -1
- package/main/default/pages/flow_selector.page.amis.json +1 -1
- package/main/default/pages/page_instance_print.page.amis.json +33 -0
- package/main/default/services/flows.service.js +3 -2
- package/package.json +4 -2
- package/public/workflow/index.css +107 -2
- package/run.js +388 -0
- package/src/rests/index.js +1 -0
- package/src/rests/migrateTemplates.js +154 -0
- package/src/util/templateConverter.js +335 -0
package/run.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Workflow Template Export/Import Script
|
|
5
|
+
*
|
|
6
|
+
* This script exports and imports workflow templates (instance_template and print_template)
|
|
7
|
+
* from MongoDB to files for Blaze-to-Liquid conversion.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node run.js export [flowId] [outputDir]
|
|
11
|
+
* node run.js import <flowId> [templateType] [inputDir]
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* node run.js export # Export all flows
|
|
15
|
+
* node run.js export abc123 # Export specific flow
|
|
16
|
+
* node run.js export abc123 /custom/path # Export to custom directory
|
|
17
|
+
* node run.js import abc123 # Import both templates
|
|
18
|
+
* node run.js import abc123 instance_template # Import only instance_template
|
|
19
|
+
*/
|
|
20
|
+
require('dotenv-flow').config({});
|
|
21
|
+
const { MongoClient } = require('mongodb');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const fs = require('fs').promises;
|
|
24
|
+
|
|
25
|
+
// MongoDB connection
|
|
26
|
+
let client = null;
|
|
27
|
+
let db = null;
|
|
28
|
+
|
|
29
|
+
// Connect to MongoDB using native driver
|
|
30
|
+
async function connectToMongoDB() {
|
|
31
|
+
if (db) {
|
|
32
|
+
return db;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/steedos-plugins';
|
|
36
|
+
console.log('mongoUrl', mongoUrl)
|
|
37
|
+
try {
|
|
38
|
+
client = new MongoClient(mongoUrl, {
|
|
39
|
+
useNewUrlParser: true,
|
|
40
|
+
useUnifiedTopology: true,
|
|
41
|
+
directConnection: true
|
|
42
|
+
});
|
|
43
|
+
await client.connect();
|
|
44
|
+
|
|
45
|
+
// Extract database name from URL
|
|
46
|
+
const dbName = mongoUrl.split('/').pop().split('?')[0];
|
|
47
|
+
db = client.db(dbName);
|
|
48
|
+
|
|
49
|
+
console.log(`Connected to MongoDB: ${dbName}`);
|
|
50
|
+
return db;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('Failed to connect to MongoDB:', error.message);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Close MongoDB connection
|
|
58
|
+
async function closeMongoDB() {
|
|
59
|
+
if (client) {
|
|
60
|
+
await client.close();
|
|
61
|
+
client = null;
|
|
62
|
+
db = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get collection from MongoDB
|
|
67
|
+
async function getCollection(name) {
|
|
68
|
+
const database = await connectToMongoDB();
|
|
69
|
+
return database.collection(name);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Export templates to .blaze files
|
|
73
|
+
async function exportTemplates(flowId, outputDir) {
|
|
74
|
+
try {
|
|
75
|
+
const baseDir = outputDir || path.join(process.cwd(), 'flows-template');
|
|
76
|
+
|
|
77
|
+
// Create directory
|
|
78
|
+
try {
|
|
79
|
+
await fs.mkdir(baseDir, { recursive: true });
|
|
80
|
+
} catch (mkdirError) {
|
|
81
|
+
if (mkdirError.code !== 'EEXIST') {
|
|
82
|
+
throw new Error(`Failed to create directory ${baseDir}: ${mkdirError.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const flowsCollection = await getCollection('flows');
|
|
87
|
+
const formsCollection = await getCollection('forms');
|
|
88
|
+
|
|
89
|
+
// Build query
|
|
90
|
+
const query = {};
|
|
91
|
+
if (flowId) {
|
|
92
|
+
// Export specific flow by ID
|
|
93
|
+
query._id = flowId;
|
|
94
|
+
} else {
|
|
95
|
+
// Export all flows with templates (when flowId is not specified)
|
|
96
|
+
query.$or = [
|
|
97
|
+
{ instance_template: { $exists: true, $ne: null, $ne: '' } },
|
|
98
|
+
{ print_template: { $exists: true, $ne: null, $ne: '' } }
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const flows = await flowsCollection.find(query, {
|
|
103
|
+
projection: {
|
|
104
|
+
_id: 1,
|
|
105
|
+
name: 1,
|
|
106
|
+
instance_template: 1,
|
|
107
|
+
print_template: 1,
|
|
108
|
+
form: 1
|
|
109
|
+
}
|
|
110
|
+
}).toArray();
|
|
111
|
+
|
|
112
|
+
if (flows.length === 0) {
|
|
113
|
+
console.log('No flows found with templates.');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (flowId) {
|
|
118
|
+
console.log(`Found flow: ${flows[0]?.name || flowId}`);
|
|
119
|
+
} else {
|
|
120
|
+
console.log(`Found ${flows.length} flow(s) with templates to export.`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const exportSummary = [];
|
|
124
|
+
|
|
125
|
+
for (const flow of flows) {
|
|
126
|
+
console.log(`\nExporting flow: ${flow.name} (${flow._id})`);
|
|
127
|
+
|
|
128
|
+
let fileCount = 0;
|
|
129
|
+
const flowSummary = {
|
|
130
|
+
id: flow._id,
|
|
131
|
+
name: flow.name,
|
|
132
|
+
templates: []
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Export instance_template
|
|
136
|
+
if (flow.instance_template) {
|
|
137
|
+
try {
|
|
138
|
+
const fileName = `${flow._id}.instance_template.blaze`;
|
|
139
|
+
const filePath = path.join(baseDir, fileName);
|
|
140
|
+
await fs.writeFile(filePath, flow.instance_template, 'utf8');
|
|
141
|
+
console.log(` ✓ ${fileName}`);
|
|
142
|
+
fileCount++;
|
|
143
|
+
flowSummary.templates.push('instance_template');
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(` ✗ Failed to write instance_template: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Export print_template
|
|
150
|
+
if (flow.print_template) {
|
|
151
|
+
try {
|
|
152
|
+
const fileName = `${flow._id}.print_template.blaze`;
|
|
153
|
+
const filePath = path.join(baseDir, fileName);
|
|
154
|
+
await fs.writeFile(filePath, flow.print_template, 'utf8');
|
|
155
|
+
console.log(` ✓ ${fileName}`);
|
|
156
|
+
fileCount++;
|
|
157
|
+
flowSummary.templates.push('print_template');
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(` ✗ Failed to write print_template: ${error.message}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Export field definitions
|
|
164
|
+
if (flow.form) {
|
|
165
|
+
const form = await formsCollection.findOne(
|
|
166
|
+
{ _id: flow.form },
|
|
167
|
+
{ projection: { 'current.fields': 1 } }
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
if (form && form.current && form.current.fields) {
|
|
171
|
+
try {
|
|
172
|
+
const fileName = `${flow._id}.fields.json`;
|
|
173
|
+
const filePath = path.join(baseDir, fileName);
|
|
174
|
+
await fs.writeFile(
|
|
175
|
+
filePath,
|
|
176
|
+
JSON.stringify(form.current.fields, null, 2),
|
|
177
|
+
'utf8'
|
|
178
|
+
);
|
|
179
|
+
console.log(` ✓ ${fileName}`);
|
|
180
|
+
fileCount++;
|
|
181
|
+
flowSummary.templates.push('fields');
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error(` ✗ Failed to write fields.json: ${error.message}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (fileCount > 0) {
|
|
189
|
+
console.log(` Exported ${fileCount} file(s)`);
|
|
190
|
+
exportSummary.push(flowSummary);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(`\n✅ Export completed. Files saved to: ${baseDir}`);
|
|
195
|
+
|
|
196
|
+
// Print summary
|
|
197
|
+
console.log('\n📋 导出清单:');
|
|
198
|
+
console.log('='.repeat(80));
|
|
199
|
+
exportSummary.forEach((item, index) => {
|
|
200
|
+
console.log(`${index + 1}. ${item.name} (${item.id})`);
|
|
201
|
+
console.log(` 模板: ${item.templates.join(', ')}`);
|
|
202
|
+
});
|
|
203
|
+
console.log('='.repeat(80));
|
|
204
|
+
console.log(`共导出 ${exportSummary.length} 个流程`);
|
|
205
|
+
|
|
206
|
+
// Save summary to file
|
|
207
|
+
const summaryFile = path.join(baseDir, 'export-summary.json');
|
|
208
|
+
await fs.writeFile(
|
|
209
|
+
summaryFile,
|
|
210
|
+
JSON.stringify(exportSummary, null, 2),
|
|
211
|
+
'utf8'
|
|
212
|
+
);
|
|
213
|
+
console.log(`\n清单已保存至: ${summaryFile}`);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.error(`\n❌ Export failed: ${error.message}`);
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Import templates from .liquid files
|
|
221
|
+
async function importTemplates(flowId, templateType, inputDir) {
|
|
222
|
+
try {
|
|
223
|
+
if (!flowId) {
|
|
224
|
+
throw new Error('flowId is required for import');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const baseDir = inputDir || path.join(process.cwd(), 'flows-template');
|
|
228
|
+
const flowsCollection = await getCollection('flows');
|
|
229
|
+
|
|
230
|
+
// Verify flow exists
|
|
231
|
+
const flow = await flowsCollection.findOne(
|
|
232
|
+
{ _id: flowId },
|
|
233
|
+
{ projection: { _id: 1, name: 1, instance_template: 1, print_template: 1 } }
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
if (!flow) {
|
|
237
|
+
throw new Error(`Flow not found: ${flowId}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.log(`\nImporting templates for flow: ${flow.name} (${flow._id})`);
|
|
241
|
+
|
|
242
|
+
const updateObj = {
|
|
243
|
+
modified: new Date()
|
|
244
|
+
};
|
|
245
|
+
const imported = [];
|
|
246
|
+
|
|
247
|
+
// Determine which templates to import
|
|
248
|
+
const templatesToImport = templateType
|
|
249
|
+
? [templateType]
|
|
250
|
+
: ['instance_template', 'print_template'];
|
|
251
|
+
|
|
252
|
+
for (const type of templatesToImport) {
|
|
253
|
+
const liquidFile = path.join(baseDir, `${flowId}.${type}.liquid`);
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// Check if file exists and is readable
|
|
257
|
+
await fs.access(liquidFile, fs.constants.R_OK);
|
|
258
|
+
|
|
259
|
+
// Read the liquid file
|
|
260
|
+
const liquidContent = await fs.readFile(liquidFile, 'utf8');
|
|
261
|
+
|
|
262
|
+
// Backup old value if it exists
|
|
263
|
+
const backupField = `${type}_backup`;
|
|
264
|
+
if (flow[type]) {
|
|
265
|
+
updateObj[backupField] = flow[type];
|
|
266
|
+
console.log(` ℹ Backing up old ${type} to ${backupField}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Set new value
|
|
270
|
+
updateObj[type] = liquidContent;
|
|
271
|
+
|
|
272
|
+
imported.push({
|
|
273
|
+
type,
|
|
274
|
+
file: `${flowId}.${type}.liquid`,
|
|
275
|
+
backed_up: !!flow[type]
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
console.log(` ✓ Imported ${type} from ${flowId}.${type}.liquid`);
|
|
279
|
+
} catch (fileError) {
|
|
280
|
+
if (fileError.code === 'ENOENT') {
|
|
281
|
+
// File doesn't exist, skip
|
|
282
|
+
console.log(` ⊗ ${type} file not found, skipping`);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (fileError.code === 'EACCES') {
|
|
286
|
+
throw new Error(`Permission denied reading file ${liquidFile}`);
|
|
287
|
+
}
|
|
288
|
+
throw new Error(`Failed to read file ${liquidFile}: ${fileError.message}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (imported.length === 0) {
|
|
293
|
+
throw new Error(`No template files found for flow ${flowId}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Update the flow
|
|
297
|
+
await flowsCollection.updateOne(
|
|
298
|
+
{ _id: flowId },
|
|
299
|
+
{ $set: updateObj }
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
console.log(`\n✅ Import completed. Imported ${imported.length} template(s).`);
|
|
303
|
+
imported.forEach(item => {
|
|
304
|
+
console.log(` - ${item.type}: ${item.backed_up ? 'updated (old value backed up)' : 'created'}`);
|
|
305
|
+
});
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error(`\n❌ Import failed: ${error.message}`);
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Main function
|
|
313
|
+
async function main() {
|
|
314
|
+
const args = process.argv.slice(2);
|
|
315
|
+
const command = args[0];
|
|
316
|
+
|
|
317
|
+
if (!command || !['export', 'import'].includes(command)) {
|
|
318
|
+
console.log(`
|
|
319
|
+
Workflow Template Export/Import Script
|
|
320
|
+
|
|
321
|
+
Usage:
|
|
322
|
+
node run.js export [flowId] [outputDir]
|
|
323
|
+
node run.js import <flowId> [templateType] [inputDir]
|
|
324
|
+
|
|
325
|
+
Commands:
|
|
326
|
+
export Export templates to .blaze files
|
|
327
|
+
import Import templates from .liquid files
|
|
328
|
+
|
|
329
|
+
Export Examples:
|
|
330
|
+
node run.js export # Export all flows
|
|
331
|
+
node run.js export abc123 # Export specific flow
|
|
332
|
+
node run.js export abc123 /custom/path # Export to custom directory
|
|
333
|
+
|
|
334
|
+
Import Examples:
|
|
335
|
+
node run.js import abc123 # Import both templates
|
|
336
|
+
node run.js import abc123 instance_template # Import only instance_template
|
|
337
|
+
node run.js import abc123 print_template # Import only print_template
|
|
338
|
+
|
|
339
|
+
Directory Structure:
|
|
340
|
+
flows-template/
|
|
341
|
+
├── {flowId}.instance_template.blaze # Exported Blaze template
|
|
342
|
+
├── {flowId}.print_template.blaze # Exported Blaze template
|
|
343
|
+
├── {flowId}.fields.json # Field definitions
|
|
344
|
+
├── {flowId}.instance_template.liquid # Converted Liquid template (for import)
|
|
345
|
+
└── {flowId}.print_template.liquid # Converted Liquid template (for import)
|
|
346
|
+
|
|
347
|
+
Workflow:
|
|
348
|
+
1. Export: node run.js export [flowId]
|
|
349
|
+
2. Convert: Convert .blaze files to .liquid format (manually or with Copilot)
|
|
350
|
+
3. Import: node run.js import <flowId>
|
|
351
|
+
`);
|
|
352
|
+
process.exit(command ? 1 : 0);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
if (command === 'export') {
|
|
357
|
+
const flowId = args[1];
|
|
358
|
+
const outputDir = args[2];
|
|
359
|
+
await exportTemplates(flowId, outputDir);
|
|
360
|
+
} else if (command === 'import') {
|
|
361
|
+
const flowId = args[1];
|
|
362
|
+
const templateType = args[2];
|
|
363
|
+
const inputDir = args[3];
|
|
364
|
+
|
|
365
|
+
// Validate templateType if provided
|
|
366
|
+
if (templateType && !['instance_template', 'print_template'].includes(templateType)) {
|
|
367
|
+
// Maybe it's the inputDir
|
|
368
|
+
await importTemplates(flowId, null, templateType);
|
|
369
|
+
} else {
|
|
370
|
+
await importTemplates(flowId, templateType, inputDir);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
await closeMongoDB();
|
|
375
|
+
process.exit(0);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error('\n❌ Error:', error.message);
|
|
378
|
+
await closeMongoDB();
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Run if executed directly
|
|
384
|
+
if (require.main === module) {
|
|
385
|
+
main();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
module.exports = { exportTemplates, importTemplates };
|
package/src/rests/index.js
CHANGED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const converter = require('../util/templateConverter');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const { MongoClient } = require('mongodb');
|
|
4
|
+
|
|
5
|
+
// MongoDB connection
|
|
6
|
+
let client = null;
|
|
7
|
+
let db = null;
|
|
8
|
+
|
|
9
|
+
async function connectToMongoDB() {
|
|
10
|
+
if (db) {
|
|
11
|
+
return db;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/steedos';
|
|
15
|
+
try {
|
|
16
|
+
client = new MongoClient(mongoUrl);
|
|
17
|
+
await client.connect();
|
|
18
|
+
|
|
19
|
+
const dbName = mongoUrl.split('/').pop().split('?')[0];
|
|
20
|
+
db = client.db(dbName);
|
|
21
|
+
return db;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error('Failed to connect to MongoDB:', error.message);
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function getCollection(name) {
|
|
29
|
+
const database = await connectToMongoDB();
|
|
30
|
+
return database.collection(name);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
rest: {
|
|
35
|
+
method: 'GET',
|
|
36
|
+
fullPath: '/api/workflow/migrateTemplates'
|
|
37
|
+
},
|
|
38
|
+
params: {
|
|
39
|
+
batchSize: { type: 'number', optional: true, convert: true },
|
|
40
|
+
force: { type: 'boolean', optional: true, convert: true },
|
|
41
|
+
flowId: { type: 'string', optional: true }
|
|
42
|
+
},
|
|
43
|
+
async handler(ctx) {
|
|
44
|
+
const { user } = ctx.meta;
|
|
45
|
+
|
|
46
|
+
if (user.is_space_admin !== true) {
|
|
47
|
+
throw new Error('只有管理员才能执行此操作');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { batchSize = 5000, force = false, flowId } = ctx.params;
|
|
51
|
+
|
|
52
|
+
const flowsCollection = await getCollection('flows');
|
|
53
|
+
const formsCollection = await getCollection('forms');
|
|
54
|
+
|
|
55
|
+
let flows = [];
|
|
56
|
+
if (flowId) {
|
|
57
|
+
flows = await flowsCollection.find({ _id: flowId }).toArray();
|
|
58
|
+
} else {
|
|
59
|
+
let query = {
|
|
60
|
+
$and: [
|
|
61
|
+
{
|
|
62
|
+
$or: [
|
|
63
|
+
{ instance_template: { $exists: true, $ne: '' } },
|
|
64
|
+
{ print_template: { $exists: true, $ne: '' } }
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (!force) {
|
|
71
|
+
query.$or = [
|
|
72
|
+
{ migrate_status: { $ne: 'completed' } },
|
|
73
|
+
{ migrate_status: { $exists: false } }
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
flows = await flowsCollection.find(query).limit(batchSize).toArray();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
total: flows.length,
|
|
81
|
+
success: 0,
|
|
82
|
+
failed: 0,
|
|
83
|
+
skipped: 0,
|
|
84
|
+
details: []
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
for (const flow of flows) {
|
|
88
|
+
const flowId = flow._id;
|
|
89
|
+
const updateDoc = {};
|
|
90
|
+
let isChanged = false;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
let fields = {};
|
|
94
|
+
if (flow.form) {
|
|
95
|
+
const form = await formsCollection.findOne(
|
|
96
|
+
{ _id: flow.form },
|
|
97
|
+
{ projection: { 'current.fields': 1 } }
|
|
98
|
+
);
|
|
99
|
+
if (form && form.current && form.current.fields) {
|
|
100
|
+
fields = _.keyBy(form.current.fields, (f) => f.name || f.code);
|
|
101
|
+
fields._raw = form.current.fields;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!flow.backup_status) {
|
|
106
|
+
if (flow.instance_template) {
|
|
107
|
+
updateDoc.instance_template_backup = flow.instance_template;
|
|
108
|
+
}
|
|
109
|
+
if (flow.print_template) {
|
|
110
|
+
updateDoc.print_template_backup = flow.print_template;
|
|
111
|
+
}
|
|
112
|
+
updateDoc.backup_status = true;
|
|
113
|
+
isChanged = true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const sourceInstance = updateDoc.instance_template_backup || flow.instance_template_backup || flow.instance_template;
|
|
117
|
+
const sourcePrint = updateDoc.print_template_backup || flow.print_template_backup || flow.print_template;
|
|
118
|
+
|
|
119
|
+
if (sourceInstance) {
|
|
120
|
+
const converted = converter.convertTemplate(sourceInstance, fields, false);
|
|
121
|
+
updateDoc.instance_template = converted;
|
|
122
|
+
isChanged = true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (sourcePrint) {
|
|
126
|
+
const converted = converter.convertTemplate(sourcePrint, fields, true);
|
|
127
|
+
updateDoc.print_template = converted;
|
|
128
|
+
isChanged = true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
updateDoc.migrate_status = 'completed';
|
|
132
|
+
updateDoc.migrated_at = new Date();
|
|
133
|
+
|
|
134
|
+
await flowsCollection.updateOne({ _id: flow._id }, { $set: updateDoc });
|
|
135
|
+
|
|
136
|
+
results.success++;
|
|
137
|
+
results.details.push({ id: flow._id, name: flow.name, status: 'success' });
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error(`Error migrating flow ${flowId}:`, error);
|
|
140
|
+
results.failed++;
|
|
141
|
+
results.details.push({ id: flow._id, name: flow.name, status: 'error', error: error.message });
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
await flowsCollection.updateOne(
|
|
145
|
+
{ _id: flow._id },
|
|
146
|
+
{ $set: { migrate_status: 'error', migrate_error: error.message } }
|
|
147
|
+
);
|
|
148
|
+
} catch (e) {}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return results;
|
|
153
|
+
}
|
|
154
|
+
}
|