lrf402788946 4 年之前
父節點
當前提交
2b169365ad
共有 6 個文件被更改,包括 438 次插入31 次删除
  1. 85 0
      app/controller/dock/.patent.js
  2. 16 0
      app/controller/dock/patent.js
  3. 31 31
      app/model/patent.js
  4. 1 0
      app/router.js
  5. 18 0
      app/router/dock/patent.js
  6. 287 0
      app/service/dock/patent.js

+ 85 - 0
app/controller/dock/.patent.js

@@ -0,0 +1,85 @@
+module.exports = {
+  create: {
+    requestBody: [
+      "create_number",
+      "create_date",
+      "success_number",
+      "success_date",
+      "name",
+      "inventor",
+      "address",
+      "apply_personal",
+      "term",
+      "type",
+      "agent_personal",
+      "agent",
+      "abstract",
+      "img_url",
+      "origin",
+    ],
+  },
+  destroy: {
+    params: ["!id"],
+    service: "delete",
+  },
+  update: {
+    params: ["!id"],
+    requestBody: [
+      "create_number",
+      "create_date",
+      "success_number",
+      "success_date",
+      "name",
+      "inventor",
+      "address",
+      "apply_personal",
+      "term",
+      "type",
+      "agent_personal",
+      "agent",
+      "abstract",
+      "img_url",
+      "origin",
+    ],
+  },
+  show: {
+    parameters: {
+      params: ["!id"],
+    },
+    service: "fetch",
+  },
+  index: {
+    parameters: {
+      query: {
+        create_number: "create_number",
+        create_date: "create_date",
+        create_number: "create_number",
+        success_date: "success_date",
+        name: "%name%",
+        inventor: "inventor",
+        address: "address",
+        apply_personal: "%apply_personal%",
+        term: "term",
+        type: "type",
+        agent_personal: "agent_personal",
+        agent: "agent",
+        origin: "%origin%",
+      },
+    },
+    service: "query",
+    options: {
+      query: ["skip", "limit"],
+      sort: ["meta.createdAt"],
+      desc: true,
+      count: true,
+    },
+  },
+  toImport: {
+    requestBody: ["uri", "origin"],
+    service: "toImport",
+  },
+  toExport: {
+    requestBody: ["user"],
+    service: "toExport",
+  },
+};

+ 16 - 0
app/controller/dock/patent.js

@@ -0,0 +1,16 @@
+'use strict';
+
+const _ = require('lodash');
+const meta = require('./.patent.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose/lib/controller');
+
+// 专利表
+class PatentController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.dock.patent;
+  }
+}
+
+module.exports = CrudController(PatentController, meta);

+ 31 - 31
app/model/patent.js

