util.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. class UtilService extends CrudService {
  14. async updatedate() {
  15. let date = new Date();
  16. date = moment(date).format('YYYY-MM-DD HH:mm:ss');
  17. return date;
  18. }
  19. async sendMail(email, subject, text, html) {
  20. const setting = await this.ctx.model.Setting.findOne();
  21. let user_email = this.ctx.app.config.user_email;
  22. let auth_code = this.ctx.app.config.auth_code;
  23. if (setting) {
  24. user_email = setting.user_email;
  25. auth_code = setting.auth_code;
  26. }
  27. const transporter = nodemailer.createTransport({
  28. host: 'smtp.exmail.qq.com',
  29. secureConnection: true,
  30. port: 465,
  31. auth: {
  32. user: user_email, // 账号
  33. pass: auth_code, // 授权码
  34. },
  35. });
  36. const mailOptions = {
  37. from: user_email, // 发送者,与上面的user一致
  38. to: email, // 接收者,可以同时发送多个,以逗号隔开
  39. subject, // 标题
  40. text, // 文本
  41. html,
  42. };
  43. try {
  44. await transporter.sendMail(mailOptions);
  45. return true;
  46. } catch (err) {
  47. return false;
  48. }
  49. }
  50. async findone({ modelname }, data) {
  51. // 查询单条
  52. const _model = _.capitalize(modelname);
  53. const res = await this.ctx.model[_model].findOne({ ...data });
  54. return res;
  55. }
  56. async findbyids({ modelname }, { data }) {
  57. // 共通批量查询方法
  58. const _model = _.capitalize(modelname);
  59. const res = [];
  60. for (const elm of data) {
  61. const result = await this.ctx.model[_model].findById(elm);
  62. res.push(result);
  63. }
  64. return res;
  65. }
  66. async findmodel({ modelname }) {
  67. // TODO找到这个班级
  68. // const tclass = { name: '1', term: '', batch: '' };
  69. // 首先用名字找能不能对上,名字能对上直接把班级信息复制过来
  70. // 名字对不上就根据当前班级的期找下本期的所有班,然后找到和他同批次的班级,查下它是第几位,按照这个位置去把模板的同位置班级信息复制过来
  71. // 获取所有年度的期计划
  72. // 处理学生入学年份超过4位的情况
  73. // const list = await this.ctx.model.Student.find({ $where: 'this.entry_year.length>4' });
  74. // const m = /^\w{4}/;
  75. // for (const stu of list) {
  76. // const { entry_year } = stu;
  77. // const r = entry_year.match(m);
  78. // if (r) {
  79. // stu.entry_year = r[0];
  80. // stu.save();
  81. // }
  82. // }
  83. // 处理学生毕业年份超过4位的情况
  84. // const list = await this.ctx.model.Student.find({ $where: 'this.finish_year.length>4' });
  85. // const m = /^\w{4}/;
  86. // for (const stu of list) {
  87. // const { finish_year } = stu;
  88. // const r = finish_year.match(m);
  89. // if (r) {
  90. // stu.finish_year = r[0];
  91. // stu.save();
  92. // }
  93. // }
  94. // 处理学生专业字段(major)带'专业'二字
  95. // const stuList = await this.ctx.model.Student.find({ major: /专业/ });
  96. // const m = /(\S+)(专业$)/;
  97. // for (const stu of stuList) {
  98. // const { name, major } = stu;
  99. // const r = major.match(m);
  100. // let nm;
  101. // if (r) nm = r[1];
  102. // stu.major = nm;
  103. // stu.save();
  104. // }
  105. // 处理教师分数错误的初始化
  106. // // 所有教师的分数都复制到beforescore上,xsscore需要重新计算
  107. // const list = await this.ctx.model.Teacher.find();
  108. // console.log(list.length);
  109. // for (const tea of list) {
  110. // if (tea.xsscore) {
  111. // tea.beforescore = tea.xsscore;
  112. // await tea.save();
  113. // }
  114. // }
  115. }
  116. async toExcel(dataList, meta, fn = '导出结果') {
  117. // 导出excel
  118. const { app } = this;
  119. const nowDate = new Date().getTime();
  120. const filename = `${fn}-${nowDate}.xlsx`;
  121. // 取出预设存储地址
  122. const rootPath = `${app.config.cdn.repos_root_path}`;
  123. const rooturl = `${app.config.cdn.repos_root_url_excel}`;
  124. const path = `${rootPath}${rooturl}`;
  125. if (!path) {
  126. throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
  127. }
  128. // 如果不存在文件夹,就创建
  129. if (!fs.existsSync(path)) {
  130. fs.mkdirSync(path);
  131. }
  132. // 生成文件
  133. const filepath = `${path}${filename}`;
  134. fs.createWriteStream(filepath);
  135. const workbook = new Excel.Workbook();
  136. const sheet = workbook.addWorksheet('sheet');
  137. sheet.columns = meta;
  138. sheet.addRows(dataList);
  139. await workbook.xlsx.writeFile(filepath);
  140. return `/files/excel/${filename}`;
  141. }
  142. /**
  143. * 导出docx
  144. * @param {Array} data 数据[{title,content([]),author}]
  145. * @param {String} fn 文件名
  146. */
  147. async toDocx(data, fn = '培训心得') {
  148. const { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType } = docx;
  149. const doc = new Document();
  150. const children = [];
  151. // 整理数据
  152. for (let i = 0; i < data.length; i++) {
  153. const obj = data[i];
  154. const { title, content, author } = obj;
  155. const c = [];
  156. if (title) {
  157. const tit = new Paragraph({
  158. children: [ new TextRun({ text: title, bold: true }) ],
  159. heading: HeadingLevel.TITLE,
  160. alignment: AlignmentType.CENTER,
  161. });
  162. c.push(tit);
  163. }
  164. if (author) {
  165. const auth = new Paragraph({
  166. children: [ new TextRun({ color: '#000000', text: author }) ],
  167. heading: HeadingLevel.HEADING_2,
  168. alignment: AlignmentType.RIGHT,
  169. });
  170. c.push(auth);
  171. }
  172. if (content && _.isArray(content) && content.length > 0) {
  173. for (const cont of content) {
  174. const p = new Paragraph({
  175. children: [ new TextRun({ text: cont, bold: true }) ],
  176. });
  177. c.push(p);
  178. }
  179. }
  180. if (i !== data.length - 1) {
  181. // 换页
  182. const last = new Paragraph({
  183. pageBreakBefore: true,
  184. });
  185. c.push(last);
  186. }
  187. if (c.length > 0) children.push(...c);
  188. }
  189. doc.addSection({
  190. properties: {},
  191. children,
  192. });
  193. const { app } = this;
  194. const rootPath = `${app.config.cdn.repos_root_path}`;
  195. const rooturl = `${app.config.cdn.repos_root_url_experience}`;
  196. const path = `${rootPath}${rooturl}`;
  197. // 如果不存在文件夹,就创建
  198. if (!fs.existsSync(path)) {
  199. fs.mkdirSync(path);
  200. }
  201. const num = new Date().getTime();
  202. console.log(`filepath=>${path}${fn}-${num}.docx`);
  203. Packer.toBuffer(doc).then(buffer => {
  204. fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
  205. });
  206. console.log(`return path=>/files${rooturl}${fn}-${num}.docx`);
  207. return `/files${rooturl}${fn}-${num}.docx`;
  208. }
  209. }
  210. module.exports = UtilService;