util.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. 'use strict';
  2. const assert = require('assert');
  3. const _ = require('lodash');
  4. const fs = require('fs');
  5. const Excel = require('exceljs');
  6. const { ObjectId } = require('mongoose').Types;
  7. const { CrudService } = require('naf-framework-mongoose/lib/service');
  8. const { BusinessError, ErrorCode } = require('naf-core').Error;
  9. const moment = require('moment');
  10. const nodemailer = require('nodemailer');
  11. const { template } = require('lodash');
  12. const docx = require('docx');
  13. const archiver = require('archiver');
  14. class UtilService extends CrudService {
  15. async updatedate() {
  16. let date = new Date();
  17. date = moment(date).format('YYYY-MM-DD HH:mm:ss');
  18. return date;
  19. }
  20. async sendMail(email, subject, text, html) {
  21. const setting = await this.ctx.model.Setting.findOne();
  22. let user_email = this.ctx.app.config.user_email;
  23. let auth_code = this.ctx.app.config.auth_code;
  24. if (setting) {
  25. user_email = setting.user_email;
  26. auth_code = setting.auth_code;
  27. }
  28. const transporter = nodemailer.createTransport({
  29. host: 'smtp.exmail.qq.com',
  30. secureConnection: true,
  31. port: 465,
  32. auth: {
  33. user: user_email, // 账号
  34. pass: auth_code, // 授权码
  35. },
  36. });
  37. const mailOptions = {
  38. from: user_email, // 发送者,与上面的user一致
  39. to: email, // 接收者,可以同时发送多个,以逗号隔开
  40. subject, // 标题
  41. text, // 文本
  42. html,
  43. };
  44. try {
  45. await transporter.sendMail(mailOptions);
  46. return true;
  47. } catch (err) {
  48. return false;
  49. }
  50. }
  51. async findone({ modelname }, data) {
  52. // 查询单条
  53. const _model = _.capitalize(modelname);
  54. const res = await this.ctx.model[_model].findOne({ ...data });
  55. return res;
  56. }
  57. async findbyids({ modelname }, { data }) {
  58. // 共通批量查询方法
  59. const _model = _.capitalize(modelname);
  60. const res = [];
  61. for (const elm of data) {
  62. const result = await this.ctx.model[_model].findById(elm);
  63. res.push(result);
  64. }
  65. return res;
  66. }
  67. async findmodel({ modelname }) {
  68. const _model = _.capitalize(modelname);
  69. const data = this.ctx.model[_model].prototype.schema.obj;
  70. return data;
  71. }
  72. async utilMethod(data, body) {
  73. // 将指定计划,期数的教师状态解除确认
  74. let trainPlan = await this.ctx.model.Trainplan.findById('5f5adb337ceb003386c9b0d4');
  75. trainPlan = JSON.parse(JSON.stringify(trainPlan));
  76. let terms = {};
  77. for (const term of trainPlan.termnum) {
  78. if (ObjectId('5f5aed5e69b4221aedaa5005').equals(term._id)) {
  79. for (const batch of term.batchnum) {
  80. for (const c of batch.class) {
  81. for (const l of c.lessons) {
  82. l.status = '0';
  83. }
  84. }
  85. }
  86. terms = term;
  87. }
  88. }
  89. const i = trainPlan.termnum.findIndex(f => ObjectId('5f5aed5e69b4221aedaa5005').equals(f._id));
  90. trainPlan.termnum[i] = terms;
  91. delete trainPlan.meta;
  92. const r = await this.ctx.model.Trainplan.update(
  93. { _id: ObjectId('5f5adb337ceb003386c9b0d4') },
  94. { ...trainPlan }
  95. );
  96. console.log(r);
  97. // const output = fs.createWriteStream('E:\\exportFile\\test.zip');
  98. // const archive = archiver('zip', {
  99. // zlib: { level: 9 },
  100. // });
  101. // archive.pipe(output);
  102. // const res = await this.ctx.curl('http://jytz.jilinjobs.cn/files/task/20200914174650.jpg');
  103. // if (res && res.data) {
  104. // archive.append(res.data, { name: 'test.jpg', prefix: 'test/test2/test3' });
  105. // }
  106. // archive.finalize();
  107. }
  108. async teacherImport() {
  109. // const filepath = './teacherlist.xlsx';
  110. // const workbook = new Excel.Workbook();
  111. // await workbook.xlsx.readFile(filepath);
  112. // const worksheet = workbook.getWorksheet(1);
  113. // if (!worksheet) return;
  114. // let arr = [];
  115. // worksheet.eachRow((row, ri) => {
  116. // if (ri !== 1) {
  117. // const obj = {};
  118. // obj.name = row.getCell(3).value || undefined;
  119. // obj.department = row.getCell(4).value || undefined;
  120. // if (row.getCell(5).value) obj.job = row.getCell(5).value;
  121. // obj.phone = row.getCell(6).value || undefined;
  122. // obj.status = '4';
  123. // arr.push(obj);
  124. // }
  125. // });
  126. // // 检查谁生成过了, user表和teacher表
  127. // let ur = await this.ctx.model.User.find({ mobile: { $in: arr.map(i => i.phone) }, type: '3' });
  128. // let tr = await this.ctx.model.Teacher.find({ phone: { $in: arr.map(i => i.phone) } });
  129. // // 将有的老师过滤出去
  130. // if (ur) {
  131. // ur = JSON.parse(JSON.stringify(ur));
  132. // arr = arr.filter(f => !ur.find(uf => `${uf.mobile}` === `${f.phone}`));
  133. // }
  134. // if (tr) {
  135. // tr = JSON.parse(JSON.stringify(tr));
  136. // arr = arr.filter(f => !(tr.find(tf => `${tf.phone}` === `${f.phone}`)));
  137. // }
  138. // for (const tea of arr) {
  139. // const ctr = await this.ctx.model.Teacher.create(tea);
  140. // if (ctr) {
  141. // const obj = { name: tea.name, mobile: tea.phone, type: '3', uid: ctr._id };
  142. // const cur = await this.ctx.model.User.create(obj);
  143. // }
  144. // }
  145. // const user = await this.ctx.model.User.find({ passwd: { $exists: false } });
  146. // console.log(user.length);
  147. // for (const u of user) {
  148. // u.passwd = { secret: '12345678' };
  149. // u.save();
  150. // }
  151. }
  152. async toExcel(dataList, meta, fn = '导出结果') {
  153. console.log(meta);
  154. // 导出excel
  155. const { app } = this;
  156. const nowDate = new Date().getTime();
  157. const filename = `${fn}-${nowDate}.xlsx`;
  158. // 取出预设存储地址
  159. const rootPath = `${app.config.cdn.repos_root_path}`;
  160. const rooturl = `${app.config.cdn.repos_root_url_excel}`;
  161. let path = `${rootPath}${rooturl}`;
  162. if (!path) {
  163. throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
  164. }
  165. if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
  166. if (!fs.existsSync(path)) {
  167. // 如果不存在文件夹,就创建
  168. fs.mkdirSync(path);
  169. }
  170. // 生成文件
  171. const filepath = `${path}${filename}`;
  172. fs.createWriteStream(filepath);
  173. const workbook = new Excel.Workbook();
  174. const sheet = workbook.addWorksheet('sheet');
  175. sheet.columns = meta;
  176. sheet.addRows(dataList);
  177. await workbook.xlsx.writeFile(filepath);
  178. return `/files/excel/${filename}`;
  179. }
  180. /**
  181. * 导出docx
  182. * @param {Array} data 数据[{title,content([]),author}]
  183. * @param {String} fn 文件名
  184. */
  185. async toDocx(data, fn = '培训心得') {
  186. const {
  187. Document,
  188. Packer,
  189. Paragraph,
  190. TextRun,
  191. HeadingLevel,
  192. AlignmentType,
  193. } = docx;
  194. const doc = new Document();
  195. const children = [];
  196. // 整理数据
  197. for (let i = 0; i < data.length; i++) {
  198. const obj = data[i];
  199. const { title, content, author } = obj;
  200. const c = [];
  201. if (title) {
  202. const tit = new Paragraph({
  203. children: [ new TextRun({ text: title, bold: true }) ],
  204. heading: HeadingLevel.TITLE,
  205. alignment: AlignmentType.CENTER,
  206. });
  207. c.push(tit);
  208. }
  209. if (author) {
  210. const auth = new Paragraph({
  211. children: [ new TextRun({ color: '#000000', text: author }) ],
  212. heading: HeadingLevel.HEADING_2,
  213. alignment: AlignmentType.RIGHT,
  214. });
  215. c.push(auth);
  216. }
  217. if (content && _.isArray(content) && content.length > 0) {
  218. for (const cont of content) {
  219. const p = new Paragraph({
  220. children: [ new TextRun({ text: cont, bold: true }) ],
  221. });
  222. c.push(p);
  223. }
  224. }
  225. if (i !== data.length - 1) {
  226. // 换页
  227. const last = new Paragraph({
  228. pageBreakBefore: true,
  229. });
  230. c.push(last);
  231. }
  232. if (c.length > 0) children.push(...c);
  233. }
  234. doc.addSection({
  235. properties: {},
  236. children,
  237. });
  238. const { app } = this;
  239. const rootPath = `${app.config.cdn.repos_root_path}`;
  240. const rooturl = `${app.config.cdn.repos_root_url_experience}`;
  241. let path = `${rootPath}${rooturl}`;
  242. // 如果不存在文件夹,就创建
  243. if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
  244. if (!fs.existsSync(path)) {
  245. fs.mkdirSync(path);
  246. }
  247. const num = new Date().getTime();
  248. const buffer = await Packer.toBuffer(doc);
  249. fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
  250. // Packer.toBuffer(doc).then(buffer => {
  251. // fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
  252. // });
  253. return `/files${rooturl}${fn}-${num}.docx`;
  254. }
  255. /**
  256. * 将选择的文件导出到zip压缩包中,提供下载
  257. * @param {*} fileList 需要导入到zip中的列表,格式有2中: [{url:""}]或[String]
  258. * @param {*} fn 文件名,默认为 "导出结果"
  259. */
  260. async toZip(fileList, fn = '导出结果') {
  261. if (!_.isArray(fileList)) {
  262. throw new BusinessError(ErrorCode.DATA_INVALID, '需要压缩的文件数据格式错误');
  263. }
  264. fn = `${fn}.zip`;
  265. // zip文件夹创建
  266. const { app } = this;
  267. const rootPath = `${app.config.cdn.repos_root_path}`;
  268. const zipPath = `${app.config.cdn.repos_root_url_zip}`;
  269. let path = `${rootPath}${zipPath}`;
  270. if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
  271. if (!fs.existsSync(path)) {
  272. fs.mkdirSync(path);
  273. }
  274. // 文件请求后将数据整理到这里
  275. const resetFileList = [];
  276. for (const file of fileList) {
  277. let uri = '';
  278. let filename = '';
  279. let prefixs;
  280. if (_.isString(file)) {
  281. uri = file;
  282. const arr = file.split('/');
  283. const last = _.last(arr);
  284. if (last) filename = last;
  285. } else if (_.isObject(file)) {
  286. const { uri: furi, url: furl, name, prefix } = file;
  287. if (furi) uri = furi;
  288. else if (furl) uri = furl;
  289. if (name) filename = name;
  290. else {
  291. const arr = uri.split('/');
  292. const last = _.last(arr);
  293. if (last) filename = last;
  294. }
  295. if (prefix) prefixs = prefix;
  296. }
  297. const obj = {};
  298. if (uri) obj.uri = uri;
  299. if (filename)obj.filename = filename;
  300. if (prefixs) obj.prefix = prefixs;
  301. resetFileList.push(obj);
  302. }
  303. // 导出
  304. const output = fs.createWriteStream(`${path}${fn}`);
  305. const archive = archiver('zip', {
  306. zlib: { level: 9 },
  307. });
  308. archive.pipe(output);
  309. // 请求文件,追加进压缩包
  310. for (const file of resetFileList) {
  311. const { uri, filename, prefix } = file;
  312. const res = await this.ctx.curl(`http://127.0.0.1${uri}`);
  313. if (res && res.data) {
  314. const options = {};
  315. if (filename)options.name = filename;
  316. if (prefix) options.prefix = prefix;
  317. if (filename) { archive.append(res.data, options); }
  318. }
  319. }
  320. await archive.finalize();
  321. return `/files${zipPath}${fn}`;
  322. }
  323. }
  324. module.exports = UtilService;