@@ -1,31 +1,31 @@
-'use strict';
-const Schema = require('mongoose').Schema;
-const metaPlugin = require('naf-framework-mongoose/lib/model/meta-plugin');
-// 专利表
-const PatentSchema = {
-  create_number: { type: String, required: false }, // 申请号
-  create_date: { type: String, required: false }, // 申请日
-  success_number: { type: String, required: false }, // 公开(公告)号
-  success_date: { type: String, required: false }, //  公开(公告)日
-  name: { type: String, required: false }, // 标题
-  inventor: { type: String, required: false }, // 发明人
-  address: { type: String, required: false }, // 发明人地址
-  apply_personal: { type: String, required: false }, // 申请人
-  term: { type: String, required: false }, // 专利有效性
-  type: { type: String, required: false }, // 专利类型
-  agent_personal: { type: String, required: false }, // 代理人
-  agent: { type: String, required: false }, // 代理机构
-  abstract: { type: String, required: false }, // 摘要
-  img_url: { type: String, required: false }, // 图片
-  origin: { type: String }, // 数据来源(手写的)
-};
-
-const schema = new Schema(PatentSchema, { toJSON: { virtuals: true } });
-schema.index({ id: 1 });
-schema.index({ 'meta.createdAt': 1 });
-schema.plugin(metaPlugin);
-
-module.exports = app => {
-  const { mongoose } = app;
-  return mongoose.model('Patent', schema, 'patent');
-};
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose/lib/model/meta-plugin');
+// 专利表
+const PatentSchema = {
+  create_number: { type: String, required: false }, // 申请号
+  create_date: { type: String, required: false }, // 申请日
+  success_number: { type: String, required: false }, // 公开(公告)号
+  success_date: { type: String, required: false }, //  公开(公告)日
+  name: { type: String, required: false }, // 标题
+  inventor: { type: String, required: false }, // 发明人
+  address: { type: String, required: false }, // 发明人地址
+  apply_personal: { type: String, required: false }, // 申请人
+  term: { type: String, required: false }, // 专利有效性
+  type: { type: String, required: false }, // 专利类型
+  agent_personal: { type: String, required: false }, // 代理人
+  agent: { type: String, required: false }, // 代理机构
+  abstract: { type: String, required: false }, // 摘要
+  img_url: { type: String, required: false }, // 图片
+  origin: { type: String }, // 数据来源(手写的)
+};
+
+const schema = new Schema(PatentSchema, { toJSON: { virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Patent', schema, 'patent');
+};

+ 1 - 0
app/router.js

@@ -45,6 +45,7 @@ module.exports = app => {
   require('./router/dock/channel')(app); // 科技频道
   require('./router/dock/channel')(app); // 科技频道
   require('./router/dock/channel_video')(app); // 科技频道视频
   require('./router/dock/channel_video')(app); // 科技频道视频
   require('./router/dock/dock_chat')(app); // 展会聊天
   require('./router/dock/dock_chat')(app); // 展会聊天
+  require('./router/dock/patent')(app); // e专利
   // tecinfo
   // tecinfo
   require('./router/tecinfo/column')(app); // 栏目
   require('./router/tecinfo/column')(app); // 栏目
   require('./router/tecinfo/news')(app); // 信息
   require('./router/tecinfo/news')(app); // 信息

+ 18 - 0
app/router/dock/patent.js

@@ -0,0 +1,18 @@
+'use strict';
+
+
+module.exports = app => {
+  const { router, controller } = app;
+  const profix = '/api/live/';
+  const vision = 'v0';
+  const index = 'dock';
+  const target = 'patent';
+  // 专利表
+  router.resources(target, `${profix}${vision}/${index}/${target}`, controller[index][target]); // index、create、show、destroy
+  router.post(target, `${profix}${vision}/${index}/${target}/update/:id`, controller[index][target].update);
+  // 导入专利
+  router.post(target, `${profix}${vision}/${index}/${target}/import`, controller[index][target].toImport);
+  // 导出
+  router.post(target, `${profix}${vision}/${index}/${target}/export`, controller[index][target].toExport);
+
+};

+ 287 - 0
app/service/dock/patent.js

@@ -0,0 +1,287 @@
+'use strict';
+
+const assert = require('assert');
+const moment = require('moment');
+const Excel = require('exceljs');
+const Path = require('path');
+const _ = require('lodash');
+const { sep } = require('path');
+const fs = require('fs');
+const { CrudService } = require('naf-framework-mongoose/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+
+class PatentService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'patent');
+    this.model = this.ctx.model.Dock.Patent;
+    this.root_path = _.get(this.ctx.app.config.export, 'root_path');
+    if (process.env.NODE_ENV !== 'production') this.root_path = 'E:\\exportFile\\';
+    this.file_type = '';
+    if (!fs.existsSync(`${this.root_path}${this.file_type}`)) {
+      // 如果不存在文件夹,就创建
+      fs.mkdirSync(`${this.root_path}${this.file_type}`);
+    }
+    this.excel_path = `${sep}excel${sep}`;
+    this.domain = 'http://127.0.0.1';
+    this.export_limit = 50;
+  }
+
+  async toImport({ uri, origin }) {
+    assert(uri, '未获取到文件地址');
+    const file = await this.ctx.curl(`${this.domain}${uri}`);
+    if (!(file && file.data)) {
+      throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到指定文件');
+    }
+    const workbook = new Excel.Workbook();
+    await workbook.xlsx.load(file.data);
+    const sheet = workbook.getWorksheet(1);
+    const arr = [];
+    const allNotice = [];
+    const sheetImageInfo = sheet.getImages();
+    const imgids = _.compact(sheetImageInfo.map(i => {
+      const { imageId, range } = i;
+      const row = _.get(range, 'tl.nativeRow');
+      if (row) return { row, imageId };
+    }));
+    sheet.eachRow((row, rindex) => {
+      if (rindex !== 1) {
+        // 组织数据,图片的索引和行索引不一致,准确的说是:图片索引比行索引少1
+        // 原因:图片在工作簿中获取,按照1,2,3...顺序排序,但是行的第一行是表头(当前文件),所以当前行数需要减掉表头那一行
+        const imgid = imgids.find(f => f.row === rindex - 1);
+        let img_url;
+        if (imgid) { img_url = this.turnImageToBase64(workbook.getImage(imgid.imageId)); }
+        const create_number = row.getCell(2).value || undefined,
+          create_date =
+            moment(row.getCell(3).value).format('YYYY-MM-DD') || undefined,
+          success_number = row.getCell(4).value || undefined,
+          success_date =
+            moment(row.getCell(5).value).format('YYYY-MM-DD') || undefined,
+          inventor = row.getCell(6).value || undefined,
+          agent = row.getCell(7).value || undefined,
+          agent_personal = row.getCell(8).value || undefined,
+          abstract = row.getCell(9).value || undefined,
+          address = row.getCell(10).value || undefined,
+          name = row.getCell(11).value || undefined,
+          apply_personal = row.getCell(12).value || undefined,
+          term = row.getCell(13).value || undefined,
+          type = row.getCell(14).value || undefined,
+          number = row.getCell(1).value || undefined;
+        const obj = {
+          create_number,
+          create_date,
+          success_number,
+          success_date,
+          inventor,
+          agent,
+          agent_personal,
+          abstract,
+          address,
+          name,
+          apply_personal,
+          term,
+          type,
+          img_url,
+          number,
+          origin,
+        };
+        // 此处添加判断条件,不限制则不需要加,直接放过即可
+        const { result, notice } = this.tocheckData(obj);
+        if (result) {
+          arr.push(obj);
+        } else {
+          allNotice.push(notice);
+        }
+      }
+    });
+    if (allNotice.length > 0) return allNotice;
+    await this.model.insertMany(arr);
+
+  }
+  async toExport({ user }) {
+    const data = {
+      title: '专利导出',
+      params: {
+        project: 'market',
+        service: 'patent',
+        method: 'export',
+      },
+      user,
+    };
+    try {
+      await this.ctx.service.util.httpUtil.cpost('/api/mission', 'mission', data);
+    } catch (error) {
+      console.log(error);
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '任务创建失败');
+
+    }
+  }
+
+  async export({ missionid }) {
+    const nowDate = new Date().getTime();
+    const filename = `导出结果-${nowDate}.xlsx`;
+    const path = `${this.root_path}${this.file_type}${this.excel_path}`;
+    if (!path) {
+      throw new BusinessError(ErrorCode.BUSINESS, '服务端没有设置存储路径');
+    }
+    if (!fs.existsSync(path)) {
+      // 如果不存在文件夹,就创建
+      fs.mkdirSync(path);
+    }
+    const total = await this.model.count();
+    let skip = 0;
+    let downloadPath;
+    // 将数据分割,否则容易直接把js堆栈干满了,服务就炸了
+    for (let i = 0; i < total; i = i + this.export_limit) {
+      const list = await this.model.find().skip(skip).limit(this.export_limit);
+      skip = skip + this.export_limit;
+      downloadPath = await this.asyncExport(list, filename, path, downloadPath);
+      try {
+        const data = {
+          progress: _.ceil((skip / total) * 100),
+          status: '1',
+          id: missionid,
+        };
+        this.ctx.service.util.httpUtil.cpost('/api/mission/progress', 'mission', data);
+      } catch (error) {
+        this.logger.error(`任务id:${missionid},进度更新失败`);
+      }
+    }
+    try {
+      const data = {
+        progress: 100,
+        status: '2',
+        uri: downloadPath,
+      };
+      await this.ctx.service.util.httpUtil.cpost(`/api/mission/update/${missionid}`, 'mission', data);
+    } catch (error) {
+      this.logger.error(`任务id:${missionid},已完成更新失败`);
+    }
+    return downloadPath;
+
+  }
+
+  async asyncExport(list, filename, path, downloadPath) {
+    const workbook = new Excel.Workbook();
+    let sheet;
+    if (!downloadPath) {
+      sheet = workbook.addWorksheet('sheet');
+    } else {
+      const file = await this.ctx.curl(`${this.domain}${downloadPath}`);
+      if (!(file && file.data)) {
+        throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到指定文件');
+      }
+      await workbook.xlsx.load(file.data);
+      sheet = workbook.getWorksheet('sheet');
+    }
+    const rowNumber = sheet.rowCount || 1;
+    const meta = this.getHeader();
+    sheet.columns = meta;
+    sheet.addRows(list);
+    for (let i = 0; i < list.length; i++) {
+      const e = list[i];
+      const { img_url, _id } = e;
+      try {
+        if (img_url) {
+          const is_base64 = img_url.includes('base64,');
+          let imgid;
+          if (is_base64) {
+            imgid = workbook.addImage({
+              base64: img_url,
+              extension: 'jpeg',
+            });
+          } else if (img_url.includes('/files')) {
+            const prefix = `${this.root_path}`;
+            const new_url = img_url.replace('/files', prefix);
+            const suffix = Path.extname(img_url).substring(1);
+            imgid = workbook.addImage({
+              filename: new_url,
+              extension: suffix,
+            });
+          } else {
+            // 什么都不是,那就下一个
+            continue;
+          }
+
+          sheet.addImage(imgid, {
+            tl: { col: 13.2, row: i + rowNumber + 0.2 },
+            br: { col: 14, row: i + rowNumber + 1 },
+          });
+        }
+      } catch (error) {
+        this.ctx.logger.error(`导出,id为:${_id}的图片处理出现错误!`);
+      }
+
+    }
+    const filepath = `${path}${filename}`;
+    if (list.length <= 0) return;
+    await workbook.xlsx.writeFile(filepath);
+    return `/files/excel/${filename}`;
+  }
+
+  /**
+   * 检查数据是否没填 必填项
+   * @param {Object} object 每行数据,已转换成model的字段名
+   */
+  tocheckData(object) {
+    let result = true;
+    const { number } = object;
+    let notice;
+    const arr = [
+      { column: 'create_number', zh: '申请号' },
+      { column: 'create_date', zh: '申请日' },
+      { column: 'success_number', zh: '公开(公告)号' },
+      { column: 'success_date', zh: '公开(公告)日' },
+      { column: 'name', zh: '标题' },
+    ];
+    const word = [];
+    for (const o of arr) {
+      const { column, zh } = o;
+      if (!_.get(object, column)) {
+        result = false;
+        word.push(`${zh}`);
+      }
+    }
+    if (!result) {
+      notice = `序号${number}缺少:${word.join(';')}`;
+    }
+    return { result, notice };
+  }
+
+  /**
+   * 转换图片为base64
+   * @param {Object} object excel获取的图片object
+   * @property extension  后缀,文件类型
+   * @property buffer 图片内容,不含头部信息的,
+   */
+  turnImageToBase64(object = {}) {
+    const { extension, buffer } = object;
+    if (extension && buffer) {
+      const suffix = object.extension;
+      const ib = object.buffer.toString('base64');
+      const base64 = `data:image/${suffix};base64,${ib}`;
+      return base64;
+    }
+  }
+
+  getHeader() {
+    const arr = [
+      { header: '申请号', key: 'create_number', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '申请日', key: 'create_date', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '公开(公告)号', key: 'success_number', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '公开(公告)日', key: 'success_date', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '发明人', key: 'inventor', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '代理机构', key: 'agent', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '代理人', key: 'agent_personal', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '摘要', key: 'abstract', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '发明人地址', key: 'address', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '标题', key: 'name', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '申请人', key: 'apply_personal', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '专利有效性', key: 'term', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '专利类型', key: 'type', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+      { header: '首页附图', width: 30, style: { alignment: { wrapText: true, vertical: 'middle', horizontal: 'center' } } },
+    ];
+    return arr;
+  }
+}
+
+module.exports = PatentService;