浏览代码

导入专利预警,解析zip包

ruifeng_liu 3 年之前
父节点
当前提交
7a97b25a7c

+ 5 - 0
app/controller/patent/.patentwarning.js

@@ -56,4 +56,9 @@ module.exports = {
       count: true,
     },
   },
+  import: {
+    requestBody: [
+      "uri"
+    ],
+  },
 };

+ 1 - 0
app/router/patent/patentwarning.js

@@ -9,4 +9,5 @@ module.exports = app => {
   const target = 'patentwarning';
   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].import);
 };

+ 225 - 2
app/service/patent/patentwarning.js

@@ -4,13 +4,236 @@ const { BusinessError, ErrorCode } = require('naf-core').Error;
 const { ObjectId } = require('mongoose').Types;
 const _ = require('lodash');
 const assert = require('assert');
-
-
+const compressing = require('compressing');
+const { sep } = require('path');
+const path = require('path');
+const fs = require('fs');
+const encoding = require('encoding'); // 编码转换
+const fxp = require('fast-xml-parser'); // xml转换
+const mime = require('mime-types'); // 任意类型转换
 // 专利运营专利申请预警表
 class PatentwarningService extends CrudService {
   constructor(ctx) {
     super(ctx, 'patentwarning');
     this.model = this.ctx.model.Patent.Patentwarning;
+    this.import_root_path = _.get(this.ctx.app.config.import, 'root_path');
+    this.patentInfo = this.ctx.model.Patent.Patentinfo;
+    this.patentApply = this.ctx.model.Patent.Patentapply;
+    this.personalModel = this.ctx.model.Personal;
+  }
+  async import({ uri }) {
+    uri = uri.replace('/files', this.import_root_path);
+    const fileFullName = path.basename(uri);
+    const arr = fileFullName.split('.');
+    const ext = _.last(arr);
+    const fileName = _.head(arr);
+    const uncompressFilePath = `${this.import_root_path}${sep}temp`;
+    if (ext !== 'zip') {
+      throw new BusinessError(ErrorCode.FILE_FAULT, '只接收压缩格式为zip的压缩包');
+    }
+    // 解压
+    await compressing.zip.uncompress(uri, `${uncompressFilePath}`);
+    // 找到
+    const filePaths = await this.findFileNameLoop(uncompressFilePath, fileName);
+    const xmlPath = filePaths.find(f => f.includes('.xml'));
+    const create_number = await this.dealXml(xmlPath);
+    if (!create_number) throw new BusinessError(ErrorCode.FILE_FAULT, '未找到申请号');
+    let result = await this.getPatentFromInfo(create_number);
+    // 再找
+    if (!result) result = await this.getPatentFromApply(create_number);
+    // 找不到就算了,弹回去了
+    if (!result) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未通过申请号找到指定专利');
+    const warningData = await this.getWarningData(result);
+    const tifPaths = filePaths.filter(f => f.includes('.tif'));
+    if (tifPaths && tifPaths.length > 0) this.toUploadTif(warningData, tifPaths);
+    await this.dirDelete(`${uncompressFilePath}${fileName}`);
+    return 'ok';
+  }
+
+  /**
+   * 上传图片,保存
+   * @param {Array} data 警告数据,未保存
+   * @param {Array} files tif图片文件路径数组
+   */
+  async toUploadTif(data, files) {
+    const filePaths = [];
+    const savePath = ObjectId();
+    for (const file of files) {
+      let arr = file.split(sep);
+      const fullFileName = _.last(arr);
+      arr = fullFileName.split('.');
+      // 文件后缀
+      const ext = _.last(arr);
+      // 图片文件
+      const buffer = await fs.readFileSync(file);
+      // 新文件名
+      const randomStr = ObjectId();
+      // 新文件名带后缀
+      const newFileName = `${randomStr}.${ext}`;
+      // 上传后部分路径
+      const shortPath = `platform${sep}patent${sep}${savePath}${sep}`;
+      // 上传全路径
+      const path = `${this.import_root_path}${sep}${shortPath}`;
+      // 检查上传全路径是否存在,不存在就创建文件夹
+      await this.checkPathAndCreate(path);
+      // 另存文件
+      await fs.writeFileSync(`${path}${newFileName}`, buffer);
+      // 替换为files开头
+      let dataPath = path.replace(this.import_root_path, '/files');
+      while (dataPath.indexOf('\\') >= 0) {
+        dataPath = dataPath.replace('\\', '/');
+      }
+      // 补全请求用路径的所有路径
+      dataPath += newFileName;
+      filePaths.push(dataPath);
+    }
+    for (const i of data) {
+      i.file_url = filePaths;
+    }
+    const res = await this.model.insertMany(data);
+    return res;
+
+  }
+
+  /**
+   * 检查路径,没有创建
+   * @param {Array} filePath 图片路径数组
+   */
+  async checkPathAndCreate(filePath) {
+    const arr = filePath.split(sep);
+    // w:第一个是盘符,直接和第二个合并
+    // l:前几个应该都是存在的.也可以合并
+    let head = _.head(arr);
+    if (arr[1]) head = `${head}${sep}${arr[1]}`;
+    arr.shift();
+    arr[0] = head;
+    let curPath;
+    for (const p of arr) {
+      curPath = `${curPath ? curPath + sep : ''}${p}`;
+      if (!fs.existsSync(curPath)) {
+        await fs.mkdirSync(curPath);
+      }
+    }
+  }
+
+  /**
+   * 组织专利预警数据
+   * @param {Object} patent 专利
+   */
+  async getWarningData(patent) {
+    const object = {};
+    if (_.get(patent, 'create_number'))object.create_number = _.get(patent, 'create_number');
+    if (_.get(patent, 'id'))object.patent_id = _.get(patent, 'id');
+    if (_.get(patent, 'name'))object.patent_name = _.get(patent, 'name');
+    // 找人的信息
+    let userIds = _.get(patent, 'user_id', _.get(patent, 'inventer'));
+    if (!userIds) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到专利的发明人');
+    userIds = userIds.map(i => ObjectId(i));
+    const users = await this.personalModel.find({ _id: userIds });
+    const arr = [];
+    for (const user of users) {
+      if (!_.get(user, '_id') || !_.get(user, 'name')) continue;
+      const { _id, name } = user;
+      const obj = _.cloneDeep(object);
+      obj.to_id = _id;
+      obj.to_name = name;
+      arr.push(obj);
+    }
+    return arr;
+
+  }
+
+  /**
+   * 在patentApply表中找指定的专利
+   * @param {String} create_number 专利的申请号
+   */
+  async getPatentFromApply(create_number) {
+    const res = await this.patentApply.findOne({ create_number });
+    return res;
+  }
+
+  /**
+   * 在patentInfo表中找指定的专利
+   * @param {String} create_number 专利的申请号
+   */
+  async getPatentFromInfo(create_number) {
+    const res = await this.patentInfo.findOne({ create_number });
+    return res;
+  }
+
+  /**
+   * 根据路径找到xml,再找到该xml的申请号
+   * @param {String} filePath xml的文件路径
+   */
+  async dealXml(filePath) {
+    const buffer = await fs.readFileSync(filePath);
+    let str = encoding.convert(buffer, 'UTF8', 'GBK');
+    if (!str) throw new BusinessError(ErrorCode.FILE_FAULT, '读取xml文件失败');
+    str = str.toString();
+    const json = fxp.parse(str);
+    const create_number = _.get(json, 'data-bus.TONGZHISXJ.SHUXINGXX.SHENQINGH');
+    return create_number;
+  }
+
+  /**
+   * 找到深层的tif和xml
+   * 因为命名方式都是以文件名,之后tif的文件夹也在深层fileName.套娃且文件名都是fileName
+   * @param {String} uri temp路径
+   * @param {String} fileName 文件名
+   */
+  async findFileNameLoop(uri, fileName) {
+    const filePath = `${uri}${sep}${fileName}`;
+    if (!fs.existsSync(filePath)) {
+      return [];
+    }
+    const files = fs.readdirSync(filePath);
+    let result = [];
+    for (const file of files) {
+      const curPath = `${filePath}${sep}${file}`;
+      if (fs.statSync(curPath).isDirectory()) {
+        const res = await this.findFileNameLoop(curPath, file);
+        result.push(...res);
+      } else {
+        result.push(curPath);
+      }
+    }
+    result = result.filter(f => f.includes('.tif') || f.includes('.xml'));
+    return result;
+  }
+
+  /**
+   * 删除路径下的所有内容
+   * @param {String} filePath 路径
+   */
+  async dirDelete(filePath) {
+    if (fs.existsSync(filePath)) {
+      const files = fs.readdirSync(filePath);
+      for (const file of files) {
+        const curPath = `${filePath}${sep}${file}`;
+        if (fs.statSync(curPath).isDirectory()) { // recurse
+          this.dirDelete(curPath);
+        } else { // delete file
+          fs.unlinkSync(curPath);
+        }
+      }
+      fs.rmdirSync(filePath);
+    }
+  }
+
+  /**
+   * 转换图片为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;
+    }
   }
 }
 

+ 3 - 0
config/config.default.js

@@ -76,6 +76,9 @@ module.exports = appInfo => {
     root_path: 'E:\\exportFile',
     domain: 'http://broadcast.waityou24.cn',
   };
+  config.import = {
+    root_path: 'E:\\exportFile',
+  };
   config.project = {
     mission: 'http://127.0.0.1:4001',
     hnhmain: 'http://127.0.0.1:9201',

+ 3 - 0
config/config.prod.js

@@ -32,5 +32,8 @@ module.exports = () => {
     root_path: 'D:\\free\\workspace\\server\\service-file\\upload',
     domain: 'http://127.0.0.1',
   };
+  config.import = {
+    root_path: 'D:\\free\\workspace\\server\\service-file\\upload',
+  };
   return config;
 };

+ 4 - 0
package.json

@@ -7,12 +7,16 @@
     "framework": "naf-framework-mongoose"
   },
   "dependencies": {
+    "compressing": "^1.5.1",
     "egg": "^2.15.1",
     "egg-naf-amqp": "0.0.13",
     "egg-redis": "^2.4.0",
     "egg-scripts": "^2.11.0",
+    "encoding": "^0.1.13",
     "exceljs": "^4.2.0",
+    "fast-xml-parser": "^3.20.3",
     "lodash": "^4.17.15",
+    "mime-types": "^2.1.33",
     "moment": "^2.24.0",
     "naf-framework-mongoose": "^0.6.11",
     "string-random": "^0.1.3",