util.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. 'use strict';
  2. const _ = require('lodash');
  3. const fs = require('fs');
  4. const Excel = require('exceljs');
  5. const { CrudService } = require('naf-framework-mongoose/lib/service');
  6. const { BusinessError, ErrorCode } = require('naf-core').Error;
  7. const moment = require('moment');
  8. const nodemailer = require('nodemailer');
  9. const docx = require('docx');
  10. const archiver = require('archiver');
  11. const { ObjectId } = require('mongoose').Types;
  12. class UtilService extends CrudService {
  13. constructor(ctx) {
  14. super(ctx);
  15. this.mq = this.ctx.mq;
  16. }
  17. async updatedate() {
  18. let date = new Date();
  19. date = moment(date).format('YYYY-MM-DD HH:mm:ss');
  20. return date;
  21. }
  22. async sendMail(email, subject, text, html) {
  23. const setting = await this.ctx.model.Setting.findOne();
  24. let user_email = this.ctx.app.config.user_email;
  25. let auth_code = this.ctx.app.config.auth_code;
  26. if (setting) {
  27. user_email = setting.user_email;
  28. auth_code = setting.auth_code;
  29. }
  30. const transporter = nodemailer.createTransport({
  31. host: 'smtp.exmail.qq.com',
  32. secureConnection: true,
  33. port: 465,
  34. auth: {
  35. user: user_email, // 账号
  36. pass: auth_code, // 授权码
  37. },
  38. });
  39. if (process.env.NODE_ENV === 'development') email = '402788946@qq.com';
  40. const mailOptions = {
  41. from: user_email, // 发送者,与上面的user一致
  42. to: email, // 接收者,可以同时发送多个,以逗号隔开
  43. subject, // 标题
  44. text, // 文本
  45. html,
  46. };
  47. try {
  48. await transporter.sendMail(mailOptions);
  49. return true;
  50. } catch (err) {
  51. return false;
  52. }
  53. }
  54. async findone({ modelname }, data) {
  55. // 查询单条
  56. const _model = _.capitalize(modelname);
  57. const res = await this.ctx.model[_model].findOne({ ...data });
  58. return res;
  59. }
  60. async findbyids({ modelname }, { data }) {
  61. // 共通批量查询方法
  62. const _model = _.capitalize(modelname);
  63. const res = [];
  64. for (const elm of data) {
  65. const result = await this.ctx.model[_model].findById(elm);
  66. res.push(result);
  67. }
  68. return res;
  69. }
  70. async findmodel({ modelname }) {
  71. const _model = _.capitalize(modelname);
  72. const data = this.ctx.model[_model].prototype.schema.obj;
  73. const keys = Object.keys(data);
  74. const res = {};
  75. for (const k of keys) {
  76. const obj = data[k];
  77. if (_.get(obj, 'zh')) res[k] = obj;
  78. }
  79. return res;
  80. }
  81. async utilMethod(query, body) {
  82. const crypto = require('crypto');
  83. // const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
  84. // modulusLength: 2048,
  85. // publicKeyEncoding: {
  86. // type: 'spki',
  87. // format: 'pem',
  88. // },
  89. // privateKeyEncoding: {
  90. // type: 'pkcs8',
  91. // format: 'pem',
  92. // cipher: 'aes-256-cbc',
  93. // passphrase: 'sks_secret',
  94. // },
  95. // });
  96. // console.log(publicKey);
  97. // console.log('-------------------');
  98. // console.log(privateKey);
  99. const privateKey = `-----BEGIN ENCRYPTED PRIVATE KEY-----
  100. MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIDrmClOodJAACAggA
  101. MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDFUSMr1yfAK9Lt9r4Idi5EBIIE
  102. 0JmkwwNWz+ya4IffVvzQS2fEbIRoAcxoBEKaZjLd2cCBQbE/0BODdlEWcJ7Z4Z3k
  103. 1NfDaqQToh+k9sE2C108q2jCETLiLPWodTMAMO0pVHBiiJeh2ZOoDxwyDWsyHYcA
  104. 6S7EhuQPOY7Fd8WKWeYcH1m8TLyL40fmWq0cseCoyvP1M0LRQ5OFKD7+uOYJBQhq
  105. 7pPwuJouorKMUHbxpribrOmr8fspfkglhP6+TZ7p7XzAs6e92mNz1SfiflmChoPH
  106. sqcJb0SJaHCdzblMT/yCF1Gm+h91gpU9L3sJUbivBtjFkX4LsE8DMs15dkPIbWHq
  107. YTJx+2QSOpGp6cK1D7LS8LInptHxI/UGRVYH3FLP1oUJuuFj+Wl7PKvOpZcc5uk1
  108. u5tbHmUkhpJeklqsCWi6brQpPxiQAiCeiqMiTOQLlwiRNTT51waOqrvb60onDHAw
  109. bEWfb0FQ0o3kb1yEQd/qPTJXWPZ96WhM3/7VNGoEDKwCzv5m8G1JbO4ct79iPF5x
  110. Y7rx0uKJFPbekNACRmb9U0f3DpXm8hdgCKVKmbkgG2ZZnnmJg2lzmT72lwQfdcnv
  111. O6IjXVLoDLjAYqjGe6HXQchi5rBHgmoguyHP6nGJ67n50hQ+upecVyp7k7LvuXi/
  112. jHF8/zJfDZaEvjf6CwkePYk9XbOnvyls+RWZM1ZwX4xbk6nbONzo71uRrI0E8sKS
  113. kWFCtvdbNJ//PaugrQNRMTs5z8Q3qMIyXRPXrxPFM/3lECaIEYvRrPyEYfdANqod
  114. JrMru+hbHvutTPhDuOgJ+RVgirwfZpxPQS5aHHXQu8bMps9DEL15i6xKOVX5lqvL
  115. lEL0YmqmRo7Idb9D+l0pyPAg9X1AwTziION/1cOPvWmjGVZqqaBv+jsMVlB+1o0o
  116. BrJBy9SEzw5ya7sYi1BCwO6k7qAsB1qSmnu8Gg/RXAIz6D9nJiJbB5kwusVQretn
  117. RqTj8ldBOgo+1la3hHzW8INTAVrlnmZjNMEGN4olYcJv6vHK9XTk7ruIkteJYD8Y
  118. 2jIFU3evsPTs4ugYxArEmzYsr1ZJR91aWf7naKcUlsap9k2zgHVw7Ks31D8T1OT3
  119. mXQZdkY9hx+0kdh8iSYOkCSa237usV/TbYg12ncB8xSVsMQMNnEQHWIcgFUBeZoo
  120. KQ27ijLmy5YYx8KYDAm7UJZ3Z00OrFa1D5qjippXkrDKbBwM3SrDSSVBtfKMGxyi
  121. epF9hMBs6ecBBSIF9sZg0LHfMlbOIWyNLklHUtdBQ4+vavzbG1V66p0pB7HJp6rp
  122. YC3moNRhHXyUFpvkIis7gFQ4xdE6FMEJsrHeZQHuOzRIB4XlwiCiAIg+IDmnP0hd
  123. 8Sxv0drpFU+zjR2aMr4bfhYdBG35NE/xfOp+ba25yCWCilljCIj46+jD0E2UvSrw
  124. 9iDtf75OXF2njqaLwmnFtHyAOA54IcXi9Y3NaXUXFxmK9iif5UbKa/dR+8rmOCF2
  125. cozCs4Ujribcpgs3cqjkVX0Z12B7YdCZM1Tpj7BgPOLjpi0h0s5wbP9QMm3yb2S2
  126. SKteoIypVcUXdpPqSwXtsUOUSuj7MeNXW2mkmQsH+W/n2Zn5OxgFKZ0JZ4z/OHei
  127. hK1UFYPQ9BEBMzsJ2ttBLwak//hAMaUJDT8EKxEoBdqb
  128. -----END ENCRYPTED PRIVATE KEY-----`;
  129. const publicKey = `-----BEGIN PUBLIC KEY-----
  130. MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqIvXeEZJ2kx0xZxgcWeC
  131. rRQ3+JQjgt7UOUN45PKnw5XNuRCxCbEvnjjLJwJZcrXo0qO54f5rafbPqYoSA5ed
  132. Wt07dY5dVJ02OUl56uIwhoFArJpUzMywFq94BvTaO5Gz58uVUDKgFRYYx9z4/t10
  133. 6AliNHb3KEbovwMqIpGYLIr5JmImfyaCi7eAqwHsLH/HALqGFLHcr6oFaquA9abP
  134. J/6wp465qmQuIUhqR9Nf3HIa/hjcF6ZTBlZhV//tzFH1wDIsUeMn8vS9Gi9vpwYa
  135. AQsB8qxp2yXhZrEbMdD+E27te67SxedRK3IZ13xAeuQ+XB0kVDIPkfRw8eUg3mK2
  136. XQIDAQAB
  137. -----END PUBLIC KEY-----`;
  138. const data = this.ctx.request.body;
  139. const enc = crypto.publicEncrypt(
  140. {
  141. key: publicKey,
  142. padding: crypto.constants.RSA_PKCS1_PADDING,
  143. },
  144. Buffer.from(JSON.stringify(data))
  145. );
  146. const str = enc.toString('base64');
  147. // const sb = Buffer.from(str, 'base64');
  148. // const decpub = crypto.privateDecrypt(
  149. // {
  150. // key: privateKey,
  151. // passphrase: 'sks_secret',
  152. // },
  153. // sb
  154. // );
  155. return enc.toString('base64');
  156. // for (const trainPlan of allTranPlan) {
  157. // const { termnum = [] } = trainPlan;
  158. // const termids = termnum.map(f => ObjectId(f._id).toString());
  159. // // 查出该期所有的申请
  160. // const termApplys = await applyModel.find({ termid: termids }).lean();
  161. // const needDeletes = termApplys.filter(f => !(allSubject.find(s => s._id === f.subid) && allTeacher.find(t => t._id === f.teacherid)));
  162. // const deleteIds = needDeletes.map(i => i._id);
  163. // console.log(deleteIds.length);
  164. // // await applyModel.deleteMany({ _id: deleteIds });
  165. // }
  166. // // // 重置班级
  167. // // 找到要删除的学生
  168. // const res = await this.ctx.model.Student.find({ school_name: '吉林职业技术学院', planid: '60769b703cead37068645fb2', termid: { $ne: '60779c0b2ec7ac704ce301ab' } });
  169. // // const test = res.map(f => {
  170. // // return { name: f.name, openid: f.openid, id: f._id };
  171. // // });
  172. // const ids = res.map(i => i._id);
  173. // await this.ctx.model.User.deleteMany({ uid: ids });
  174. // await this.ctx.model.Student.deleteMany({ school_name: '吉林职业技术学院', planid: '60769b703cead37068645fb2', termid: { $ne: '60779c0b2ec7ac704ce301ab' } });
  175. // const ids = res.map(i => i.openid);
  176. // // console.log(ids);
  177. //
  178. // console.log(userRes.length);
  179. // const { planid, termid, batchid, classid } = query;
  180. // const filters = {};
  181. // if (classid) filters.classid = classid;
  182. // else if (batchid)filters.batchid = batchid;
  183. // else if (termid)filters.termid = termid;
  184. // else if (planid)filters.planid = planid;
  185. // else throw new BusinessError(ErrorCode.BADPARAM, '需要重置范围');
  186. // await this.ctx.model.Student.updateMany(filters, { classid: undefined });
  187. // // 重置班级结束
  188. }
  189. async getQueryOptions({ skip, limit, sort, desc } = {}) {
  190. if (sort && _.isString(sort)) {
  191. sort = { [sort]: desc ? -1 : 1 };
  192. } else if (sort && _.isArray(sort)) {
  193. sort = sort.map(f => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {});
  194. }
  195. return { skip, limit, sort };
  196. }
  197. /**
  198. * 更新进度,状态 不存在/完成以外(不是2的值)的数值, 不添加参数,只更新进度
  199. * @param {String} missionid 任务id
  200. * @param {String} progress 进度(人为划分)
  201. * @param {String} status 状态
  202. * @param {Object} params 参数
  203. */
  204. async updateProcess(missionid, progress, status, params) {
  205. try {
  206. if (!missionid) return;
  207. const { baseUrl } = _.get(this.ctx.app.config, 'mission');
  208. let url = `${baseUrl}`;
  209. let data;
  210. if (!baseUrl) return;
  211. if (!status || status !== '2') {
  212. url = `${url}/api/mission/progress`;
  213. data = { id: missionid, progress };
  214. } else if (status === '3') {
  215. url = `${url}/api/mission/update/${missionid}`;
  216. data = { status };
  217. } else {
  218. url = `${url}/api/mission/update/${missionid}`;
  219. data = { progress: '100', params, status };
  220. }
  221. await this.ctx.curl(url, {
  222. method: 'post',
  223. headers: {
  224. 'content-type': 'application/json',
  225. },
  226. data,
  227. dataType: 'json',
  228. });
  229. } catch (error) {
  230. console.error(`任务更新进度报错,missionid:${missionid}\n`);
  231. }
  232. }
  233. async teacherImport() {
  234. // const filepath = './teacherlist.xlsx';
  235. // const workbook = new Excel.Workbook();
  236. // await workbook.xlsx.readFile(filepath);
  237. // const worksheet = workbook.getWorksheet(1);
  238. // if (!worksheet) return;
  239. // let arr = [];
  240. // worksheet.eachRow((row, ri) => {
  241. // if (ri !== 1) {
  242. // const obj = {};
  243. // obj.name = row.getCell(3).value || undefined;
  244. // obj.department = row.getCell(4).value || undefined;
  245. // if (row.getCell(5).value) obj.job = row.getCell(5).value;
  246. // obj.phone = row.getCell(6).value || undefined;
  247. // obj.status = '4';
  248. // arr.push(obj);
  249. // }
  250. // });
  251. // // 检查谁生成过了, user表和teacher表
  252. // let ur = await this.ctx.model.User.find({ mobile: { $in: arr.map(i => i.phone) }, type: '3' });
  253. // let tr = await this.ctx.model.Teacher.find({ phone: { $in: arr.map(i => i.phone) } });
  254. // // 将有的老师过滤出去
  255. // if (ur) {
  256. // ur = JSON.parse(JSON.stringify(ur));
  257. // arr = arr.filter(f => !ur.find(uf => `${uf.mobile}` === `${f.phone}`));
  258. // }
  259. // if (tr) {
  260. // tr = JSON.parse(JSON.stringify(tr));
  261. // arr = arr.filter(f => !(tr.find(tf => `${tf.phone}` === `${f.phone}`)));
  262. // }
  263. // for (const tea of arr) {
  264. // const ctr = await this.ctx.model.Teacher.create(tea);
  265. // if (ctr) {
  266. // const obj = { name: tea.name, mobile: tea.phone, type: '3', uid: ctr._id };
  267. // const cur = await this.ctx.model.User.create(obj);
  268. // }
  269. // }
  270. // const user = await this.ctx.model.User.find({ passwd: { $exists: false } });
  271. // for (const u of user) {
  272. // u.passwd = { secret: '12345678' };
  273. // u.save();
  274. // }
  275. }
  276. /**
  277. * 导出excel
  278. * @param {Array} dataList 数据集合
  279. * @param {Array} meta 表头(可以没有)
  280. * @param {String} fn 文件名
  281. * @param {Array} opera 单元格操作
  282. */
  283. async toExcel(dataList, meta, fn = '导出结果', opera) {
  284. // 导出excel
  285. const { app } = this;
  286. const nowDate = new Date().getTime();
  287. const filename = `${fn}-${nowDate}.xlsx`;
  288. // 取出预设存储地址
  289. const rootPath = `${app.config.cdn.repos_root_path}`;
  290. const rooturl = `${app.config.cdn.repos_root_url_excel}`;
  291. const path = `${rootPath}${rooturl}`;
  292. if (!path) {
  293. throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
  294. }
  295. // if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
  296. if (!fs.existsSync(path)) {
  297. // 如果不存在文件夹,就创建
  298. fs.mkdirSync(path);
  299. }
  300. // 生成文件
  301. const filepath = `${path}${filename}`;
  302. fs.createWriteStream(filepath);
  303. const workbook = new Excel.Workbook();
  304. const sheet = workbook.addWorksheet('sheet');
  305. if (meta) sheet.columns = meta;
  306. sheet.addRows(dataList);
  307. if (_.isArray(opera)) {
  308. for (const o of opera) {
  309. const { startRow, startCol, endRow, endCol } = o;
  310. sheet.mergeCells(startRow, startCol, endRow, endCol);
  311. }
  312. }
  313. // 垂直居中
  314. const length = dataList.length;
  315. const alignment = { vertical: 'middle', horizontal: 'center' };
  316. for (let i = 1; i <= length; i++) {
  317. sheet.getRow(i).alignment = alignment;
  318. }
  319. await workbook.xlsx.writeFile(filepath);
  320. return `/files/excel/${filename}`;
  321. }
  322. /**
  323. * 创建/重复写入excel
  324. * @param {Array} dataList 数据
  325. * @param {String} fn 文件名, 第一次进入时,只是单纯的名,第二次开始,有后缀与时间戳
  326. * @param {String} downloadPath 下载文件路径
  327. * @param {String} type 方向,没有默认横向
  328. */
  329. async toAsyncExcel(dataList, fn = '导出结果', downloadPath, type) {
  330. const { app } = this;
  331. const workbook = new Excel.Workbook();
  332. let sheet;
  333. // 取出预设存储地址
  334. const rootPath = `${app.config.cdn.repos_root_path}`;
  335. const rooturl = `${app.config.cdn.repos_root_url_excel}`;
  336. let path = `${rootPath}${rooturl}`;
  337. let filepath = '';
  338. if (!path) {
  339. throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
  340. }
  341. if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\excel\\';
  342. if (!fs.existsSync(path)) {
  343. // 如果不存在文件夹,就创建
  344. fs.mkdirSync(path);
  345. }
  346. if (!downloadPath) {
  347. // 第一次进入,文件还未生成
  348. const nowDate = new Date().getTime();
  349. fn = `${fn}-${nowDate}.xlsx`;
  350. sheet = workbook.addWorksheet('sheet');
  351. } else {
  352. let domain = 'http://127.0.0.1';
  353. if (process.env.NODE_ENV === 'development') domain = `${domain}:8000`;
  354. const file = await this.ctx.curl(`${domain}${downloadPath}`);
  355. if (!(file && file.data)) {
  356. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找导出的excel');
  357. }
  358. // 读取文件
  359. await workbook.xlsx.load(file.data);
  360. sheet = workbook.getWorksheet('sheet');
  361. }
  362. if (!type || type === 'horizontal') sheet.addRows(dataList);
  363. else if (type === 'vertical') {
  364. for (let i = 1; i <= dataList.length; i++) {
  365. const element = dataList[i - 1];
  366. const rows = sheet.getRow(i);
  367. if (rows.values.length <= 0) rows.values = element;
  368. else rows.values = rows.values.concat(element);
  369. rows.commit();
  370. }
  371. }
  372. filepath = `${path}${fn}`;
  373. await workbook.xlsx.writeFile(filepath);
  374. return { downloadPath: `/files/excel/${fn}`, fn };
  375. }
  376. /**
  377. * 导出docx
  378. * @param {Array} data 数据[{title,content([]),author}]
  379. * @param {String} fn 文件名
  380. */
  381. async toDocx(data, fn = '培训心得') {
  382. const { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType } = docx;
  383. const doc = new Document();
  384. const children = [];
  385. // 整理数据
  386. for (let i = 0; i < data.length; i++) {
  387. const obj = data[i];
  388. const { title, content, author } = obj;
  389. const c = [];
  390. if (title) {
  391. const tit = new Paragraph({
  392. children: [ new TextRun({ text: title, bold: true }) ],
  393. heading: HeadingLevel.TITLE,
  394. alignment: AlignmentType.CENTER,
  395. });
  396. c.push(tit);
  397. }
  398. if (author) {
  399. const auth = new Paragraph({
  400. children: [ new TextRun({ color: '#000000', text: author }) ],
  401. heading: HeadingLevel.HEADING_2,
  402. alignment: AlignmentType.RIGHT,
  403. });
  404. c.push(auth);
  405. }
  406. if (content && _.isArray(content) && content.length > 0) {
  407. for (const cont of content) {
  408. const p = new Paragraph({
  409. children: [ new TextRun({ text: cont, bold: true }) ],
  410. });
  411. c.push(p);
  412. }
  413. }
  414. if (i !== data.length - 1) {
  415. // 换页
  416. const last = new Paragraph({
  417. pageBreakBefore: true,
  418. });
  419. c.push(last);
  420. }
  421. if (c.length > 0) children.push(...c);
  422. }
  423. doc.addSection({
  424. properties: {},
  425. children,
  426. });
  427. const { app } = this;
  428. const rootPath = `${app.config.cdn.repos_root_path}`;
  429. const rooturl = `${app.config.cdn.repos_root_url_experience}`;
  430. const path = `${rootPath}${rooturl}`;
  431. // 如果不存在文件夹,就创建
  432. // if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
  433. if (!fs.existsSync(path)) {
  434. fs.mkdirSync(path);
  435. }
  436. const num = new Date().getTime();
  437. const buffer = await Packer.toBuffer(doc);
  438. fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
  439. // Packer.toBuffer(doc).then(buffer => {
  440. // fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
  441. // });
  442. return `/files${rooturl}${fn}-${num}.docx`;
  443. }
  444. /**
  445. * 将选择的文件导出到zip压缩包中,提供下载
  446. * @param {*} fileList 需要导入到zip中的列表,格式有2中: [{url:""}]或[String]
  447. * @param {*} fn 文件名,默认为 "导出结果"
  448. */
  449. async toZip(fileList, fn = '导出结果') {
  450. if (!_.isArray(fileList)) {
  451. throw new BusinessError(ErrorCode.DATA_INVALID, '需要压缩的文件数据格式错误');
  452. }
  453. fn = `${fn}.zip`;
  454. // zip文件夹创建
  455. const { app } = this;
  456. const rootPath = `${app.config.cdn.repos_root_path}`;
  457. const zipPath = `${app.config.cdn.repos_root_url_zip}`;
  458. let path = `${rootPath}${zipPath}`;
  459. if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
  460. if (!fs.existsSync(path)) {
  461. fs.mkdirSync(path);
  462. }
  463. // 文件请求后将数据整理到这里
  464. const resetFileList = [];
  465. for (const file of fileList) {
  466. let uri = '';
  467. let filename = '';
  468. let prefixs;
  469. if (_.isString(file)) {
  470. uri = file;
  471. const arr = file.split('/');
  472. const last = _.last(arr);
  473. if (last) filename = last;
  474. } else if (_.isObject(file)) {
  475. const { uri: furi, url: furl, name, prefix } = file;
  476. if (furi) uri = furi;
  477. else if (furl) uri = furl;
  478. if (name) filename = name;
  479. else {
  480. const arr = uri.split('/');
  481. const last = _.last(arr);
  482. if (last) filename = last;
  483. }
  484. if (prefix) prefixs = prefix;
  485. }
  486. const obj = {};
  487. if (uri) obj.uri = uri;
  488. if (filename) obj.filename = filename;
  489. if (prefixs) obj.prefix = prefixs;
  490. resetFileList.push(obj);
  491. }
  492. // 导出
  493. const output = fs.createWriteStream(`${path}${fn}`);
  494. const archive = archiver('zip', {
  495. zlib: { level: 9 },
  496. });
  497. archive.pipe(output);
  498. // 请求文件,追加进压缩包
  499. for (const file of resetFileList) {
  500. const { uri, filename, prefix } = file;
  501. const res = await this.ctx.curl(`http://127.0.0.1${uri}`);
  502. if (res && res.data) {
  503. const options = {};
  504. if (filename) options.name = filename;
  505. if (prefix) options.prefix = prefix;
  506. if (filename) {
  507. archive.append(res.data, options);
  508. }
  509. }
  510. }
  511. await archive.finalize();
  512. return `/files${zipPath}${fn}`;
  513. }
  514. }
  515. module.exports = UtilService;