import.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose-free/lib/service');
  3. const { BusinessError, ErrorCode } = require('naf-core').Error;
  4. const _ = require('lodash');
  5. const assert = require('assert');
  6. const Excel = require('exceljs');
  7. const moment = require('moment');
  8. const importSetting = require('../public/importSetting');
  9. const fs = require('fs');
  10. // 通用导入
  11. class ImportService extends CrudService {
  12. constructor(ctx) {
  13. super(ctx, 'import');
  14. this.http = this.ctx.service.util.httpUtil;
  15. this.spMark = this.ctx.service.special;
  16. this.prefix = '/db';
  17. this.temp = 'temp';
  18. this.fetchService = this.ctx.service.fetch;
  19. this.createService = this.ctx.service.create;
  20. this.updateService = this.ctx.service.update;
  21. this.deleteService = this.ctx.service.delete;
  22. }
  23. async getFile(uri) {
  24. const { domain } = this.ctx.app.config.import;
  25. const file = await this.ctx.curl(`${domain}${uri}`);
  26. if (!(file && file.data)) {
  27. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到指定文件');
  28. }
  29. return file;
  30. }
  31. /**
  32. * 导入:需要自行配置每个表的导入字段的映射关系
  33. * @param {Object} param0 {table} 表名
  34. * @param {Object} param1 {uri} 上传的文件地址
  35. */
  36. async index({ table }, { uri }) {
  37. assert(table, '缺少导入目标');
  38. const file = await this.getFile(uri);
  39. const workbook = new Excel.Workbook();
  40. await workbook.xlsx.load(file.data);
  41. const sheet = workbook.getWorksheet(1);
  42. let meta = importSetting[table];
  43. if (!meta) {
  44. throw new BusinessError(ErrorCode.SERVICE_FAULT, '未设置该表的映射关系');
  45. }
  46. // 获取表头
  47. const heads = sheet.getRow(1).values;
  48. for (const h of meta) {
  49. if (!_.get(h, 'key')) continue;
  50. // 直接找索引,下面也是用索引找
  51. const r = heads.findIndex(f => f === h.key);
  52. if (r >= 0) h.index = r;
  53. }
  54. // 将没有索引的踢出去,别影响下面,可能报异常会炸
  55. meta = meta.filter(i => _.get(i, 'index'));
  56. const arr = [];
  57. sheet.eachRow((row, ri) => {
  58. if (ri === 1) return;
  59. // 遍历meta,拼成数据
  60. const object = {};
  61. const excelValues = row.values;
  62. for (const i of meta) {
  63. const { index, column, turn } = i;
  64. if (_.get(excelValues, index) && column) {
  65. let value = _.get(excelValues, index);
  66. if (turn) value = this[turn](i);
  67. object[column] = value;
  68. }
  69. }
  70. arr.push(object);
  71. });
  72. const errorArray = [];
  73. for (const i of arr) {
  74. try {
  75. // try catch保证能一直往里插,把错误回收到一起统一显示
  76. // TODO:此处await应该可以取消,但是可能就呜呜呜呜一顿添加数据了.可能没有问题,也可能会爆炸,需要试试
  77. await this.ctx.service.create.index({ table }, i);
  78. } catch (error) {
  79. errorArray.push(i);
  80. }
  81. }
  82. if (errorArray.length > 0) return errorArray;
  83. }
  84. /**
  85. * 考试相关特殊导入
  86. * @param {Object} param {uri} 上传的文件地址
  87. */
  88. async updateExaminee({ uri }) {
  89. const file = await this.getFile(uri);
  90. const workbook = new Excel.Workbook();
  91. await workbook.xlsx.load(file.data);
  92. const sheet = workbook.getWorksheet(1);
  93. let meta = this.getExamineeMeta();
  94. // 获取表头
  95. const heads = sheet.getRow(1).values;
  96. for (const h of meta) {
  97. if (!_.get(h, 'key')) continue;
  98. // 直接找索引,下面也是用索引找
  99. const r = heads.findIndex(f => f === h.key);
  100. if (r >= 0) h.index = r;
  101. }
  102. // 将没有索引的踢出去,别影响下面,可能报异常会炸
  103. meta = meta.filter(i => _.get(i, 'index'));
  104. const arr = [];
  105. sheet.eachRow((row, ri) => {
  106. if (ri === 1) return;
  107. // 遍历meta,拼成数据
  108. const object = {};
  109. const excelValues = row.values;
  110. for (const i of meta) {
  111. const { index, column } = i;
  112. if (_.get(excelValues, index) && column) {
  113. const value = _.get(excelValues, index);
  114. if (column === 'exam_date') {
  115. const date = moment(value).format('YYYY-MM-DD');
  116. object[column] = date;
  117. } else {
  118. object[column] = value;
  119. }
  120. }
  121. }
  122. arr.push(object);
  123. });
  124. const errorList = [];
  125. // 去修改
  126. for (const i of arr) {
  127. const { card, testsite_num } = i;
  128. if (!card || !testsite_num) {
  129. errorList.push(i);
  130. continue;
  131. }
  132. const table = 'examination_examinee';
  133. const query = { card, testsite_num };
  134. const body = i;
  135. try {
  136. await this.ctx.service.update.index({ table }, query, body);
  137. } catch (error) {
  138. errorList.push(i);
  139. }
  140. }
  141. if (errorList.length > 0) return errorList;
  142. return 'ok';
  143. }
  144. getExamineeMeta() {
  145. return [
  146. { key: '姓名', column: 'name' },
  147. { key: '性别', column: 'gender' },
  148. { key: '身份证号', column: 'card' },
  149. { key: '联系电话', column: 'phone' },
  150. { key: '准考证号', column: 'exam_num' },
  151. { key: '公安机关', column: 'police_office' },
  152. { key: '考试成绩', column: 'exam_achieve' },
  153. { key: '体能', column: 'stamina' },
  154. { key: '考试等级', column: 'exam_grade' },
  155. { key: '考试日期', column: 'exam_date' },
  156. { key: '考试时间', column: 'exam_time' },
  157. { key: '考点编码', column: 'testsite_num' },
  158. { key: '考试地点名称', column: 'exam_addr' },
  159. { key: '座位号', column: 'seat_num' },
  160. { key: '场次', column: 'exam_play' },
  161. { key: '参考区县', column: 'district' },
  162. { key: '考试类型', column: 'exam_type' },
  163. ];
  164. }
  165. async staffBase({ uri }) {
  166. const file = await this.getFile(uri);
  167. const workbook = new Excel.Workbook();
  168. await workbook.xlsx.load(file.data);
  169. const sheet = workbook.getWorksheet(1);
  170. let meta = this.getStaffBase();
  171. // 获取表头
  172. const heads = sheet.getRow(1).values;
  173. for (const h of meta) {
  174. if (!_.get(h, 'key')) continue;
  175. // 直接找索引,下面也是用索引找
  176. const r = heads.findIndex(f => f === h.key);
  177. if (r >= 0) {
  178. h.index = r;
  179. }
  180. }
  181. // 将没有索引的踢出去,别影响下面,可能报异常会炸
  182. meta = meta.filter(i => _.get(i, 'index'));
  183. const arr = [];
  184. // 处理图片
  185. const sheetImageInfo = sheet.getImages();
  186. const imgs = _.compact(
  187. sheetImageInfo.map(i => {
  188. const { imageId, range } = i;
  189. const row = _.get(range, 'tl.nativeRow');
  190. const col = _.get(range, 'tl.nativeCol');
  191. // const column = _.get
  192. if (row && col) return { row, col: col + 1, imageId };
  193. })
  194. );
  195. sheet.eachRow((row, ri) => {
  196. if (ri === 1) return;
  197. // 遍历meta,拼成数据
  198. const object = {};
  199. const excelValues = row.values;
  200. for (const i of meta) {
  201. const { index, column } = i;
  202. if (_.get(excelValues, index) && column) {
  203. const value = _.get(excelValues, index);
  204. if (column === 'birth' || column === 'acceptance_date') {
  205. const date = moment(value).format('YYYY-MM-DD');
  206. object[column] = date;
  207. } else {
  208. object[column] = value;
  209. }
  210. }
  211. }
  212. arr.push(object);
  213. });
  214. // 上传前的准备工作.因为函数里有很多变量是这个函数中的全局变量,移出去的话会有很多参数,所以昨成立函数中的函数
  215. const toUpload = async (i, key, card) => {
  216. const rowIndex = i + 1;
  217. const metaObject = meta.find(f => f.column === key);
  218. if (!metaObject) return;
  219. const colIndex = metaObject.index;
  220. const imgObject = imgs.find(f => f.row === rowIndex && f.col === colIndex);
  221. const imgId = _.get(imgObject, 'imageId');
  222. if (imgId || imgId === 0) {
  223. const img = workbook.getImage(imgId);
  224. const uri = `baoan_staffBase_card/${card}/upload`;
  225. img.uri = uri;
  226. img.name = key === 'id_just' ? 'front' : 'back';
  227. const url = `http://127.0.0.1:${process.env.NODE_ENV === 'development' ? '9999' : '80'}/files/server/upload`;
  228. const res = await this.uploadImage(url, img);
  229. return res;
  230. }
  231. };
  232. const errorList = [];
  233. for (let i = 0; i < arr.length; i++) {
  234. const object = arr[i];
  235. const { name, card } = object;
  236. if (!name || !card) {
  237. // 缺少名和身份证号的不加
  238. errorList.push(object);
  239. continue;
  240. }
  241. const id_just = _.get(object, 'id_just');
  242. const id_back = _.get(object, 'id_back');
  243. if (!id_just) {
  244. // 没有,获取
  245. const result = await toUpload(i, 'id_just', card);
  246. object.id_just = result;
  247. }
  248. if (!id_back) {
  249. const result = await toUpload(i, 'id_back', card);
  250. object.id_back = result;
  251. }
  252. // 身份证后六位为密码
  253. const password = card.substring(12);
  254. if (password) object.password = password;
  255. // 加人
  256. let baseInfo;
  257. try {
  258. baseInfo = await this.ctx.service.create.index({ table: 'security_guard_base' }, object);
  259. if (baseInfo) baseInfo = baseInfo.data;
  260. } catch (error) {
  261. console.error('保安人员添加失败');
  262. this.ctx.logger.error('保安人员添加失败');
  263. errorList.push({ ...object, reason: '保安人员添加失败' });
  264. continue;
  265. }
  266. try {
  267. if (!_.get(baseInfo, 'id')) continue;
  268. await this.ctx.service.create.index(
  269. { table: 'security_guard_collect' },
  270. { name: _.get(baseInfo, 'name'), security_guard_id: _.get(baseInfo, 'id'), data: _.get(baseInfo, 'id_just'), type: '人像' }
  271. );
  272. await this.ctx.service.update.index({ table: 'security_guard_base' }, { id: _.get(baseInfo, 'id') }, { collect_photo: '已采集' });
  273. } catch (error) {
  274. console.error('保安人员采集信息添加失败');
  275. this.ctx.logger.error('保安人员采集信息添加失败');
  276. errorList.push({ ...object, reason: '保安人员采集信息添加失败' });
  277. if (_.get(baseInfo, 'id')) {
  278. await this.ctx.service.delete.index({ table: 'security_guard_base' }, { id: _.get(baseInfo, 'id') });
  279. }
  280. }
  281. }
  282. if (errorList.length > 0) return errorList;
  283. return 'ok';
  284. }
  285. getStaffBase() {
  286. return [
  287. { key: '是否上班', column: 'is_class' },
  288. { key: '主键', column: 'id' },
  289. { key: '姓名', column: 'name' },
  290. { key: '曾用名', column: 'beforeName' },
  291. { key: '身份证号', column: 'card' },
  292. { key: '性别', column: 'gender' },
  293. { key: '民族', column: 'nation' },
  294. { key: '出生日期', column: 'birth' },
  295. { key: '政治面貌', column: 'politics' },
  296. { key: '文化程度', column: 'education' },
  297. { key: '兵役状况', column: 'soldier' },
  298. { key: '婚姻状况', column: 'marriage' },
  299. { key: '血型', column: 'blood' },
  300. { key: '身高', column: 'height' },
  301. { key: '保安员等级', column: 'grade' },
  302. { key: '驾驶证号', column: 'drive_num' },
  303. { key: '准驾车型', column: 'drive_type' },
  304. { key: '健康状态', column: 'health' },
  305. { key: '户籍省市县', column: 'house_city' },
  306. { key: '户籍/居住证派出所名称', column: 'house_police' },
  307. { key: '微信公众号OpenID', column: 'gopenid' },
  308. { key: '微信统一id', column: 'unionid' },
  309. { key: '是否加入人才库', column: 'is_talent' },
  310. { key: '微信小程序绑定的用户OpenID', column: 'openid' },
  311. { key: '报名企业', column: 'sign_company' },
  312. { key: '培训机构编码', column: 'train_num' },
  313. { key: '现住地省市县', column: 'onhouse_city' },
  314. { key: '户籍地详址', column: 'house_address' },
  315. { key: '现住地详址', column: 'house_onaddress' },
  316. { key: '家庭主要成员', column: 'home_member' },
  317. { key: '教育经历', column: 'education_experience' },
  318. { key: '工作经历', column: 'work_experience' },
  319. { key: '注销人', column: 'cancel_personal' },
  320. { key: '注销原因', column: 'cancel_reason' },
  321. { key: '注销时间', column: 'cancel_date' },
  322. { key: '状态', column: 'status' },
  323. { key: '受理人', column: 'acceptance_personal' },
  324. { key: '受理公安机关', column: 'acceptance_police' },
  325. { key: '受理时间', column: 'acceptance_date' },
  326. { key: '身份证正面', column: 'id_just' },
  327. { key: '身份证反面', column: 'id_back' },
  328. { key: '联系电话', column: 'phone' },
  329. { key: '登录密码', column: 'password' },
  330. { key: '创建时间', column: 'create_time' },
  331. { key: '更新时间', column: 'update_time' },
  332. { key: '采集照片', column: 'collect_photo' },
  333. { key: '采集指纹', column: 'collect_fingerprint' },
  334. { key: '审核结果', column: 'examine_status' },
  335. { key: '审批结果', column: 'approve_status' },
  336. { key: '注销状态', column: 'cancel_status' },
  337. ];
  338. }
  339. /**
  340. * 上传图片(服务端=>服务端)
  341. * @param {String} uri 上传路径
  342. * @param {Object} img 从sheet取出的每项的图片object
  343. */
  344. async uploadImage(uri, img) {
  345. const base64 = this.turnImageToBase64(img);
  346. delete img.buffer;
  347. img.code = base64;
  348. const res = await this.http.$post(uri, img);
  349. if (res && res.uri) {
  350. return res.uri;
  351. }
  352. }
  353. /**
  354. * 转换图片为base64
  355. * @param {Object} object excel获取的图片object
  356. * @property extension 后缀,文件类型
  357. * @property buffer 图片内容,不含头部信息的,
  358. */
  359. turnImageToBase64(object = {}) {
  360. const { extension, buffer } = object;
  361. if (extension && buffer) {
  362. const suffix = object.extension;
  363. const ib = object.buffer.toString('base64');
  364. const base64 = `data:image/${suffix};base64,${ib}`;
  365. return base64;
  366. }
  367. }
  368. /**
  369. * 上传保安人员
  370. * @param {String} uri 文件路径
  371. */
  372. async securityGuard({ uri }) {
  373. assert(uri, '缺少文件地址,无法读取文件');
  374. const meta = require('../public/securityGuardImportMeta');
  375. const metaArr = [ ...meta.base, ...meta.cert ];
  376. const file = await this.getFile(uri);
  377. const workbook = new Excel.Workbook();
  378. await workbook.xlsx.load(file.data);
  379. const sheet = workbook.getWorksheet(1);
  380. const head = _.get(sheet.getRow(1), 'values', []);
  381. for (let i = 0; i < head.length; i++) {
  382. const e = head[i];
  383. if (!e) continue;
  384. const r = metaArr.find(f => f.zh === e);
  385. if (r) r.index = i;
  386. }
  387. // 需要整理出 必填字段,默认字段,检查token字段,需要执行函数来赋值的字段
  388. /**
  389. * 从excel获取数据的字段
  390. */
  391. const getFormExcelArray = metaArr.filter(f => f.index);
  392. /**
  393. * 默认值字段
  394. */
  395. const defArray = metaArr.filter(f => f.default);
  396. const defObject = {};
  397. for (const i of defArray) {
  398. const { key, default: def } = i;
  399. defObject[key] = def;
  400. }
  401. const user = this.ctx.user;
  402. if (!user) {
  403. new BusinessError(ErrorCode.NOT_LOGIN, '未找到操作人信息');
  404. }
  405. /**
  406. * checkToken部分处理
  407. */
  408. const ctArray = metaArr.filter(f => f.checkToken);
  409. for (const i of ctArray) {
  410. const { method, key } = i;
  411. defObject[key] = method(user);
  412. }
  413. /**
  414. * 错误列表
  415. */
  416. const errorList = [];
  417. /**
  418. * 数据列表
  419. */
  420. const dataList = [];
  421. /**
  422. * 获取excel的数据
  423. */
  424. sheet.eachRow(async (row, index) => {
  425. if (index !== 1) {
  426. const values = row.values;
  427. let obj = {};
  428. for (const m of getFormExcelArray) {
  429. const { required, key, index, zh, format } = m;
  430. const value = values[index];
  431. if (required && !value) {
  432. // 必填且没值的情况
  433. errorList.push({ message: `第${index}行数据,缺少必填项 ${zh};` });
  434. continue;
  435. }
  436. if (format) {
  437. if (_.isFunction(format)) obj[key] = format(value);
  438. } else obj[key] = value;
  439. }
  440. obj = Object.assign(obj, defObject);
  441. dataList.push(obj);
  442. }
  443. });
  444. if (errorList.length > 0) return errorList;
  445. /**
  446. * 保安员基础表字段
  447. */
  448. const baseMeta = meta.base.map(i => i.key);
  449. /**
  450. * 保安员证书表字段
  451. */
  452. const certMeta = meta.cert.map(i => i.key);
  453. for (const eData of dataList) {
  454. const baseObject = _.pick(eData, baseMeta);
  455. const certObject = _.pick(eData, certMeta);
  456. const rd = await this.fetchService.index({ table: 'security_guard_base' }, { card: baseObject.card });
  457. if (!rd.data) {
  458. // 创建
  459. /**
  460. * 公安机关机构
  461. */
  462. let policeDepartmentObject;
  463. /**
  464. * 创建后的保安人员基础信息
  465. */
  466. let securityBaseObject;
  467. /**
  468. * 创建后的保安证信息
  469. */
  470. let certificatesBaseObject;
  471. // 补全编码
  472. try {
  473. policeDepartmentObject = await this.ctx.service.securityGuard.base.getHousePoliceCode(eData);
  474. if (policeDepartmentObject) {
  475. baseObject.house_police_code = policeDepartmentObject.num;
  476. } else {
  477. // 业务逻辑错误.并非异常
  478. errorList.push({ message: `${baseObject.name}: 未找到 户籍/居住证派出所编码!请查询公安机构名称数据与表格数据是否一致;` });
  479. continue;
  480. }
  481. } catch (error) {
  482. errorList.push({ message: `${baseObject.name}: 补全 户籍/居住证派出所编码 失败!;` });
  483. continue;
  484. }
  485. // 创建保安员信息
  486. try {
  487. const baseResult = await this.createService.index({ table: 'security_guard_base' }, baseObject);
  488. if (baseResult) securityBaseObject = baseResult.data;
  489. } catch (error) {
  490. errorList.push({ message: `${baseObject.name}: 保安员信息创建 失败!;` });
  491. continue;
  492. }
  493. // 将保安员id放入证的数据中
  494. certObject.security_guard_id = securityBaseObject.id;
  495. // 创建保安证的信息;相关信息补全及创建
  496. // 如果是业务逻辑异常,则内部catch不需要向errorList输出信息.反之则需要输出带有 '失败' 的字样.提示是接口相关错误,而不是逻辑错误
  497. // 嵌套try...catch如果想到上一层try...catch中.只能使用通常异常,使用工具异常会直接跳出去
  498. try {
  499. /**
  500. * 保安证信息补全,企业查询.错误是否已经输出的判断变量
  501. */
  502. let companyHaveFault = false;
  503. try {
  504. if (certObject.company_name) {
  505. // 查询保安证所在企业的企业id,并赋值
  506. const companyResutl = await this.fetchService.index({ table: 'company_base' }, { name: certObject.company_name });
  507. if (companyResutl.data) {
  508. certObject.company_id = _.get(companyResutl, 'data.id');
  509. } else {
  510. // 业务逻辑错误.并非异常,但是需要抛出异常,触发删除
  511. errorList.push({ message: `${baseObject.name}补全保安证信息: 未找到 ${certObject.company_name} 相关信息!请查询是否有该企业信息;` });
  512. companyHaveFault = true;
  513. throw '接口错误';
  514. }
  515. }
  516. } catch (error) {
  517. if (!companyHaveFault) errorList.push({ message: `${baseObject.name}补全保安证信息: 查询 ${certObject.company_name} 相关信息 失败!;` });
  518. throw '接口错误';
  519. }
  520. // 创建保安证
  521. try {
  522. const certResult = await this.createService.index({ table: 'certificates_base' }, certObject);
  523. if (certResult) certificatesBaseObject = certResult.data;
  524. } catch (error) {
  525. errorList.push({ message: `${baseObject.name}: 保安证信息创建 失败!;` });
  526. throw '接口错误';
  527. }
  528. } catch (error) {
  529. // 删除保安员信息
  530. await this.deleteService.index({ table: 'security_guard_base' }, { id: securityBaseObject.id });
  531. continue;
  532. }
  533. } else {
  534. errorList.push(`已存在 ${baseObject.name} 保安员,不予处理`);
  535. }
  536. }
  537. return { errorList };
  538. }
  539. /**
  540. * excel批量办理入职
  541. * @param {String} uri 文件路径
  542. */
  543. async guardWork({ uri }) {
  544. assert(uri, '缺少文件地址,无法读取文件');
  545. const meta = require('../public/guardWorkMeta');
  546. const file = await this.getFile(uri);
  547. const workbook = new Excel.Workbook();
  548. await workbook.xlsx.load(file.data);
  549. const sheet = workbook.getWorksheet(1);
  550. const head = _.get(sheet.getRow(1), 'values', []);
  551. for (let i = 0; i < head.length; i++) {
  552. const e = head[i];
  553. if (!e) continue;
  554. const r = meta.find(f => f.zh === e);
  555. if (r) r.index = i;
  556. }
  557. /**
  558. * 判断当前用户是否是企业用户的变量
  559. */
  560. let is_company = false;
  561. /**
  562. * 从excel获取数据的字段
  563. */
  564. const getFormExcelArray = meta.filter(f => f.index);
  565. // 需要判断,如果当前用户不是企业,则必须要有 企业名称 列,否则直接提示错误
  566. if (this.ctx.user.table !== 'company_base') {
  567. const r = getFormExcelArray.find(f => f.key === 'company_name');
  568. if (!r) throw new BusinessError(ErrorCode.DATA_INVALID, '非企业用户,缺少企业名称会导致入职缺少企业相关信息!拒绝导入!');
  569. } else is_company = true;
  570. /**
  571. * 默认值字段
  572. */
  573. const defArray = meta.filter(f => f.default);
  574. /**
  575. * 查表处理
  576. */
  577. const tableArray = meta.filter(f => f.table);
  578. const errorList = [];
  579. const dataList = [];
  580. /**
  581. * 获取excel的数据
  582. */
  583. sheet.eachRow(async (row, index) => {
  584. if (index !== 1) {
  585. const values = row.values;
  586. const obj = {};
  587. for (const m of getFormExcelArray) {
  588. const { required, key, index, zh, format } = m;
  589. const value = values[index];
  590. if (required && !value) {
  591. // 必填且没值的情况
  592. errorList.push({ message: `第${index}行数据,缺少必填项 ${zh};` });
  593. continue;
  594. }
  595. if (format) {
  596. if (_.isFunction(format)) obj[key] = format(value);
  597. } else obj[key] = value;
  598. }
  599. dataList.push(obj);
  600. }
  601. });
  602. if (errorList.length > 0) return errorList;
  603. // 处理default值
  604. for (const d of dataList) {
  605. for (const m of defArray) {
  606. const { key, default: def } = m;
  607. if (_.isFunction(def)) d[key] = def(d[key]);
  608. else d[key] = def;
  609. }
  610. }
  611. // 取值函数,就这用,就不外面写了
  612. const getValue = (column, object) => {
  613. const { key, from } = column;
  614. const obj = { key };
  615. obj.value = object[from] || object[key];
  616. return obj;
  617. };
  618. // 补充数据,需要从别的表拽来的那些
  619. for (const d of dataList) {
  620. for (const object of tableArray) {
  621. const { query, table, columns, zh } = object;
  622. // 当前要处理的数据源
  623. let originData;
  624. if (table === 'company_base' && is_company) {
  625. // 补充企业信息,且当前用户为企业时:则不需要请求处理,直接从this.ctx.user中取出来就好
  626. d.company_name = this.ctx.user.name;
  627. originData = _.cloneDeep(this.ctx.user);
  628. } else {
  629. const q = {};
  630. for (const key in query) {
  631. const v = query[key];
  632. if (_.isFunction(v)) q[key] = v(d);
  633. else q[key] = v;
  634. }
  635. const r = await this.fetchService.index({ table }, q);
  636. if (r.data) originData = r.data;
  637. else {
  638. errorList.push({ message: `${d.name}: 未找到 ${zh} 信息` });
  639. continue;
  640. }
  641. }
  642. // 获取到了数据源,开始补值
  643. for (const column of columns) {
  644. const { key, value } = getValue(column, originData);
  645. d[key] = value;
  646. }
  647. }
  648. }
  649. if (errorList.length > 0) return { errorList };
  650. const res = await this.ctx.service.securityGuard.work.toWork({ data: dataList });
  651. return { errorList: res };
  652. }
  653. }
  654. module.exports = ImportService;