@steedos/data-import 2.2.50 → 2.2.51-beta.4
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/package.json +5 -6
- package/src/importRecords.router.ts +0 -99
- package/src/objectImport.ts +0 -938
- package/tsconfig.json +0 -31
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steedos/data-import",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.51-beta.4",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"prepare": "tsc"
|
|
7
|
+
"build": "tsc"
|
|
9
8
|
},
|
|
10
9
|
"dependencies": {
|
|
11
|
-
"@steedos/core": "2.2.
|
|
12
|
-
"@steedos/objectql": "2.2.
|
|
10
|
+
"@steedos/core": "2.2.51-beta.4",
|
|
11
|
+
"@steedos/objectql": "2.2.51-beta.4",
|
|
13
12
|
"dotenv-flow": "^3.1.0",
|
|
14
13
|
"node-xlsx": "^0.16.1"
|
|
15
14
|
},
|
|
@@ -25,5 +24,5 @@
|
|
|
25
24
|
"publishConfig": {
|
|
26
25
|
"access": "public"
|
|
27
26
|
},
|
|
28
|
-
"gitHead": "
|
|
27
|
+
"gitHead": "975061203a5c8ee2ed2ab495cfed25a100304c96"
|
|
29
28
|
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { importWithCmsFile } from "./objectImport"
|
|
2
|
-
import { requireAuthentication } from '@steedos/core';
|
|
3
|
-
|
|
4
|
-
const express = require("express");
|
|
5
|
-
const router = express.Router();
|
|
6
|
-
const Fiber = require('fibers');
|
|
7
|
-
|
|
8
|
-
const json2xls = require('json2xls');
|
|
9
|
-
import { getObject } from '@steedos/objectql';
|
|
10
|
-
import _ from 'lodash';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// declare var Creator: any;
|
|
14
|
-
// declare var Meteor: any;
|
|
15
|
-
declare var Steedos: any;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param req
|
|
19
|
-
* @param res
|
|
20
|
-
* @returns
|
|
21
|
-
*/
|
|
22
|
-
const initiateImport = async function (req, res) {
|
|
23
|
-
try {
|
|
24
|
-
const userSession = req.user;
|
|
25
|
-
// const spaceId = userSession.spaceId;
|
|
26
|
-
let { importObjId, importObjectHistoryId } = req.body;
|
|
27
|
-
let fileId = null;
|
|
28
|
-
const isSpaceAdmin = req.user.is_space_admin;
|
|
29
|
-
//传入了importObjId参数,必须是工作区管理员权限
|
|
30
|
-
if (importObjId && !isSpaceAdmin) {
|
|
31
|
-
return res
|
|
32
|
-
.status(401)
|
|
33
|
-
.send({ status: "error", message: "Permission denied" });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (importObjectHistoryId) {
|
|
37
|
-
const record = await getObject('queue_import_history').findOne(importObjectHistoryId);
|
|
38
|
-
if(!record){
|
|
39
|
-
throw new Error(
|
|
40
|
-
`can not find queue_import_history record with given id "${importObjectHistoryId}"`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
if(!record.file){
|
|
44
|
-
throw new Error(`Upload excel file, please.`);
|
|
45
|
-
}
|
|
46
|
-
fileId = record.file
|
|
47
|
-
importObjId = record.queue_import
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// if (!Steedos.hasFeature('metadata_api', spaceId)) {
|
|
51
|
-
// return res.status(403).send({ status: 'error', message: 'Please upgrade the platform license to Enterprise Edition' });
|
|
52
|
-
// }
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
let result = await importWithCmsFile(importObjId, userSession, importObjectHistoryId, fileId);
|
|
56
|
-
return res.status(200).send({ status: "success", result });
|
|
57
|
-
} catch (error) {
|
|
58
|
-
return res
|
|
59
|
-
.status(500)
|
|
60
|
-
.send({ status: "failed", message: error.message });
|
|
61
|
-
}
|
|
62
|
-
} catch (error) {
|
|
63
|
-
return res.status(500).send({ status: "failed", error: error.message });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
router.post('/api/data/initiateImport', requireAuthentication, function (req, res) { //requireAuthentication
|
|
68
|
-
return Fiber(function () {
|
|
69
|
-
return initiateImport(req, res);
|
|
70
|
-
}).run();;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
router.get('/api/data/download/template/:record_id', requireAuthentication, async function (req, res) { //requireAuthentication
|
|
74
|
-
try {
|
|
75
|
-
const { record_id } = req.params;
|
|
76
|
-
let queueImportDoc = await getObject("queue_import").findOne(record_id);
|
|
77
|
-
let fieldMaps = queueImportDoc.field_mappings;
|
|
78
|
-
if (_.isEmpty(fieldMaps)) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
let json = {};
|
|
82
|
-
for (const fMap of fieldMaps) {
|
|
83
|
-
json[fMap.header] = null;
|
|
84
|
-
}
|
|
85
|
-
let xls = json2xls([json]);
|
|
86
|
-
res.writeHead(200, {
|
|
87
|
-
'Content-Type': 'application/octet-stream',
|
|
88
|
-
'Content-Disposition': 'attachment;filename=' + encodeURI(queueImportDoc.description + '.xlsx'),
|
|
89
|
-
});
|
|
90
|
-
res.end(xls, 'binary');
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error(error);
|
|
93
|
-
res.status(500).send({ error: error.message });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
exports.default = router;
|
|
99
|
-
|
package/src/objectImport.ts
DELETED
|
@@ -1,938 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const _ = require("underscore");
|
|
4
|
-
const objectql = require("@steedos/objectql");
|
|
5
|
-
const Fiber = require("fibers");
|
|
6
|
-
const xlsx = require("node-xlsx");
|
|
7
|
-
const moment = require("moment");
|
|
8
|
-
declare var Creator: any;
|
|
9
|
-
|
|
10
|
-
type ImportOptions = {
|
|
11
|
-
objectName: string;
|
|
12
|
-
externalIdName: string | null;
|
|
13
|
-
lookupFieldMap: any;
|
|
14
|
-
fireWorkflows: boolean;
|
|
15
|
-
operation: string;
|
|
16
|
-
userSession: any;
|
|
17
|
-
mappings: any[];
|
|
18
|
-
keyIndexes: number;
|
|
19
|
-
queueImportId: string | null;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function converterString(field_name, dataCell, jsonObj) {
|
|
23
|
-
if (dataCell) {
|
|
24
|
-
jsonObj[field_name] = dataCell.toString();
|
|
25
|
-
} else {
|
|
26
|
-
jsonObj[field_name] = null;
|
|
27
|
-
}
|
|
28
|
-
return "";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function converterDate(field_name, dataCell, jsonObj, utcOffset) {
|
|
32
|
-
var date, date_error;
|
|
33
|
-
date_error = "";
|
|
34
|
-
if (_.isEmpty(dataCell) && !_.isDate(dataCell)) {
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (_.isDate(dataCell)) {
|
|
39
|
-
try {
|
|
40
|
-
jsonObj[field_name] = moment(dataCell).add(utcOffset, 'h').hour(0).minute(0).second(0).millisecond(0).toDate();
|
|
41
|
-
} catch (error) {
|
|
42
|
-
return error.message
|
|
43
|
-
}
|
|
44
|
-
return "";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
date = new Date(dataCell);
|
|
48
|
-
if (
|
|
49
|
-
date.getFullYear() &&
|
|
50
|
-
Object.prototype.toString.call(date) === "[object Date]"
|
|
51
|
-
) {
|
|
52
|
-
jsonObj[field_name] = date;
|
|
53
|
-
} else {
|
|
54
|
-
date_error = `${dataCell}不是日期类型数据`;
|
|
55
|
-
}
|
|
56
|
-
return date_error;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function converterDateTime(field_name, dataCell, jsonObj, utcOffset) {
|
|
60
|
-
var date, date_error;
|
|
61
|
-
date_error = "";
|
|
62
|
-
if (_.isEmpty(dataCell) && !_.isDate(dataCell)) {
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
date = new Date(dataCell);
|
|
66
|
-
if (
|
|
67
|
-
date.getFullYear() &&
|
|
68
|
-
Object.prototype.toString.call(date) === "[object Date]"
|
|
69
|
-
) {
|
|
70
|
-
jsonObj[field_name] = moment(dataCell).add(moment().utcOffset() - utcOffset * 60, 'm').toDate();
|
|
71
|
-
} else {
|
|
72
|
-
date_error = `${dataCell}不是日期时间类型数据`;
|
|
73
|
-
}
|
|
74
|
-
return date_error;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// 从excel单元格中解析并转换时间字段为Date类型
|
|
78
|
-
function converterTime(field_name, dataCell, jsonObj, utcOffset) {
|
|
79
|
-
var date, date_error;
|
|
80
|
-
date_error = "";
|
|
81
|
-
if (_.isEmpty(dataCell) && !_.isDate(dataCell)) {
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
date = new Date(`1970-01-01T${dataCell}Z`);
|
|
85
|
-
if (
|
|
86
|
-
date.getFullYear() &&
|
|
87
|
-
Object.prototype.toString.call(date) === "[object Date]"
|
|
88
|
-
) {
|
|
89
|
-
jsonObj[field_name] = moment(`1970-01-01T${dataCell}Z`, 'YYYY-MM-DDThh:mmZ').toDate();
|
|
90
|
-
} else {
|
|
91
|
-
date_error = `${dataCell}不是时间类型数据`;
|
|
92
|
-
}
|
|
93
|
-
return date_error;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function converteNum(field, field_name, dataCell, jsonObj) {
|
|
97
|
-
var number, number_error;
|
|
98
|
-
number_error = "";
|
|
99
|
-
if(_.isNumber(dataCell) || _.isNull(dataCell)){
|
|
100
|
-
jsonObj[field_name] = dataCell;
|
|
101
|
-
}else{
|
|
102
|
-
number = parseFloat(dataCell);
|
|
103
|
-
if (!isNaN(number)) {
|
|
104
|
-
jsonObj[field_name] = number;
|
|
105
|
-
} else {
|
|
106
|
-
number_error = `${dataCell}不是数值类型数据`;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (field.required && _.isEmpty(number_error) && _.isNull(jsonObj[field_name])) {
|
|
111
|
-
number_error += `${field_name}字段为必填项`;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return number_error;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function converterSelect(objectName, field_name, dataCell, jsonObj) {
|
|
118
|
-
var allowedValues, allowedLabels, fields, ref, select_error;
|
|
119
|
-
select_error = "";
|
|
120
|
-
let objectConfig = await objectql.getObject(objectName).toConfig();
|
|
121
|
-
fields = objectConfig.fields;
|
|
122
|
-
let field = fields[field_name];
|
|
123
|
-
allowedValues = _.pluck(field.options, "value");
|
|
124
|
-
allowedLabels = _.pluck(field.options, "label");
|
|
125
|
-
let optionsMap = _.object(allowedLabels, allowedValues);
|
|
126
|
-
|
|
127
|
-
let hasOptionsFunction =
|
|
128
|
-
!_.isEmpty(field.optionsFunction) ||
|
|
129
|
-
!_.isEmpty(field._optionsFunction) ||
|
|
130
|
-
_.isFunction(field.optionsFunction);
|
|
131
|
-
|
|
132
|
-
let cellContents: any = [];
|
|
133
|
-
let noResult = true;
|
|
134
|
-
if (field.multiple) {
|
|
135
|
-
jsonObj[field_name] = [];
|
|
136
|
-
if (dataCell) {
|
|
137
|
-
cellContents = dataCell.split(";");
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
jsonObj[field_name] = null;
|
|
141
|
-
cellContents.push(dataCell);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
for (let cellContent of cellContents) {
|
|
145
|
-
if (!cellContent) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (hasOptionsFunction) {
|
|
149
|
-
noResult = false;
|
|
150
|
-
if (field.multiple) {
|
|
151
|
-
jsonObj[field_name].push(cellContent);
|
|
152
|
-
} else {
|
|
153
|
-
jsonObj[field_name] = cellContent;
|
|
154
|
-
}
|
|
155
|
-
} else if (allowedLabels.indexOf(cellContent) >= 0) {
|
|
156
|
-
noResult = false;
|
|
157
|
-
if (field.multiple) {
|
|
158
|
-
jsonObj[field_name].push(optionsMap[cellContent]);
|
|
159
|
-
} else {
|
|
160
|
-
jsonObj[field_name] = optionsMap[cellContent];
|
|
161
|
-
}
|
|
162
|
-
} else {
|
|
163
|
-
select_error = `${cellContent}不属于${field_name}的可选范围`;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (noResult && field.required) {
|
|
168
|
-
//
|
|
169
|
-
select_error += `${field_name}字段为必填项`;
|
|
170
|
-
}
|
|
171
|
-
return select_error;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function getNameFieldKey(fields) {
|
|
175
|
-
let NAME_FIELD_KEY = "name";
|
|
176
|
-
for (let fieldName in fields) {
|
|
177
|
-
let field = fields[fieldName];
|
|
178
|
-
if (field.is_name) {
|
|
179
|
-
NAME_FIELD_KEY = fieldName;
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return NAME_FIELD_KEY;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async function converterLookup(
|
|
187
|
-
objectName,
|
|
188
|
-
field_name,
|
|
189
|
-
dataCell,
|
|
190
|
-
jsonObj,
|
|
191
|
-
fieldMap,
|
|
192
|
-
options
|
|
193
|
-
) {
|
|
194
|
-
var fields,
|
|
195
|
-
lookups,
|
|
196
|
-
lookup_error,
|
|
197
|
-
field,
|
|
198
|
-
reference_to_object,
|
|
199
|
-
reference_to_field,
|
|
200
|
-
selectfield;
|
|
201
|
-
lookup_error = "";
|
|
202
|
-
let objectConfig = await objectql.getObject(objectName).toConfig();
|
|
203
|
-
fields = objectConfig.fields;
|
|
204
|
-
field = fields[field_name];
|
|
205
|
-
reference_to_object = field.reference_to;
|
|
206
|
-
|
|
207
|
-
reference_to_field = field.reference_to_field;
|
|
208
|
-
let noResult = true; // 判断是否所有数据都找不到结果
|
|
209
|
-
|
|
210
|
-
if (!reference_to_field) {
|
|
211
|
-
reference_to_field = "_id";
|
|
212
|
-
}
|
|
213
|
-
if (fieldMap[field_name].matched_by) {
|
|
214
|
-
selectfield = fieldMap[field_name].matched_by;
|
|
215
|
-
} else {
|
|
216
|
-
selectfield = getNameFieldKey(fields);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// 这一条加在了permission_set.object.yml里面
|
|
220
|
-
// if(field_name == 'profile'){
|
|
221
|
-
// reference_to_object = 'permission_set'
|
|
222
|
-
// }
|
|
223
|
-
|
|
224
|
-
let lookupCollection = await objectql.getObject(reference_to_object);
|
|
225
|
-
|
|
226
|
-
let cellContents: any = [];
|
|
227
|
-
if (field.multiple) {
|
|
228
|
-
jsonObj[field_name] = [];
|
|
229
|
-
if (dataCell) {
|
|
230
|
-
cellContents = dataCell.split(";");
|
|
231
|
-
}
|
|
232
|
-
} else {
|
|
233
|
-
jsonObj[field_name] = null;
|
|
234
|
-
cellContents.push(dataCell);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
for (let cellContent of cellContents) {
|
|
238
|
-
if (!cellContent) {
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
//修复导入单元格为数字式文本问题
|
|
243
|
-
if (lookupCollection.fields[selectfield]._type == 'varchar') {
|
|
244
|
-
cellContent = cellContent.toString();
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
let cellFilter = [selectfield, "=", cellContent];
|
|
248
|
-
let spaceFilter = ["space", "=", options.userSession.spaceId];
|
|
249
|
-
let filters = [cellFilter, spaceFilter, ['is_deleted', '!=', true]];
|
|
250
|
-
lookups = await lookupCollection.find({ filters: filters });
|
|
251
|
-
|
|
252
|
-
if (lookups.length == 0) {
|
|
253
|
-
//找不到记录可能是对象上没有space属性
|
|
254
|
-
lookups = await lookupCollection.find({ filters: cellFilter });
|
|
255
|
-
let hasSpace = false;
|
|
256
|
-
for (let lookup of lookups) {
|
|
257
|
-
if (lookup.space) {
|
|
258
|
-
hasSpace = true;
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (hasSpace) {
|
|
263
|
-
lookup_error += `所查找的${reference_to_object}不属于当前space`;
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// [[selectfield, '=', dataCell], ['space', '=', options.userSession.spaceId]]
|
|
269
|
-
|
|
270
|
-
let allRecordCount = lookups.length;
|
|
271
|
-
let dbRecordCount = lookups.length;
|
|
272
|
-
for (let lookup of lookups) {
|
|
273
|
-
if (lookup.is_system) {
|
|
274
|
-
dbRecordCount--;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
if (dbRecordCount == 1 || allRecordCount == 1) {
|
|
278
|
-
noResult = false;
|
|
279
|
-
if (field.multiple) {
|
|
280
|
-
jsonObj[field_name].push(lookups[0][reference_to_field]);
|
|
281
|
-
} else {
|
|
282
|
-
jsonObj[field_name] = lookups[0][reference_to_field];
|
|
283
|
-
}
|
|
284
|
-
} else if (dbRecordCount.length == 0) {
|
|
285
|
-
if (!dataCell) {
|
|
286
|
-
// 单元格没有填写
|
|
287
|
-
|
|
288
|
-
if (!field.multiple) {
|
|
289
|
-
jsonObj[field_name] = null;
|
|
290
|
-
}
|
|
291
|
-
} else {
|
|
292
|
-
// 单元格有值却找不到记录
|
|
293
|
-
if (fieldMap[field_name].save_key_while_fail) {
|
|
294
|
-
jsonObj[field_name] = cellContent;
|
|
295
|
-
} else {
|
|
296
|
-
lookup_error += `${dataCell}不是${field_name}类型数据的key`;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
noResult = false;
|
|
301
|
-
lookup_error += `无法根据${selectfield}: ${dataCell}找到唯一的${reference_to_object}`;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (noResult && field.multiple) {
|
|
306
|
-
//
|
|
307
|
-
jsonObj[field_name] = null;
|
|
308
|
-
}
|
|
309
|
-
if (noResult && field.required) {
|
|
310
|
-
//
|
|
311
|
-
lookup_error += `${field_name}字段为必填项`;
|
|
312
|
-
}
|
|
313
|
-
return lookup_error;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function converterBool(field_name, dataCell, jsonObj) {
|
|
317
|
-
var bool_error, flag;
|
|
318
|
-
bool_error = "";
|
|
319
|
-
flag = dataCell.toString().toLowerCase();
|
|
320
|
-
if (flag === "是" || flag === "1" || flag === "yes" || flag === "true") {
|
|
321
|
-
jsonObj[field_name] = true;
|
|
322
|
-
} else if (
|
|
323
|
-
flag === "否" ||
|
|
324
|
-
flag === "0" ||
|
|
325
|
-
flag === "no" ||
|
|
326
|
-
flag === "false"
|
|
327
|
-
) {
|
|
328
|
-
jsonObj[field_name] = false;
|
|
329
|
-
} else {
|
|
330
|
-
bool_error = `${dataCell}不是bool类型数据`;
|
|
331
|
-
}
|
|
332
|
-
return bool_error;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
async function insertRow(dataRow, objectName, options: ImportOptions) {
|
|
336
|
-
var errorInfo, insertInfo, jsonObj, objFields, ref, lookupFieldMap;
|
|
337
|
-
jsonObj = {};
|
|
338
|
-
insertInfo = {};
|
|
339
|
-
insertInfo["create"] = false;
|
|
340
|
-
insertInfo["update"] = false;
|
|
341
|
-
errorInfo = "";
|
|
342
|
-
lookupFieldMap = options.lookupFieldMap;
|
|
343
|
-
|
|
344
|
-
let mappings = options.mappings;
|
|
345
|
-
let space = options.userSession.spaceId;
|
|
346
|
-
let utcOffset = options.userSession.utcOffset;
|
|
347
|
-
|
|
348
|
-
// 对象的fields
|
|
349
|
-
ref = await objectql.getObject(objectName).toConfig();
|
|
350
|
-
objFields = ref.fields;
|
|
351
|
-
objFields = Object.assign({ _id: { name: "_id", type: "text" } }, objFields);
|
|
352
|
-
|
|
353
|
-
let dataLength =
|
|
354
|
-
dataRow.length > mappings.length ? dataRow.length : mappings.length;
|
|
355
|
-
for (let i = 0; i < dataLength; i++) {
|
|
356
|
-
let dataCell = dataRow[i];
|
|
357
|
-
if (!dataCell && !_.isNumber(dataCell)) {
|
|
358
|
-
dataCell = null;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
var error, mapping, noField;
|
|
362
|
-
mapping = mappings[i];
|
|
363
|
-
if (!mapping) {
|
|
364
|
-
continue;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// 找到需要插入的数据
|
|
368
|
-
for (let apiName of mapping) {
|
|
369
|
-
error = null;
|
|
370
|
-
noField = true;
|
|
371
|
-
for (let field_name in objFields) {
|
|
372
|
-
let field = objFields[field_name];
|
|
373
|
-
if (field_name == apiName) {
|
|
374
|
-
noField = false;
|
|
375
|
-
try {
|
|
376
|
-
switch (field != null ? field.type : void 0) {
|
|
377
|
-
case "date":
|
|
378
|
-
error = converterDate(field_name, dataCell, jsonObj, utcOffset);
|
|
379
|
-
break;
|
|
380
|
-
case "datetime":
|
|
381
|
-
error = converterDateTime(field_name, dataCell, jsonObj, utcOffset);
|
|
382
|
-
break;
|
|
383
|
-
case "time":
|
|
384
|
-
error = converterTime(field_name, dataCell, jsonObj, utcOffset);
|
|
385
|
-
break;
|
|
386
|
-
case "number":
|
|
387
|
-
error = converteNum(field, field_name, dataCell, jsonObj);
|
|
388
|
-
break;
|
|
389
|
-
case "boolean":
|
|
390
|
-
error = converterBool(field_name, dataCell, jsonObj);
|
|
391
|
-
break;
|
|
392
|
-
case "select":
|
|
393
|
-
error = await converterSelect(
|
|
394
|
-
objectName,
|
|
395
|
-
field_name,
|
|
396
|
-
dataCell,
|
|
397
|
-
jsonObj
|
|
398
|
-
);
|
|
399
|
-
break;
|
|
400
|
-
case "lookup":
|
|
401
|
-
error = await converterLookup(
|
|
402
|
-
objectName,
|
|
403
|
-
field_name,
|
|
404
|
-
dataCell,
|
|
405
|
-
jsonObj,
|
|
406
|
-
lookupFieldMap,
|
|
407
|
-
options
|
|
408
|
-
);
|
|
409
|
-
break;
|
|
410
|
-
case "text":
|
|
411
|
-
error = converterString(field_name, dataCell, jsonObj);
|
|
412
|
-
break;
|
|
413
|
-
case "textarea":
|
|
414
|
-
error = converterString(field_name, dataCell, jsonObj);
|
|
415
|
-
break;
|
|
416
|
-
case "master_detail":
|
|
417
|
-
error = await converterLookup(
|
|
418
|
-
objectName,
|
|
419
|
-
field_name,
|
|
420
|
-
dataCell,
|
|
421
|
-
jsonObj,
|
|
422
|
-
lookupFieldMap,
|
|
423
|
-
options
|
|
424
|
-
);
|
|
425
|
-
break;
|
|
426
|
-
case "email":
|
|
427
|
-
error = converterString(field_name, dataCell, jsonObj);
|
|
428
|
-
break;
|
|
429
|
-
case "toggle":
|
|
430
|
-
error = converterBool(field_name, dataCell, jsonObj);
|
|
431
|
-
break;
|
|
432
|
-
case "url":
|
|
433
|
-
error = converterString(field_name, dataCell, jsonObj);
|
|
434
|
-
break;
|
|
435
|
-
case "currency":
|
|
436
|
-
error = converteNum(field, field_name, dataCell, jsonObj);
|
|
437
|
-
break;
|
|
438
|
-
case "percent":
|
|
439
|
-
error = converteNum(field, field_name, dataCell, jsonObj);
|
|
440
|
-
break;
|
|
441
|
-
default:
|
|
442
|
-
throw new Error(`Unsupported data type: ${field.type}`);
|
|
443
|
-
}
|
|
444
|
-
} catch (err) {
|
|
445
|
-
console.error(error);
|
|
446
|
-
error = err.message;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
if (noField) {
|
|
451
|
-
if (objectName == "space_users" && apiName == "password") {
|
|
452
|
-
jsonObj[apiName] = dataCell;
|
|
453
|
-
} else {
|
|
454
|
-
error = `${apiName}不是对象${objectName}的属性`;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
if (error) {
|
|
458
|
-
errorInfo = errorInfo + "," + error;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
insertInfo["insertState"] = true;
|
|
464
|
-
let selectObj = {};
|
|
465
|
-
let recordExists = false;
|
|
466
|
-
let recordExistsDoc: any = null;
|
|
467
|
-
let objectCollection = await objectql.getObject(objectName);
|
|
468
|
-
|
|
469
|
-
if (jsonObj && !errorInfo) {
|
|
470
|
-
if (
|
|
471
|
-
objectCollection.datasource.name == "default" ||
|
|
472
|
-
objectCollection.datasource.name == "meteor"
|
|
473
|
-
) {
|
|
474
|
-
jsonObj.space = space;
|
|
475
|
-
}
|
|
476
|
-
// 不存在则新建,存在则更新
|
|
477
|
-
let external_id_name = options.externalIdName;
|
|
478
|
-
if (external_id_name) {
|
|
479
|
-
let allUndefined = true;
|
|
480
|
-
for (let _external_id_name of external_id_name) {
|
|
481
|
-
selectObj[_external_id_name] = jsonObj[_external_id_name];
|
|
482
|
-
if (selectObj[_external_id_name] != undefined) {
|
|
483
|
-
allUndefined = false;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (!allUndefined) {
|
|
487
|
-
let filters = selectObjectToFilters(selectObj);
|
|
488
|
-
let records = await objectCollection.find({ filters });
|
|
489
|
-
if (records.length == 1) {
|
|
490
|
-
selectObj["space"] = space;
|
|
491
|
-
filters = selectObjectToFilters(selectObj);
|
|
492
|
-
records = await objectCollection.find({ filters });
|
|
493
|
-
|
|
494
|
-
if (records.length == 1) {
|
|
495
|
-
recordExists = true;
|
|
496
|
-
recordExistsDoc = records[0];
|
|
497
|
-
} else if (records.length > 1) {
|
|
498
|
-
errorInfo = `无法根据${external_id_name}: ${selectObj[external_id_name]}找到唯一的${objectName}记录`;
|
|
499
|
-
} else {
|
|
500
|
-
errorInfo = `所查找的记录不属于当前space`;
|
|
501
|
-
}
|
|
502
|
-
} else if (records.length > 1) {
|
|
503
|
-
errorInfo = `无法根据${external_id_name}: ${selectObj[external_id_name]}找到唯一的${objectName}记录`;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
if (!errorInfo) {
|
|
509
|
-
let operation = options.operation; // insert update upsert
|
|
510
|
-
let filters = selectObjectToFilters(selectObj);
|
|
511
|
-
// delete jsonObj._id; // 支持导入自定义_id
|
|
512
|
-
if (
|
|
513
|
-
recordExists &&
|
|
514
|
-
recordExistsDoc &&
|
|
515
|
-
(operation == "update" || operation == "upsert")
|
|
516
|
-
) {
|
|
517
|
-
try {
|
|
518
|
-
// if(options.userSession){
|
|
519
|
-
// jsonObj['modified_by'] = options.userSession.userId;
|
|
520
|
-
// }
|
|
521
|
-
delete jsonObj._id; // 更新内容不包括_id
|
|
522
|
-
await objectCollection.update(recordExistsDoc._id, jsonObj);
|
|
523
|
-
insertInfo["create"] = false;
|
|
524
|
-
insertInfo["update"] = true;
|
|
525
|
-
} catch (error) {
|
|
526
|
-
console.error(error);
|
|
527
|
-
errorInfo = error.message;
|
|
528
|
-
insertInfo["update"] = false;
|
|
529
|
-
insertInfo["insertState"] = false;
|
|
530
|
-
}
|
|
531
|
-
} else if (
|
|
532
|
-
!recordExists &&
|
|
533
|
-
(operation == "insert" || operation == "upsert")
|
|
534
|
-
) {
|
|
535
|
-
try {
|
|
536
|
-
if (
|
|
537
|
-
objectCollection.datasource.name == "default" ||
|
|
538
|
-
objectCollection.datasource.name == "meteor"
|
|
539
|
-
) {
|
|
540
|
-
if (!jsonObj.owner && options.userSession) {
|
|
541
|
-
let userId = options.userSession.userId;
|
|
542
|
-
jsonObj["owner"] = userId;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// if (jsonObj.owner) {
|
|
547
|
-
// let space_user_collection = await objectql.getObject('space_users')
|
|
548
|
-
// let space_users = await space_user_collection.find(['user', '=', jsonObj.owner]);
|
|
549
|
-
// let space_user = space_users [0]
|
|
550
|
-
// jsonObj['created_by'] = space_user.user;
|
|
551
|
-
// jsonObj['modified_by'] = space_user.user;
|
|
552
|
-
// // jsonObj['company_id'] = space_user.company_id;
|
|
553
|
-
// // jsonObj['company_ids'] = space_user.company_ids
|
|
554
|
-
|
|
555
|
-
// }
|
|
556
|
-
// 如果有自定义_id,则校验_id为字符串类型且有字母、数字组成
|
|
557
|
-
if (jsonObj.hasOwnProperty('_id')) {
|
|
558
|
-
if (!_.isString(jsonObj._id) || !/^[a-zA-Z0-9]*$/.test(jsonObj._id)) {
|
|
559
|
-
throw new Error('Primary Key ( _id )必须为字母、数字组成的字符串。');
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
await objectCollection.insert(jsonObj, options.userSession);
|
|
563
|
-
insertInfo["create"] = true;
|
|
564
|
-
insertInfo["update"] = false;
|
|
565
|
-
} catch (error) {
|
|
566
|
-
console.error(error);
|
|
567
|
-
errorInfo = error.message;
|
|
568
|
-
insertInfo["create"] = false;
|
|
569
|
-
insertInfo["insertState"] = false;
|
|
570
|
-
}
|
|
571
|
-
} else {
|
|
572
|
-
insertInfo["ignored"] = true;
|
|
573
|
-
}
|
|
574
|
-
} else {
|
|
575
|
-
insertInfo["insertState"] = false;
|
|
576
|
-
}
|
|
577
|
-
insertInfo["errorInfo"] = errorInfo;
|
|
578
|
-
return insertInfo;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
function selectObjectToFilters(selectObj) {
|
|
582
|
-
let filters: any = [['is_deleted', '!=', true]];
|
|
583
|
-
for (let k in selectObj) {
|
|
584
|
-
let filter: any = [];
|
|
585
|
-
filter.push(k);
|
|
586
|
-
filter.push("=");
|
|
587
|
-
filter.push(selectObj[k]);
|
|
588
|
-
filters.push(filter);
|
|
589
|
-
}
|
|
590
|
-
return filters;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function loadWorkbook(file) {
|
|
594
|
-
var stream, chunks;
|
|
595
|
-
stream = file.createReadStream("files");
|
|
596
|
-
chunks = [];
|
|
597
|
-
|
|
598
|
-
let loadStream = new Promise(function(resolve, reject) {
|
|
599
|
-
stream.on("data", function(chunk) {
|
|
600
|
-
return chunks.push(chunk);
|
|
601
|
-
});
|
|
602
|
-
stream.on("end", function() {
|
|
603
|
-
let workbook = xlsx.parse(Buffer.concat(chunks), {
|
|
604
|
-
cellDates: true,
|
|
605
|
-
});
|
|
606
|
-
resolve(workbook);
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
return loadStream;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
*
|
|
615
|
-
* @param importObjId
|
|
616
|
-
* @param userSession
|
|
617
|
-
* @param fileId 如果指定了fileId,则使用此id; 否则使用importObject的file
|
|
618
|
-
* @returns
|
|
619
|
-
*/
|
|
620
|
-
export async function importWithCmsFile(
|
|
621
|
-
importObjId,
|
|
622
|
-
userSession,
|
|
623
|
-
importObjectHistoryId,
|
|
624
|
-
fileId
|
|
625
|
-
) {
|
|
626
|
-
var file, files;
|
|
627
|
-
|
|
628
|
-
let queueImport = await objectql
|
|
629
|
-
.getObject("queue_import")
|
|
630
|
-
.findOne(importObjId);
|
|
631
|
-
|
|
632
|
-
if (!queueImport) {
|
|
633
|
-
throw new Error(
|
|
634
|
-
`can not find queue_import record with given id "${importObjId}"`
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
let options: any = {
|
|
638
|
-
userSession,
|
|
639
|
-
objectName: queueImport.object_name,
|
|
640
|
-
operation: queueImport.operation,
|
|
641
|
-
externalIdName: queueImport.external_id_name,
|
|
642
|
-
fieldMappings: queueImport.field_mappings,
|
|
643
|
-
queueImportId: importObjId,
|
|
644
|
-
importObjectHistoryId: importObjectHistoryId,
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
if (
|
|
648
|
-
options.operation != "insert" &&
|
|
649
|
-
(!options.externalIdName || _.isEmpty(options.externalIdName))
|
|
650
|
-
) {
|
|
651
|
-
throw new Error(
|
|
652
|
-
`external_id_name is required with operation: update or upsert`
|
|
653
|
-
);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
let fileCollection = await objectql.getObject("cfs_files_filerecord");
|
|
657
|
-
files = await fileCollection.find({
|
|
658
|
-
filters: [["_id", "=", fileId || queueImport.file]],
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
if (files && files.length == 0) {
|
|
662
|
-
throw new Error(`Upload excel file, please.`);
|
|
663
|
-
} else if (files.length > 1) {
|
|
664
|
-
throw new Error(`Just need one file.`);
|
|
665
|
-
}
|
|
666
|
-
file = files[0];
|
|
667
|
-
|
|
668
|
-
await importWithExcelFile(file, options);
|
|
669
|
-
|
|
670
|
-
//先返回数据导入对象的id
|
|
671
|
-
return {
|
|
672
|
-
status: "success",
|
|
673
|
-
msg: ``,
|
|
674
|
-
queueImportId: queueImport._id,
|
|
675
|
-
importObjectHistoryId: importObjectHistoryId,
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
const formatErrors = function(errorList) {
|
|
680
|
-
let errors: any = null;
|
|
681
|
-
|
|
682
|
-
if (errorList && _.isArray(errorList) && errorList.length > 0) {
|
|
683
|
-
errors = "";
|
|
684
|
-
_.each(errorList, (item, index) => {
|
|
685
|
-
errors = `${errors}\n:::warning\n${item}\n\n:::\n\n\\\n`;
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
return errors;
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
export async function importWithExcelFile(file, options) {
|
|
693
|
-
const start_time = new Date();
|
|
694
|
-
|
|
695
|
-
const allowedOperation = ["insert", "update", "upsert"];
|
|
696
|
-
if (!options.operation || !_.contains(allowedOperation, options.operation)) {
|
|
697
|
-
throw new Error(`unsupported operation "${options.operation}"`);
|
|
698
|
-
}
|
|
699
|
-
let fieldMappings = options.fieldMappings;
|
|
700
|
-
if (!fieldMappings) {
|
|
701
|
-
throw new Error(`fieldMapping is required`);
|
|
702
|
-
}
|
|
703
|
-
let objectConfig = await objectql.getObject(options.objectName).toConfig();
|
|
704
|
-
if (!objectConfig) {
|
|
705
|
-
throw new Error(`can not find object "${options.objectName}"`);
|
|
706
|
-
}
|
|
707
|
-
if (options.operation != "insert" && !options.externalIdName) {
|
|
708
|
-
throw new Error(`missing option: externalIdName`);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
let mappedFieldNames: any = [];
|
|
712
|
-
for (let i = 0; i < fieldMappings.length; i++) {
|
|
713
|
-
let mapping = fieldMappings[i];
|
|
714
|
-
|
|
715
|
-
if (!mapping || !mapping.api_name) {
|
|
716
|
-
continue;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
if (_.contains(mappedFieldNames, mapping.api_name)) {
|
|
720
|
-
throw new Error(`field "${mapping.api_name}" should be mapped only once`);
|
|
721
|
-
}
|
|
722
|
-
mappedFieldNames.push(mapping.api_name);
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
if (options.operation != "insert") {
|
|
726
|
-
for (let _externalIdName of options.externalIdName) {
|
|
727
|
-
if (!_.contains(mappedFieldNames, _externalIdName)) {
|
|
728
|
-
throw new Error(
|
|
729
|
-
`externalIdName "${_externalIdName}" is not mapped in fieldMapping`
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
if (!_.contains(_.keys(objectConfig.fields), _externalIdName)) {
|
|
733
|
-
if (_externalIdName != "_id") {
|
|
734
|
-
throw new Error(
|
|
735
|
-
`externalIdName "${_externalIdName}" should be a field of object "${options.objectName}"`
|
|
736
|
-
);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
for (let fieldName in objectConfig.fields) {
|
|
743
|
-
let field = objectConfig.fields[fieldName];
|
|
744
|
-
// update时必填字段需要被映射
|
|
745
|
-
if (
|
|
746
|
-
options.operation == "update" &&
|
|
747
|
-
field.required &&
|
|
748
|
-
!_.contains(mappedFieldNames, fieldName)
|
|
749
|
-
) {
|
|
750
|
-
throw new Error(
|
|
751
|
-
`field "${fieldName}" is required but not mapped in fieldMapping`
|
|
752
|
-
);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
let recordDatas = await filetoRecords(file, options);
|
|
756
|
-
|
|
757
|
-
async function importDataAsync(recordDatas) {
|
|
758
|
-
//在回调函数里面异步处理导入
|
|
759
|
-
|
|
760
|
-
let importResult: any = await importWithRecords(recordDatas, options);
|
|
761
|
-
|
|
762
|
-
//最后更新导入结果
|
|
763
|
-
const end_time = new Date();
|
|
764
|
-
|
|
765
|
-
if (options.queueImportId) {
|
|
766
|
-
await objectql.getObject("queue_import_history").directUpdate(
|
|
767
|
-
{
|
|
768
|
-
filters: ["_id", "=", options.importObjectHistoryId],
|
|
769
|
-
},
|
|
770
|
-
{
|
|
771
|
-
modified_by: options.userSession.userId,
|
|
772
|
-
error: formatErrors(importResult.errorList),
|
|
773
|
-
total_count: importResult.total_count,
|
|
774
|
-
success_count: importResult.success_count,
|
|
775
|
-
failure_count: importResult.failure_count,
|
|
776
|
-
state: "finished",
|
|
777
|
-
start_time,
|
|
778
|
-
end_time,
|
|
779
|
-
}
|
|
780
|
-
);
|
|
781
|
-
}
|
|
782
|
-
let notificationBody = `总共导入${importResult.total_count}条记录;\n成功: ${importResult.success_count}条;\n失败: ${importResult.failure_count};`;
|
|
783
|
-
if (importResult.errorList && importResult.errorList.length > 0) {
|
|
784
|
-
notificationBody = `${notificationBody}\n错误信息: ${importResult.errorList.join(
|
|
785
|
-
"\n "
|
|
786
|
-
)}`;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
//发送通知
|
|
790
|
-
return Fiber(function() {
|
|
791
|
-
Creator.addNotifications(
|
|
792
|
-
{
|
|
793
|
-
name: `导入完成: ${file.original.name}`,
|
|
794
|
-
body: notificationBody,
|
|
795
|
-
related_to: {
|
|
796
|
-
o: "queue_import_history",
|
|
797
|
-
ids: [options.importObjectHistoryId],
|
|
798
|
-
},
|
|
799
|
-
related_name: file.original.name,
|
|
800
|
-
from: options.userSession.userId,
|
|
801
|
-
space: options.userSession.spaceId,
|
|
802
|
-
},
|
|
803
|
-
options.userSession.userId,
|
|
804
|
-
options.userSession.userId
|
|
805
|
-
);
|
|
806
|
-
}).run();
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
importDataAsync(recordDatas);
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
export async function filetoRecords(file, options) {
|
|
813
|
-
let workbook: any = await loadWorkbook(file);
|
|
814
|
-
var data, headers, recordDatas;
|
|
815
|
-
|
|
816
|
-
data = workbook[0].data;
|
|
817
|
-
headers = data[0];
|
|
818
|
-
recordDatas = data.slice(1);
|
|
819
|
-
|
|
820
|
-
let fieldMappings = options.fieldMappings;
|
|
821
|
-
let keyIndexes: number[] = [],
|
|
822
|
-
lookupFieldMap = {};
|
|
823
|
-
let parsedMappings: any = [];
|
|
824
|
-
|
|
825
|
-
let headerMap = {};
|
|
826
|
-
for (let i = 0; i < headers.length; i++) {
|
|
827
|
-
let header = headers[i];
|
|
828
|
-
for (let j = 0; j < fieldMappings.length; j++) {
|
|
829
|
-
let mapping = fieldMappings[j];
|
|
830
|
-
if (mapping.header == header) {
|
|
831
|
-
if (_.contains(options.externalIdName, mapping.api_name)) {
|
|
832
|
-
keyIndexes.push(i);
|
|
833
|
-
}
|
|
834
|
-
lookupFieldMap[mapping.api_name] = {
|
|
835
|
-
save_key_while_fail: mapping.save_key_while_fail,
|
|
836
|
-
};
|
|
837
|
-
if (mapping.matched_by) {
|
|
838
|
-
lookupFieldMap[mapping.api_name]["matched_by"] = mapping.matched_by;
|
|
839
|
-
}
|
|
840
|
-
mapping.mapped = true;
|
|
841
|
-
if (!parsedMappings[i]) {
|
|
842
|
-
parsedMappings[i] = [];
|
|
843
|
-
}
|
|
844
|
-
parsedMappings[i].push(mapping.api_name);
|
|
845
|
-
headers[i] = null;
|
|
846
|
-
// break;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
if (!parsedMappings[i]) {
|
|
850
|
-
parsedMappings[i] = null;
|
|
851
|
-
}
|
|
852
|
-
if (headerMap[header]) {
|
|
853
|
-
throw new Error(
|
|
854
|
-
`The Excel file contained duplicate header(s): ${header}`
|
|
855
|
-
);
|
|
856
|
-
} else {
|
|
857
|
-
headerMap[header] = true;
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
options.mappings = parsedMappings;
|
|
861
|
-
options.keyIndexes = keyIndexes;
|
|
862
|
-
|
|
863
|
-
if (recordDatas.length > 50000) {
|
|
864
|
-
throw new Error(`The data file exceeds the row limit of 50,000.`);
|
|
865
|
-
}
|
|
866
|
-
options.lookupFieldMap = lookupFieldMap;
|
|
867
|
-
return recordDatas;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
function getKeyString(dataRow, keyIndexes) {
|
|
871
|
-
let key = "";
|
|
872
|
-
|
|
873
|
-
for (let j = 0; j < keyIndexes.length; j++) {
|
|
874
|
-
key += dataRow[keyIndexes[j]];
|
|
875
|
-
}
|
|
876
|
-
return key;
|
|
877
|
-
}
|
|
878
|
-
export async function importWithRecords(recordDatas, options) {
|
|
879
|
-
var failure_count, success_count;
|
|
880
|
-
success_count = 0;
|
|
881
|
-
failure_count = 0;
|
|
882
|
-
var errorList: any[] = [];
|
|
883
|
-
var total_count = recordDatas.length;
|
|
884
|
-
let keyMap: any = {};
|
|
885
|
-
|
|
886
|
-
let objectName = options.objectName;
|
|
887
|
-
let keyIndexes = options.keyIndexes;
|
|
888
|
-
|
|
889
|
-
for (let i = 0; i < recordDatas.length; i++) {
|
|
890
|
-
let dataRow = recordDatas[i];
|
|
891
|
-
|
|
892
|
-
let key = getKeyString(dataRow, keyIndexes);
|
|
893
|
-
|
|
894
|
-
let keyCount = keyMap[key];
|
|
895
|
-
if (typeof keyCount == "undefined") {
|
|
896
|
-
keyCount = keyMap[key] = 1;
|
|
897
|
-
} else {
|
|
898
|
-
keyMap[key] = keyMap[key] + 1;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
for (let i = 0; i < recordDatas.length; i++) {
|
|
903
|
-
let dataRow = recordDatas[i];
|
|
904
|
-
if (!dataRow || dataRow.length == 0) {
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
if (!options.externalIdName && !_.isEmpty(options.externalIdName)) {
|
|
909
|
-
let key = getKeyString(dataRow, keyIndexes);
|
|
910
|
-
|
|
911
|
-
if (keyMap[key] > 1) {
|
|
912
|
-
failure_count = failure_count + 1;
|
|
913
|
-
errorList.push(`文件中存在重复的${options.externalIdName}: "${key}"`);
|
|
914
|
-
continue;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
var insertInfo;
|
|
919
|
-
insertInfo = await insertRow(dataRow, objectName, options);
|
|
920
|
-
|
|
921
|
-
if (insertInfo != null ? insertInfo.errorInfo : void 0) {
|
|
922
|
-
errorList.push(dataRow + insertInfo.errorInfo);
|
|
923
|
-
}
|
|
924
|
-
// # 插入一行数据
|
|
925
|
-
if (insertInfo != null ? insertInfo.insertState : void 0) {
|
|
926
|
-
success_count = success_count + 1;
|
|
927
|
-
} else {
|
|
928
|
-
failure_count = failure_count + 1;
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
return {
|
|
933
|
-
errorList,
|
|
934
|
-
total_count,
|
|
935
|
-
success_count,
|
|
936
|
-
failure_count,
|
|
937
|
-
};
|
|
938
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "commonjs",
|
|
4
|
-
"target": "ES2017",
|
|
5
|
-
"noImplicitAny": false,
|
|
6
|
-
"emitDecoratorMetadata": true,
|
|
7
|
-
"experimentalDecorators": true,
|
|
8
|
-
"sourceMap": true,
|
|
9
|
-
"declaration": false,
|
|
10
|
-
"noFallthroughCasesInSwitch": true,
|
|
11
|
-
"noImplicitReturns": true,
|
|
12
|
-
"stripInternal": true,
|
|
13
|
-
"strictNullChecks": true,
|
|
14
|
-
"resolveJsonModule": true,
|
|
15
|
-
"outDir": "./lib",
|
|
16
|
-
"newLine": "LF",
|
|
17
|
-
"lib": [
|
|
18
|
-
"es2017"
|
|
19
|
-
],
|
|
20
|
-
"skipLibCheck": true,
|
|
21
|
-
"esModuleInterop": true,
|
|
22
|
-
},
|
|
23
|
-
"include": [
|
|
24
|
-
"./src"
|
|
25
|
-
],
|
|
26
|
-
"exclude": [
|
|
27
|
-
"**/node_modules",
|
|
28
|
-
"**/*.spec.ts"
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
|