lrf402788946 4 年之前
父節點
當前提交
b4dbf8d3f4
共有 2 個文件被更改,包括 1652 次插入1457 次删除
  1. 1205 1028
      app/service/trainplan.js
  2. 447 429
      app/service/util.js

File diff suppressed because it is too large
+ 1205 - 1028
app/service/trainplan.js


+ 447 - 429
app/service/util.js

@@ -1,429 +1,447 @@
-'use strict';
-
-
-const assert = require('assert');
-const _ = require('lodash');
-const fs = require('fs');
-const Excel = require('exceljs');
-const { ObjectId } = require('mongoose').Types;
-const { CrudService } = require('naf-framework-mongoose/lib/service');
-const { BusinessError, ErrorCode } = require('naf-core').Error;
-const moment = require('moment');
-const nodemailer = require('nodemailer');
-const { template } = require('lodash');
-const docx = require('docx');
-const archiver = require('archiver');
-class UtilService extends CrudService {
-  constructor(ctx) {
-    super(ctx);
-    this.mq = this.ctx.mq;
-  }
-  async updatedate() {
-    let date = new Date();
-    date = moment(date).format('YYYY-MM-DD HH:mm:ss');
-    return date;
-  }
-
-  async sendMail(email, subject, text, html) {
-    const setting = await this.ctx.model.Setting.findOne();
-    let user_email = this.ctx.app.config.user_email;
-    let auth_code = this.ctx.app.config.auth_code;
-    if (setting) {
-      user_email = setting.user_email;
-      auth_code = setting.auth_code;
-    }
-    const transporter = nodemailer.createTransport({
-      host: 'smtp.exmail.qq.com',
-      secureConnection: true,
-      port: 465,
-      auth: {
-        user: user_email, // 账号
-        pass: auth_code, // 授权码
-      },
-    });
-    const mailOptions = {
-      from: user_email, // 发送者,与上面的user一致
-      to: email, // 接收者,可以同时发送多个,以逗号隔开
-      subject, // 标题
-      text, // 文本
-      html,
-    };
-
-    try {
-      await transporter.sendMail(mailOptions);
-      return true;
-    } catch (err) {
-      return false;
-    }
-  }
-
-  async findone({ modelname }, data) {
-    // 查询单条
-    const _model = _.capitalize(modelname);
-    const res = await this.ctx.model[_model].findOne({ ...data });
-    return res;
-  }
-
-  async findbyids({ modelname }, { data }) {
-    // 共通批量查询方法
-    const _model = _.capitalize(modelname);
-    const res = [];
-    for (const elm of data) {
-      const result = await this.ctx.model[_model].findById(elm);
-      res.push(result);
-    }
-    return res;
-  }
-  async findmodel({ modelname }) {
-    const _model = _.capitalize(modelname);
-    const data = this.ctx.model[_model].prototype.schema.obj;
-    const keys = Object.keys(data);
-    const res = {};
-    for (const k of keys) {
-      const obj = data[k];
-      if (_.get(obj, 'zh')) res[k] = obj;
-    }
-    return res;
-  }
-  async utilMethod(query, body) {
-    // const ch = await this.ctx.amqp.conn.createChannel();
-    // const queue = 'hello';
-    // ch.consume(
-    //   queue,
-    //   msg => {
-    //     console.log(' [x] Received %s', msg.content.toString());
-    //   },
-    //   { noAck: true }
-    // );
-    // // ch.close();
-  }
-  async getQueryOptions({ skip, limit, sort, desc } = {}) {
-    if (sort && _.isString(sort)) {
-      sort = { [sort]: desc ? -1 : 1 };
-    } else if (sort && _.isArray(sort)) {
-      sort = sort
-        .map(f => ({ [f]: desc ? -1 : 1 }))
-        .reduce((p, c) => ({ ...p, ...c }), {});
-    }
-    return { skip, limit, sort };
-  }
-
-  /**
-   * 更新进度,状态 不存在/完成以外(不是2的值)的数值, 不添加参数,只更新进度
-   * @param {String} missionid 任务id
-   * @param {String} progress 进度(人为划分)
-   * @param {String} status 状态
-   * @param {Object} params 参数
-   */
-  async updateProcess(missionid, progress, status, params) {
-    try {
-      if (!missionid) return;
-      const { baseUrl } = _.get(this.ctx.app.config, 'mission');
-      let url = `${baseUrl}`;
-      let data;
-      if (!baseUrl) return;
-      if (!status || status !== '2') {
-        url = `${url}/api/mission/progress`;
-        data = { id: missionid, progress };
-      } else if (status === '3') {
-        url = `${url}/api/mission/update/${missionid}`;
-        data = { status };
-      } else {
-        url = `${url}/api/mission/update/${missionid}`;
-        data = { progress: '100', params, status };
-      }
-      await this.ctx.curl(url, {
-        method: 'post',
-        headers: {
-          'content-type': 'application/json',
-        },
-        data,
-        dataType: 'json',
-      });
-    } catch (error) {
-      console.error(`任务更新进度报错,missionid:${missionid}\n`);
-    }
-  }
-
-  async teacherImport() {
-    // const filepath = './teacherlist.xlsx';
-    // const workbook = new Excel.Workbook();
-    // await workbook.xlsx.readFile(filepath);
-    // const worksheet = workbook.getWorksheet(1);
-    // if (!worksheet) return;
-    // let arr = [];
-    // worksheet.eachRow((row, ri) => {
-    //   if (ri !== 1) {
-    //     const obj = {};
-    //     obj.name = row.getCell(3).value || undefined;
-    //     obj.department = row.getCell(4).value || undefined;
-    //     if (row.getCell(5).value) obj.job = row.getCell(5).value;
-    //     obj.phone = row.getCell(6).value || undefined;
-    //     obj.status = '4';
-    //     arr.push(obj);
-    //   }
-    // });
-    // // 检查谁生成过了, user表和teacher表
-    // let ur = await this.ctx.model.User.find({ mobile: { $in: arr.map(i => i.phone) }, type: '3' });
-    // let tr = await this.ctx.model.Teacher.find({ phone: { $in: arr.map(i => i.phone) } });
-    // // 将有的老师过滤出去
-    // if (ur) {
-    //   ur = JSON.parse(JSON.stringify(ur));
-    //   arr = arr.filter(f => !ur.find(uf => `${uf.mobile}` === `${f.phone}`));
-    // }
-    // if (tr) {
-    //   tr = JSON.parse(JSON.stringify(tr));
-    //   arr = arr.filter(f => !(tr.find(tf => `${tf.phone}` === `${f.phone}`)));
-    // }
-    // for (const tea of arr) {
-    //   const ctr = await this.ctx.model.Teacher.create(tea);
-    //   if (ctr) {
-    //     const obj = { name: tea.name, mobile: tea.phone, type: '3', uid: ctr._id };
-    //     const cur = await this.ctx.model.User.create(obj);
-    //   }
-    // }
-    // const user = await this.ctx.model.User.find({ passwd: { $exists: false } });
-    // for (const u of user) {
-    //   u.passwd = { secret: '12345678' };
-    //   u.save();
-    // }
-  }
-
-  async toExcel(dataList, meta, fn = '导出结果') {
-    // 导出excel
-    const { app } = this;
-    const nowDate = new Date().getTime();
-    const filename = `${fn}-${nowDate}.xlsx`;
-    // 取出预设存储地址
-    const rootPath = `${app.config.cdn.repos_root_path}`;
-    const rooturl = `${app.config.cdn.repos_root_url_excel}`;
-    let path = `${rootPath}${rooturl}`;
-    if (!path) {
-      throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
-    }
-    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
-    if (!fs.existsSync(path)) {
-      // 如果不存在文件夹,就创建
-      fs.mkdirSync(path);
-    }
-    // 生成文件
-    const filepath = `${path}${filename}`;
-    fs.createWriteStream(filepath);
-    const workbook = new Excel.Workbook();
-    const sheet = workbook.addWorksheet('sheet');
-    sheet.columns = meta;
-    sheet.addRows(dataList);
-    await workbook.xlsx.writeFile(filepath);
-    return `/files/excel/${filename}`;
-  }
-
-  /**
-   * 创建/重复写入excel
-   * @param {Array} dataList 数据
-   * @param {String} fn 文件名, 第一次进入时,只是单纯的名,第二次开始,有后缀与时间戳
-   * @param {String} downloadPath 下载文件路径
-   * @param {String} type 方向,没有默认横向
-   */
-  async toAsyncExcel(dataList, fn = '导出结果', downloadPath, type) {
-    const { app } = this;
-    const workbook = new Excel.Workbook();
-    let sheet;
-    // 取出预设存储地址
-    const rootPath = `${app.config.cdn.repos_root_path}`;
-    const rooturl = `${app.config.cdn.repos_root_url_excel}`;
-    let path = `${rootPath}${rooturl}`;
-    let filepath = '';
-
-    if (!path) {
-      throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
-    }
-    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\excel\\';
-    if (!fs.existsSync(path)) {
-      // 如果不存在文件夹,就创建
-      fs.mkdirSync(path);
-    }
-
-    if (!downloadPath) {
-      // 第一次进入,文件还未生成
-      const nowDate = new Date().getTime();
-      fn = `${fn}-${nowDate}.xlsx`;
-      sheet = workbook.addWorksheet('sheet');
-    } else {
-      let domain = 'http://127.0.0.1';
-      if (process.env.NODE_ENV === 'development')domain = `${domain}:8000`;
-      const file = await this.ctx.curl(`${domain}${downloadPath}`);
-      if (!(file && file.data)) {
-        throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找导出的excel');
-      }
-      // 读取文件
-      await workbook.xlsx.load(file.data);
-      sheet = workbook.getWorksheet('sheet');
-    }
-    if (!type || type === 'horizontal') sheet.addRows(dataList);
-    else if (type === 'vertical') {
-      for (let i = 1; i <= dataList.length; i++) {
-        const element = dataList[i - 1];
-        const rows = sheet.getRow(i);
-        if (rows.values.length <= 0) rows.values = element;
-        else rows.values = rows.values.concat(element);
-        rows.commit();
-      }
-    }
-    filepath = `${path}${fn}`;
-    await workbook.xlsx.writeFile(filepath);
-    return { downloadPath: `/files/excel/${fn}`, fn };
-  }
-
-  /**
-   * 导出docx
-   * @param {Array} data 数据[{title,content([]),author}]
-   * @param {String} fn 文件名
-   */
-  async toDocx(data, fn = '培训心得') {
-    const {
-      Document,
-      Packer,
-      Paragraph,
-      TextRun,
-      HeadingLevel,
-      AlignmentType,
-    } = docx;
-    const doc = new Document();
-    const children = [];
-    // 整理数据
-    for (let i = 0; i < data.length; i++) {
-      const obj = data[i];
-      const { title, content, author } = obj;
-      const c = [];
-      if (title) {
-        const tit = new Paragraph({
-          children: [ new TextRun({ text: title, bold: true }) ],
-          heading: HeadingLevel.TITLE,
-          alignment: AlignmentType.CENTER,
-        });
-        c.push(tit);
-      }
-      if (author) {
-        const auth = new Paragraph({
-          children: [ new TextRun({ color: '#000000', text: author }) ],
-          heading: HeadingLevel.HEADING_2,
-          alignment: AlignmentType.RIGHT,
-        });
-        c.push(auth);
-      }
-      if (content && _.isArray(content) && content.length > 0) {
-        for (const cont of content) {
-          const p = new Paragraph({
-            children: [ new TextRun({ text: cont, bold: true }) ],
-          });
-          c.push(p);
-        }
-      }
-      if (i !== data.length - 1) {
-        // 换页
-        const last = new Paragraph({
-          pageBreakBefore: true,
-        });
-        c.push(last);
-      }
-      if (c.length > 0) children.push(...c);
-    }
-    doc.addSection({
-      properties: {},
-      children,
-    });
-
-    const { app } = this;
-    const rootPath = `${app.config.cdn.repos_root_path}`;
-    const rooturl = `${app.config.cdn.repos_root_url_experience}`;
-    let path = `${rootPath}${rooturl}`;
-    // 如果不存在文件夹,就创建
-    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
-    if (!fs.existsSync(path)) {
-      fs.mkdirSync(path);
-    }
-    const num = new Date().getTime();
-    const buffer = await Packer.toBuffer(doc);
-    fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
-    // Packer.toBuffer(doc).then(buffer => {
-    //   fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
-    // });
-    return `/files${rooturl}${fn}-${num}.docx`;
-  }
-
-  /**
-   * 将选择的文件导出到zip压缩包中,提供下载
-   * @param {*} fileList 需要导入到zip中的列表,格式有2中: [{url:""}]或[String]
-   * @param {*} fn 文件名,默认为 "导出结果"
-   */
-  async toZip(fileList, fn = '导出结果') {
-    if (!_.isArray(fileList)) {
-      throw new BusinessError(
-        ErrorCode.DATA_INVALID,
-        '需要压缩的文件数据格式错误'
-      );
-    }
-    fn = `${fn}.zip`;
-    // zip文件夹创建
-    const { app } = this;
-    const rootPath = `${app.config.cdn.repos_root_path}`;
-    const zipPath = `${app.config.cdn.repos_root_url_zip}`;
-    let path = `${rootPath}${zipPath}`;
-    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
-    if (!fs.existsSync(path)) {
-      fs.mkdirSync(path);
-    }
-    // 文件请求后将数据整理到这里
-    const resetFileList = [];
-    for (const file of fileList) {
-      let uri = '';
-      let filename = '';
-      let prefixs;
-      if (_.isString(file)) {
-        uri = file;
-        const arr = file.split('/');
-        const last = _.last(arr);
-        if (last) filename = last;
-      } else if (_.isObject(file)) {
-        const { uri: furi, url: furl, name, prefix } = file;
-        if (furi) uri = furi;
-        else if (furl) uri = furl;
-        if (name) filename = name;
-        else {
-          const arr = uri.split('/');
-          const last = _.last(arr);
-          if (last) filename = last;
-        }
-        if (prefix) prefixs = prefix;
-      }
-      const obj = {};
-      if (uri) obj.uri = uri;
-      if (filename) obj.filename = filename;
-      if (prefixs) obj.prefix = prefixs;
-      resetFileList.push(obj);
-    }
-    // 导出
-    const output = fs.createWriteStream(`${path}${fn}`);
-    const archive = archiver('zip', {
-      zlib: { level: 9 },
-    });
-    archive.pipe(output);
-    // 请求文件,追加进压缩包
-    for (const file of resetFileList) {
-      const { uri, filename, prefix } = file;
-      const res = await this.ctx.curl(`http://127.0.0.1${uri}`);
-      if (res && res.data) {
-        const options = {};
-        if (filename) options.name = filename;
-        if (prefix) options.prefix = prefix;
-        if (filename) {
-          archive.append(res.data, options);
-        }
-      }
-    }
-    await archive.finalize();
-    return `/files${zipPath}${fn}`;
-  }
-}
-
-module.exports = UtilService;
+'use strict';
+
+
+const assert = require('assert');
+const _ = require('lodash');
+const fs = require('fs');
+const Excel = require('exceljs');
+const { ObjectId } = require('mongoose').Types;
+const { CrudService } = require('naf-framework-mongoose/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const moment = require('moment');
+const nodemailer = require('nodemailer');
+const { template } = require('lodash');
+const docx = require('docx');
+const archiver = require('archiver');
+class UtilService extends CrudService {
+  constructor(ctx) {
+    super(ctx);
+    this.mq = this.ctx.mq;
+  }
+  async updatedate() {
+    let date = new Date();
+    date = moment(date).format('YYYY-MM-DD HH:mm:ss');
+    return date;
+  }
+
+  async sendMail(email, subject, text, html) {
+    const setting = await this.ctx.model.Setting.findOne();
+    let user_email = this.ctx.app.config.user_email;
+    let auth_code = this.ctx.app.config.auth_code;
+    if (setting) {
+      user_email = setting.user_email;
+      auth_code = setting.auth_code;
+    }
+    const transporter = nodemailer.createTransport({
+      host: 'smtp.exmail.qq.com',
+      secureConnection: true,
+      port: 465,
+      auth: {
+        user: user_email, // 账号
+        pass: auth_code, // 授权码
+      },
+    });
+    const mailOptions = {
+      from: user_email, // 发送者,与上面的user一致
+      to: email, // 接收者,可以同时发送多个,以逗号隔开
+      subject, // 标题
+      text, // 文本
+      html,
+    };
+
+    try {
+      await transporter.sendMail(mailOptions);
+      return true;
+    } catch (err) {
+      return false;
+    }
+  }
+
+  async findone({ modelname }, data) {
+    // 查询单条
+    const _model = _.capitalize(modelname);
+    const res = await this.ctx.model[_model].findOne({ ...data });
+    return res;
+  }
+
+  async findbyids({ modelname }, { data }) {
+    // 共通批量查询方法
+    const _model = _.capitalize(modelname);
+    const res = [];
+    for (const elm of data) {
+      const result = await this.ctx.model[_model].findById(elm);
+      res.push(result);
+    }
+    return res;
+  }
+  async findmodel({ modelname }) {
+    const _model = _.capitalize(modelname);
+    const data = this.ctx.model[_model].prototype.schema.obj;
+    const keys = Object.keys(data);
+    const res = {};
+    for (const k of keys) {
+      const obj = data[k];
+      if (_.get(obj, 'zh')) res[k] = obj;
+    }
+    return res;
+  }
+  async utilMethod(query, body) {
+    // const ch = await this.ctx.amqp.conn.createChannel();
+    // const queue = 'hello';
+    // ch.consume(
+    //   queue,
+    //   msg => {
+    //     console.log(' [x] Received %s', msg.content.toString());
+    //   },
+    //   { noAck: true }
+    // );
+    // // ch.close();
+  }
+  async getQueryOptions({ skip, limit, sort, desc } = {}) {
+    if (sort && _.isString(sort)) {
+      sort = { [sort]: desc ? -1 : 1 };
+    } else if (sort && _.isArray(sort)) {
+      sort = sort
+        .map(f => ({ [f]: desc ? -1 : 1 }))
+        .reduce((p, c) => ({ ...p, ...c }), {});
+    }
+    return { skip, limit, sort };
+  }
+
+  /**
+   * 更新进度,状态 不存在/完成以外(不是2的值)的数值, 不添加参数,只更新进度
+   * @param {String} missionid 任务id
+   * @param {String} progress 进度(人为划分)
+   * @param {String} status 状态
+   * @param {Object} params 参数
+   */
+  async updateProcess(missionid, progress, status, params) {
+    try {
+      if (!missionid) return;
+      const { baseUrl } = _.get(this.ctx.app.config, 'mission');
+      let url = `${baseUrl}`;
+      let data;
+      if (!baseUrl) return;
+      if (!status || status !== '2') {
+        url = `${url}/api/mission/progress`;
+        data = { id: missionid, progress };
+      } else if (status === '3') {
+        url = `${url}/api/mission/update/${missionid}`;
+        data = { status };
+      } else {
+        url = `${url}/api/mission/update/${missionid}`;
+        data = { progress: '100', params, status };
+      }
+      await this.ctx.curl(url, {
+        method: 'post',
+        headers: {
+          'content-type': 'application/json',
+        },
+        data,
+        dataType: 'json',
+      });
+    } catch (error) {
+      console.error(`任务更新进度报错,missionid:${missionid}\n`);
+    }
+  }
+
+  async teacherImport() {
+    // const filepath = './teacherlist.xlsx';
+    // const workbook = new Excel.Workbook();
+    // await workbook.xlsx.readFile(filepath);
+    // const worksheet = workbook.getWorksheet(1);
+    // if (!worksheet) return;
+    // let arr = [];
+    // worksheet.eachRow((row, ri) => {
+    //   if (ri !== 1) {
+    //     const obj = {};
+    //     obj.name = row.getCell(3).value || undefined;
+    //     obj.department = row.getCell(4).value || undefined;
+    //     if (row.getCell(5).value) obj.job = row.getCell(5).value;
+    //     obj.phone = row.getCell(6).value || undefined;
+    //     obj.status = '4';
+    //     arr.push(obj);
+    //   }
+    // });
+    // // 检查谁生成过了, user表和teacher表
+    // let ur = await this.ctx.model.User.find({ mobile: { $in: arr.map(i => i.phone) }, type: '3' });
+    // let tr = await this.ctx.model.Teacher.find({ phone: { $in: arr.map(i => i.phone) } });
+    // // 将有的老师过滤出去
+    // if (ur) {
+    //   ur = JSON.parse(JSON.stringify(ur));
+    //   arr = arr.filter(f => !ur.find(uf => `${uf.mobile}` === `${f.phone}`));
+    // }
+    // if (tr) {
+    //   tr = JSON.parse(JSON.stringify(tr));
+    //   arr = arr.filter(f => !(tr.find(tf => `${tf.phone}` === `${f.phone}`)));
+    // }
+    // for (const tea of arr) {
+    //   const ctr = await this.ctx.model.Teacher.create(tea);
+    //   if (ctr) {
+    //     const obj = { name: tea.name, mobile: tea.phone, type: '3', uid: ctr._id };
+    //     const cur = await this.ctx.model.User.create(obj);
+    //   }
+    // }
+    // const user = await this.ctx.model.User.find({ passwd: { $exists: false } });
+    // for (const u of user) {
+    //   u.passwd = { secret: '12345678' };
+    //   u.save();
+    // }
+  }
+  /**
+   * 导出excel
+   * @param {Array} dataList 数据集合
+   * @param {Array} meta 表头(可以没有)
+   * @param {String} fn 文件名
+   * @param {Array} opera 单元格操作
+   */
+  async toExcel(dataList, meta, fn = '导出结果', opera) {
+    // 导出excel
+    const { app } = this;
+    const nowDate = new Date().getTime();
+    const filename = `${fn}-${nowDate}.xlsx`;
+    // 取出预设存储地址
+    const rootPath = `${app.config.cdn.repos_root_path}`;
+    const rooturl = `${app.config.cdn.repos_root_url_excel}`;
+    let path = `${rootPath}${rooturl}`;
+    if (!path) {
+      throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
+    }
+    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
+    if (!fs.existsSync(path)) {
+      // 如果不存在文件夹,就创建
+      fs.mkdirSync(path);
+    }
+    // 生成文件
+    const filepath = `${path}${filename}`;
+    fs.createWriteStream(filepath);
+    const workbook = new Excel.Workbook();
+    const sheet = workbook.addWorksheet('sheet');
+    if (meta)sheet.columns = meta;
+    sheet.addRows(dataList);
+    if (_.isArray(opera)) {
+      for (const o of opera) {
+        const { startRow, startCol, endRow, endCol } = o;
+        sheet.mergeCells(startRow, startCol, endRow, endCol);
+      }
+    }
+    // 垂直居中
+    const length = dataList.length;
+    const alignment = { vertical: 'middle', horizontal: 'center' };
+    for (let i = 1; i <= length; i++) {
+      sheet.getRow(i).alignment = alignment;
+    }
+    await workbook.xlsx.writeFile(filepath);
+    return `/files/excel/${filename}`;
+  }
+
+  /**
+   * 创建/重复写入excel
+   * @param {Array} dataList 数据
+   * @param {String} fn 文件名, 第一次进入时,只是单纯的名,第二次开始,有后缀与时间戳
+   * @param {String} downloadPath 下载文件路径
+   * @param {String} type 方向,没有默认横向
+   */
+  async toAsyncExcel(dataList, fn = '导出结果', downloadPath, type) {
+    const { app } = this;
+    const workbook = new Excel.Workbook();
+    let sheet;
+    // 取出预设存储地址
+    const rootPath = `${app.config.cdn.repos_root_path}`;
+    const rooturl = `${app.config.cdn.repos_root_url_excel}`;
+    let path = `${rootPath}${rooturl}`;
+    let filepath = '';
+
+    if (!path) {
+      throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
+    }
+    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\excel\\';
+    if (!fs.existsSync(path)) {
+      // 如果不存在文件夹,就创建
+      fs.mkdirSync(path);
+    }
+
+    if (!downloadPath) {
+      // 第一次进入,文件还未生成
+      const nowDate = new Date().getTime();
+      fn = `${fn}-${nowDate}.xlsx`;
+      sheet = workbook.addWorksheet('sheet');
+    } else {
+      let domain = 'http://127.0.0.1';
+      if (process.env.NODE_ENV === 'development')domain = `${domain}:8000`;
+      const file = await this.ctx.curl(`${domain}${downloadPath}`);
+      if (!(file && file.data)) {
+        throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找导出的excel');
+      }
+      // 读取文件
+      await workbook.xlsx.load(file.data);
+      sheet = workbook.getWorksheet('sheet');
+    }
+    if (!type || type === 'horizontal') sheet.addRows(dataList);
+    else if (type === 'vertical') {
+      for (let i = 1; i <= dataList.length; i++) {
+        const element = dataList[i - 1];
+        const rows = sheet.getRow(i);
+        if (rows.values.length <= 0) rows.values = element;
+        else rows.values = rows.values.concat(element);
+        rows.commit();
+      }
+    }
+    filepath = `${path}${fn}`;
+    await workbook.xlsx.writeFile(filepath);
+    return { downloadPath: `/files/excel/${fn}`, fn };
+  }
+
+  /**
+   * 导出docx
+   * @param {Array} data 数据[{title,content([]),author}]
+   * @param {String} fn 文件名
+   */
+  async toDocx(data, fn = '培训心得') {
+    const {
+      Document,
+      Packer,
+      Paragraph,
+      TextRun,
+      HeadingLevel,
+      AlignmentType,
+    } = docx;
+    const doc = new Document();
+    const children = [];
+    // 整理数据
+    for (let i = 0; i < data.length; i++) {
+      const obj = data[i];
+      const { title, content, author } = obj;
+      const c = [];
+      if (title) {
+        const tit = new Paragraph({
+          children: [ new TextRun({ text: title, bold: true }) ],
+          heading: HeadingLevel.TITLE,
+          alignment: AlignmentType.CENTER,
+        });
+        c.push(tit);
+      }
+      if (author) {
+        const auth = new Paragraph({
+          children: [ new TextRun({ color: '#000000', text: author }) ],
+          heading: HeadingLevel.HEADING_2,
+          alignment: AlignmentType.RIGHT,
+        });
+        c.push(auth);
+      }
+      if (content && _.isArray(content) && content.length > 0) {
+        for (const cont of content) {
+          const p = new Paragraph({
+            children: [ new TextRun({ text: cont, bold: true }) ],
+          });
+          c.push(p);
+        }
+      }
+      if (i !== data.length - 1) {
+        // 换页
+        const last = new Paragraph({
+          pageBreakBefore: true,
+        });
+        c.push(last);
+      }
+      if (c.length > 0) children.push(...c);
+    }
+    doc.addSection({
+      properties: {},
+      children,
+    });
+
+    const { app } = this;
+    const rootPath = `${app.config.cdn.repos_root_path}`;
+    const rooturl = `${app.config.cdn.repos_root_url_experience}`;
+    let path = `${rootPath}${rooturl}`;
+    // 如果不存在文件夹,就创建
+    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
+    if (!fs.existsSync(path)) {
+      fs.mkdirSync(path);
+    }
+    const num = new Date().getTime();
+    const buffer = await Packer.toBuffer(doc);
+    fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
+    // Packer.toBuffer(doc).then(buffer => {
+    //   fs.writeFileSync(`${path}${fn}-${num}.docx`, buffer);
+    // });
+    return `/files${rooturl}${fn}-${num}.docx`;
+  }
+
+  /**
+   * 将选择的文件导出到zip压缩包中,提供下载
+   * @param {*} fileList 需要导入到zip中的列表,格式有2中: [{url:""}]或[String]
+   * @param {*} fn 文件名,默认为 "导出结果"
+   */
+  async toZip(fileList, fn = '导出结果') {
+    if (!_.isArray(fileList)) {
+      throw new BusinessError(
+        ErrorCode.DATA_INVALID,
+        '需要压缩的文件数据格式错误'
+      );
+    }
+    fn = `${fn}.zip`;
+    // zip文件夹创建
+    const { app } = this;
+    const rootPath = `${app.config.cdn.repos_root_path}`;
+    const zipPath = `${app.config.cdn.repos_root_url_zip}`;
+    let path = `${rootPath}${zipPath}`;
+    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
+    if (!fs.existsSync(path)) {
+      fs.mkdirSync(path);
+    }
+    // 文件请求后将数据整理到这里
+    const resetFileList = [];
+    for (const file of fileList) {
+      let uri = '';
+      let filename = '';
+      let prefixs;
+      if (_.isString(file)) {
+        uri = file;
+        const arr = file.split('/');
+        const last = _.last(arr);
+        if (last) filename = last;
+      } else if (_.isObject(file)) {
+        const { uri: furi, url: furl, name, prefix } = file;
+        if (furi) uri = furi;
+        else if (furl) uri = furl;
+        if (name) filename = name;
+        else {
+          const arr = uri.split('/');
+          const last = _.last(arr);
+          if (last) filename = last;
+        }
+        if (prefix) prefixs = prefix;
+      }
+      const obj = {};
+      if (uri) obj.uri = uri;
+      if (filename) obj.filename = filename;
+      if (prefixs) obj.prefix = prefixs;
+      resetFileList.push(obj);
+    }
+    // 导出
+    const output = fs.createWriteStream(`${path}${fn}`);
+    const archive = archiver('zip', {
+      zlib: { level: 9 },
+    });
+    archive.pipe(output);
+    // 请求文件,追加进压缩包
+    for (const file of resetFileList) {
+      const { uri, filename, prefix } = file;
+      const res = await this.ctx.curl(`http://127.0.0.1${uri}`);
+      if (res && res.data) {
+        const options = {};
+        if (filename) options.name = filename;
+        if (prefix) options.prefix = prefix;
+        if (filename) {
+          archive.append(res.data, options);
+        }
+      }
+    }
+    await archive.finalize();
+    return `/files${zipPath}${fn}`;
+  }
+}
+
+module.exports = UtilService;