patentwarning.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose/lib/service');
  3. const { BusinessError, ErrorCode } = require('naf-core').Error;
  4. const { ObjectId } = require('mongoose').Types;
  5. const _ = require('lodash');
  6. const compressing = require('compressing');
  7. const { sep } = require('path');
  8. const path = require('path');
  9. const fs = require('fs');
  10. const encoding = require('encoding'); // 编码转换
  11. const fxp = require('fast-xml-parser'); // xml转换
  12. // 专利运营专利申请预警表
  13. class PatentwarningService extends CrudService {
  14. constructor(ctx) {
  15. super(ctx, 'patentwarning');
  16. this.model = this.ctx.model.Patent.Patentwarning;
  17. this.import_root_path = _.get(this.ctx.app.config.import, 'root_path');
  18. this.patentInfo = this.ctx.model.Patent.Patentinfo;
  19. this.patentApply = this.ctx.model.Patent.Patentapply;
  20. this.personalModel = this.ctx.model.Personal;
  21. }
  22. async import({ uri }) {
  23. uri = uri.replace('/files', this.import_root_path);
  24. const fileFullName = path.basename(uri);
  25. const arr = fileFullName.split('.');
  26. const ext = _.last(arr);
  27. const fileName = _.head(arr);
  28. const uncompressFilePath = `${this.import_root_path}${sep}temp`;
  29. if (ext !== 'zip') {
  30. throw new BusinessError(ErrorCode.FILE_FAULT, '只接收压缩格式为zip的压缩包');
  31. }
  32. // 解压
  33. try {
  34. await compressing.zip.uncompress(uri, `${uncompressFilePath}`);
  35. } catch (error) {
  36. throw new BusinessError(ErrorCode.FILE_FAULT, '解压失败');
  37. }
  38. // 修改,多个专利:需要扫描下解压后的文件夹,然后循环处理下面所有的内容
  39. const tempDirRootPath = `${uncompressFilePath}${sep}`;
  40. const tempDir = await fs.readdirSync(tempDirRootPath);
  41. const errorList = [];
  42. for (const tempDirPath of tempDir) {
  43. const thisDirPath = `${tempDirRootPath}${sep}${tempDirPath}`;
  44. const patentNumber = tempDirPath;
  45. try {
  46. // 找到
  47. let filePaths = [];
  48. try {
  49. filePaths = await this.findFileNameLoop(thisDirPath);
  50. } catch (error) {
  51. errorList.push({ key: patentNumber, word: '解析文件具体位置失败' });
  52. continue;
  53. }
  54. if (filePaths.length <= 0) {
  55. errorList.push({ key: patentNumber, word: '未找到文件' });
  56. continue;
  57. }
  58. // 2021-10-16 修改: 取出第一层的xml文件,即:路径最短的;除文件名外,字符串长度最小的
  59. let xmlPaths = filePaths.filter(f => f.includes('.xml'));
  60. if (xmlPaths.length > 0) {
  61. // 去掉文件名
  62. xmlPaths = xmlPaths.map(i => {
  63. let arr = i.split(sep);
  64. arr = arr.pop();
  65. return arr.join(sep);
  66. });
  67. // 按路径字符串长度排序
  68. xmlPaths = xmlPaths.sort((a, b) => {
  69. const anum = a.length;
  70. const bnum = b.length;
  71. return anum - bnum;
  72. });
  73. }
  74. const xmlPath = _.head(xmlPaths);
  75. if (!xmlPath) {
  76. errorList.push({ key: patentNumber, word: '未找到xml文件' });
  77. continue;
  78. // throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到xml文件');
  79. }
  80. let xmlData;
  81. try {
  82. xmlData = await this.dealXml(xmlPath);
  83. } catch (error) {
  84. errorList.push({ key: patentNumber, word: '解析xml数据中的 申请号 和 专利名称 失败' });
  85. continue;
  86. // throw new BusinessError(ErrorCode.SERVICE_FAULT, '解析申请号失败');
  87. }
  88. if (!xmlData) {
  89. errorList.push({ key: patentNumber, word: '未找到xml的数据' });
  90. continue;
  91. // throw new BusinessError(ErrorCode.FILE_FAULT, '未找到申请号');
  92. }
  93. let result;
  94. try {
  95. result = await this.getPatent(xmlData);
  96. } catch (error) {
  97. errorList.push({ key: patentNumber, word: '查询专利信息失败' });
  98. continue;
  99. // throw new BusinessError(ErrorCode.SERVICE_FAULT, '查询专利信息失败');
  100. }
  101. // 找不到就算了,弹回去了
  102. if (!result || !_.get(result, 'patent')) {
  103. errorList.push({ key: patentNumber, word: '未通过 申请号 或 专利名称 找到指定专利' });
  104. continue;
  105. // throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未通过申请号找到指定专利');
  106. }
  107. const patent = _.get(result, 'patent');
  108. let warningData;
  109. try {
  110. warningData = await this.getWarningData(patent);
  111. } catch (error) {
  112. errorList.push({ key: patentNumber, word: '组织专利信息警告数据失败' });
  113. continue;
  114. // throw new BusinessError(ErrorCode.SERVICE_FAULT, '组织专利信息警告数据失败');
  115. }
  116. const tifPaths = filePaths.filter(f => f.includes('.tif'));
  117. if (tifPaths && tifPaths.length > 0) {
  118. try {
  119. await this.toUploadTif(warningData, tifPaths);
  120. } catch (error) {
  121. errorList.push({ key: patentNumber, word: '存储数据失败' });
  122. continue;
  123. }
  124. }
  125. // 查看是否需要更改申请号
  126. const needChange = _.get(result, 'needChange');
  127. if (needChange) {
  128. try {
  129. await this.patentApply.updateOne({ _id: patent._id }, patent);
  130. } catch (error) {
  131. // errorList.push({ key: patentNumber, word: 'xml申请号同步至专利申请号失败' });
  132. continue;
  133. }
  134. }
  135. } finally {
  136. try {
  137. await this.dirDelete(`${thisDirPath}`);
  138. } catch (error) {
  139. this.ctx.logger.error('清除缓存失败');
  140. }
  141. }
  142. }
  143. if (errorList.length > 0) return errorList;
  144. return 'ok';
  145. }
  146. /**
  147. * 上传图片,保存
  148. * @param {Array} data 警告数据,未保存
  149. * @param {Array} files tif图片文件路径数组
  150. */
  151. async toUploadTif(data, files) {
  152. const filePaths = [];
  153. const savePath = ObjectId();
  154. for (const file of files) {
  155. let arr = file.split(sep);
  156. const fullFileName = _.last(arr);
  157. arr = fullFileName.split('.');
  158. // 文件后缀
  159. const ext = _.last(arr);
  160. // 图片文件
  161. const buffer = await fs.readFileSync(file);
  162. // 新文件名
  163. const randomStr = ObjectId();
  164. // 新文件名带后缀
  165. const newFileName = `${randomStr}.${ext}`;
  166. // 上传后部分路径
  167. const shortPath = `platform${sep}patent${sep}${savePath}${sep}`;
  168. // 上传全路径
  169. const path = `${this.import_root_path}${sep}${shortPath}`;
  170. // 检查上传全路径是否存在,不存在就创建文件夹
  171. await this.checkPathAndCreate(path);
  172. // 另存文件
  173. await fs.writeFileSync(`${path}${newFileName}`, buffer);
  174. // 替换为files开头
  175. let dataPath = path.replace(this.import_root_path, '/files');
  176. while (dataPath.indexOf('\\') >= 0) {
  177. dataPath = dataPath.replace('\\', '/');
  178. }
  179. // 补全请求用路径的所有路径
  180. dataPath += newFileName;
  181. filePaths.push(dataPath);
  182. }
  183. for (const i of data) {
  184. i.file_url = filePaths;
  185. }
  186. const res = await this.model.insertMany(data);
  187. return res;
  188. }
  189. /**
  190. * 检查路径,没有创建
  191. * @param {Array} filePath 图片路径数组
  192. */
  193. async checkPathAndCreate(filePath) {
  194. const arr = filePath.split(sep);
  195. // w:第一个是盘符,直接和第二个合并
  196. // l:前几个应该都是存在的.也可以合并
  197. let head = _.head(arr);
  198. if (arr[1]) head = `${head}${sep}${arr[1]}`;
  199. arr.shift();
  200. arr[0] = head;
  201. let curPath;
  202. for (const p of arr) {
  203. curPath = `${curPath ? curPath + sep : ''}${p}`;
  204. if (!fs.existsSync(curPath)) {
  205. await fs.mkdirSync(curPath);
  206. }
  207. }
  208. }
  209. /**
  210. * 组织专利预警数据
  211. * @param {Object} patent 专利
  212. */
  213. async getWarningData(patent) {
  214. const object = {};
  215. if (_.get(patent, 'create_number'))object.create_number = _.get(patent, 'create_number');
  216. if (_.get(patent, 'id'))object.patent_id = _.get(patent, 'id');
  217. if (_.get(patent, 'name'))object.patent_name = _.get(patent, 'name');
  218. // 找人的信息
  219. const users = _.get(patent, 'inventor');
  220. const arr = [];
  221. for (const user of users) {
  222. if (!_.get(user, 'user_id') || !_.get(user, 'name')) continue;
  223. const { user_id, name } = user;
  224. const obj = _.cloneDeep(object);
  225. obj.to_id = user_id;
  226. obj.to_name = name;
  227. arr.push(obj);
  228. }
  229. return arr;
  230. }
  231. /**
  232. * 在patentApply/patentInfo表中找指定的专利
  233. * @param {Object} object xml的数据
  234. */
  235. async getPatent(object) {
  236. const { name, create_number } = object;
  237. let res;
  238. let needChange = false;
  239. res = await this.patentApply.findOne({ create_number });
  240. // 如果在申请中,使用名称找到,则之后需要将该数据的专利号改为xml中的专利号
  241. if (!res) {
  242. res = await this.patentApply.findOne({ name });
  243. needChange = true;
  244. }
  245. if (!res) {
  246. res = await this.patentInfo.findOne({ create_number });
  247. }
  248. if (!res) {
  249. res = await this.patentInfo.findOne({ name });
  250. }
  251. const returnObject = {};
  252. if (res) {
  253. if (needChange) {
  254. res.create_number = create_number;
  255. returnObject.needChange = needChange;
  256. }
  257. returnObject.patent = res;
  258. return returnObject;
  259. }
  260. return null;
  261. }
  262. /**
  263. * 根据路径找到xml,再找到该xml的申请号和专利名称
  264. * @param {String} filePath xml的文件路径
  265. */
  266. async dealXml(filePath) {
  267. const buffer = await fs.readFileSync(filePath);
  268. let str = encoding.convert(buffer, 'UTF8', 'GBK');
  269. if (!str) throw new BusinessError(ErrorCode.FILE_FAULT, '读取xml文件失败');
  270. str = str.toString();
  271. const json = fxp.parse(str);
  272. const create_number = _.get(json, 'data-bus.TONGZHISXJ.SHUXINGXX.SHENQINGH');
  273. const name = _.get(json, 'data-bus.TONGZHISXJ.SHUXINGXX.FAMINGMC');
  274. if (!name) return null;
  275. return { name, create_number };
  276. }
  277. /**
  278. * 找到深层的tif和xml
  279. * 因为命名方式都是以文件名,之后tif的文件夹也在深层fileName.套娃且文件名都是fileName
  280. * @param {String} uri temp路径
  281. */
  282. async findFileNameLoop(uri) {
  283. const filePath = `${_.trimEnd(uri, sep)}${sep}`;
  284. if (!fs.existsSync(filePath)) {
  285. return [];
  286. }
  287. const files = fs.readdirSync(filePath);
  288. let result = [];
  289. for (const file of files) {
  290. const curPath = `${filePath}${file}`;
  291. if (fs.statSync(curPath).isDirectory()) {
  292. const arr = await this.findFileNameLoop(curPath);
  293. result.push(...arr);
  294. } else {
  295. result.push(curPath);
  296. }
  297. }
  298. result = result.filter(f => f.includes('.tif') || f.includes('.xml'));
  299. return result;
  300. }
  301. /**
  302. * 删除路径下的所有内容
  303. * @param {String} filePath 路径
  304. */
  305. async dirDelete(filePath) {
  306. if (fs.existsSync(filePath)) {
  307. const files = fs.readdirSync(filePath);
  308. for (const file of files) {
  309. const curPath = `${filePath}${sep}${file}`;
  310. if (fs.statSync(curPath).isDirectory()) { // recurse
  311. this.dirDelete(curPath);
  312. } else { // delete file
  313. fs.unlinkSync(curPath);
  314. }
  315. }
  316. fs.rmdirSync(filePath);
  317. }
  318. }
  319. }
  320. module.exports = PatentwarningService;