'use strict'; const { CrudService } = require('naf-framework-mongoose-free/lib/service'); const { BusinessError, ErrorCode } = require('naf-core').Error; const { ObjectId } = require('mongoose').Types; const _ = require('lodash'); 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转换 // 专利运营专利申请预警表 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.User.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 uncompressFilePath = `${this.import_root_path}${sep}temp`; if (ext !== 'zip') { throw new BusinessError(ErrorCode.FILE_FAULT, '只接收压缩格式为zip的压缩包'); } // 解压 try { await compressing.zip.uncompress(uri, `${uncompressFilePath}`); } catch (error) { throw new BusinessError(ErrorCode.FILE_FAULT, '解压失败'); } // 修改,多个专利:需要扫描下解压后的文件夹,然后循环处理下面所有的内容 const tempDirRootPath = `${uncompressFilePath}${sep}`; const tempDir = await fs.readdirSync(tempDirRootPath); const errorList = []; for (const tempDirPath of tempDir) { const thisDirPath = `${tempDirRootPath}${sep}${tempDirPath}`; const patentNumber = tempDirPath; try { // 找到 let filePaths = []; try { filePaths = await this.findFileNameLoop(thisDirPath); } catch (error) { errorList.push({ key: patentNumber, word: '解析文件具体位置失败' }); continue; } if (filePaths.length <= 0) { errorList.push({ key: patentNumber, word: '未找到文件' }); continue; } // 2021-10-16 修改: 取出第一层的xml文件,即:路径最短的;除文件名外,字符串长度最小的 let xmlPaths = filePaths.filter(f => f.includes('.xml')); if (xmlPaths.length > 0) { // 去掉文件名 xmlPaths = xmlPaths.map(i => { const arr = i.split(sep); arr.pop(); const obj = { dir: arr.join(sep), fullPath: i }; return obj; }); // 按路径字符串长度排序 xmlPaths = xmlPaths.sort((a, b) => { const anum = a.dir.length; const bnum = b.dir.length; return anum - bnum; }); } const xmlPath = _.get(_.head(xmlPaths), 'fullPath'); if (!xmlPath) { errorList.push({ key: patentNumber, word: '未找到xml文件' }); continue; // throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到xml文件'); } let xmlData; try { xmlData = await this.dealXml(xmlPath); } catch (error) { errorList.push({ key: patentNumber, word: `解析xml数据中的 申请号 和 专利名称 失败 于:${xmlPath}` }); continue; // throw new BusinessError(ErrorCode.SERVICE_FAULT, '解析申请号失败'); } if (!xmlData) { errorList.push({ key: patentNumber, word: '未找到xml的数据' }); continue; // throw new BusinessError(ErrorCode.FILE_FAULT, '未找到申请号'); } let result; try { result = await this.getPatent(xmlData); } catch (error) { errorList.push({ key: patentNumber, word: '查询专利信息失败' }); continue; // throw new BusinessError(ErrorCode.SERVICE_FAULT, '查询专利信息失败'); } // 找不到就算了,弹回去了 if (!result || !_.get(result, 'patent')) { errorList.push({ key: patentNumber, word: '未通过 申请号 或 专利名称 找到指定专利' }); continue; // throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未通过申请号找到指定专利'); } const patent = _.get(result, 'patent'); let warningData; try { warningData = await this.getWarningData(patent); } catch (error) { errorList.push({ key: patentNumber, word: '组织专利信息警告数据失败' }); continue; // throw new BusinessError(ErrorCode.SERVICE_FAULT, '组织专利信息警告数据失败'); } const tifPaths = filePaths.filter(f => f.includes('.tif')); if (tifPaths && tifPaths.length > 0) { try { await this.toUploadTif(warningData, tifPaths); } catch (error) { errorList.push({ key: patentNumber, word: '存储数据失败' }); continue; } } // 查看是否需要更改申请号 const needChange = _.get(result, 'needChange'); if (needChange) { try { await this.patentApply.updateOne({ _id: patent._id }, patent); } catch (error) { // errorList.push({ key: patentNumber, word: 'xml申请号同步至专利申请号失败' }); continue; } } } finally { try { await this.dirDelete(`${thisDirPath}`); } catch (error) { this.ctx.logger.error('清除缓存失败'); } } } if (errorList.length > 0) return errorList; 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 = {}; const arr = []; 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'); // 找人的信息 const users = _.get(patent, 'user_id'); if (users && _.isArray(users)) { for (const user of users) { if (!_.get(user, 'user_id') || !_.get(user, 'name')) continue; const { user_id, name } = user; const obj = _.cloneDeep(object); obj.to_id = user_id; obj.to_name = name; arr.push(obj); } // 2021-11-18 添加,user,mech,admin,agentmech是否存在,如果存在,也添加预警信息 const list = [ 'user', 'mech', 'admin', 'agentmech' ]; const suffix = [ 'id', 'name' ]; for (const prefix of list) { const marr = suffix.map(i => `${prefix}_${i}`); const r = marr.every(i => _.get(patent, i)); if (r) { const obj = _.cloneDeep(object); for (const key of suffix) { obj[`to_${key}`] = _.get(patent, `${prefix}_${key}`); } arr.push(obj); } } } else if (users) { const obj = _.cloneDeep(object); const name = _.get(patent, 'user_name'); obj.to_id = users; obj.to_name = name; arr.push(obj); } return arr; } /** * 在patentApply/patentInfo表中找指定的专利 * @param {Object} object xml的数据 */ async getPatent(object) { const { name, create_number } = object; let res; let needChange = false; res = await this.patentApply.findOne({ create_number }); // 如果在申请中,使用名称找到,则之后需要将该数据的专利号改为xml中的专利号 if (!res) { res = await this.patentApply.findOne({ name }); needChange = true; } if (!res) { res = await this.patentInfo.findOne({ create_number }); } if (!res) { res = await this.patentInfo.findOne({ name }); } const returnObject = {}; if (res) { if (needChange) { res.create_number = create_number; returnObject.needChange = needChange; } returnObject.patent = res; return returnObject; } return null; } /** * 根据路径找到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'); const name = _.get(json, 'data-bus.TONGZHISXJ.SHUXINGXX.FAMINGMC'); if (!name) return null; return { name, create_number }; } /** * 找到深层的tif和xml * 因为命名方式都是以文件名,之后tif的文件夹也在深层fileName.套娃且文件名都是fileName * @param {String} uri temp路径 */ async findFileNameLoop(uri) { const filePath = `${_.trimEnd(uri, sep)}${sep}`; if (!fs.existsSync(filePath)) { return []; } const files = fs.readdirSync(filePath); let result = []; for (const file of files) { const curPath = `${filePath}${file}`; if (fs.statSync(curPath).isDirectory()) { const arr = await this.findFileNameLoop(curPath); result.push(...arr); } 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); } } } module.exports = PatentwarningService;