patentwarning.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose-free/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.User.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 uncompressFilePath = `${this.import_root_path}${sep}temp`;
  28. if (ext !== 'zip') {
  29. throw new BusinessError(ErrorCode.FILE_FAULT, '只接收压缩格式为zip的压缩包');
  30. }
  31. // 解压
  32. try {
  33. await compressing.zip.uncompress(uri, `${uncompressFilePath}`);
  34. } catch (error) {
  35. throw new BusinessError(ErrorCode.FILE_FAULT, '解压失败');
  36. }
  37. // 修改,多个专利:需要扫描下解压后的文件夹,然后循环处理下面所有的内容
  38. const tempDirRootPath = `${uncompressFilePath}${sep}`;
  39. const tempDir = await fs.readdirSync(tempDirRootPath);
  40. const errorList = [];
  41. for (const tempDirPath of tempDir) {
  42. const thisDirPath = `${tempDirRootPath}${sep}${tempDirPath}`;
  43. const patentNumber = tempDirPath;
  44. try {
  45. // 找到
  46. let filePaths = [];
  47. try {
  48. filePaths = await this.findFileNameLoop(thisDirPath);
  49. } catch (error) {
  50. errorList.push({ key: patentNumber, word: '解析文件具体位置失败' });
  51. continue;
  52. }
  53. if (filePaths.length <= 0) {
  54. errorList.push({ key: patentNumber, word: '未找到文件' });
  55. continue;
  56. }
  57. // 2021-10-16 修改: 取出第一层的xml文件,即:路径最短的;除文件名外,字符串长度最小的
  58. let xmlPaths = filePaths.filter(f => f.includes('.xml'));
  59. if (xmlPaths.length > 0) {
  60. // 去掉文件名
  61. xmlPaths = xmlPaths.map(i => {
  62. const arr = i.split(sep);
  63. arr.pop();
  64. const obj = { dir: arr.join(sep), fullPath: i };
  65. return obj;
  66. });
  67. // 按路径字符串长度排序
  68. xmlPaths = xmlPaths.sort((a, b) => {
  69. const anum = a.dir.length;
  70. const bnum = b.dir.length;
  71. return anum - bnum;
  72. });
  73. }
  74. const xmlPath = _.get(_.head(xmlPaths), 'fullPath');
  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数据中的 申请号 和 专利名称 失败 于:${xmlPath}` });
  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. const arr = [];
  216. if (_.get(patent, 'create_number')) object.create_number = _.get(patent, 'create_number');
  217. if (_.get(patent, 'id')) object.patent_id = _.get(patent, 'id');
  218. if (_.get(patent, 'name')) object.patent_name = _.get(patent, 'name');
  219. // 找人的信息
  220. const users = _.get(patent, 'user_id');
  221. if (users && _.isArray(users)) {
  222. for (const user of users) {
  223. if (!_.get(user, 'user_id') || !_.get(user, 'name')) continue;
  224. const { user_id, name } = user;
  225. const obj = _.cloneDeep(object);
  226. obj.to_id = user_id;
  227. obj.to_name = name;
  228. arr.push(obj);
  229. }
  230. // 2021-11-18 添加,user,mech,admin,agentmech是否存在,如果存在,也添加预警信息
  231. const list = [ 'user', 'mech', 'admin', 'agentmech' ];
  232. const suffix = [ 'id', 'name' ];
  233. for (const prefix of list) {
  234. const marr = suffix.map(i => `${prefix}_${i}`);
  235. const r = marr.every(i => _.get(patent, i));
  236. if (r) {
  237. const obj = _.cloneDeep(object);
  238. for (const key of suffix) {
  239. obj[`to_${key}`] = _.get(patent, `${prefix}_${key}`);
  240. }
  241. arr.push(obj);
  242. }
  243. }
  244. } else if (users) {
  245. const obj = _.cloneDeep(object);
  246. const name = _.get(patent, 'user_name');
  247. obj.to_id = users;
  248. obj.to_name = name;
  249. arr.push(obj);
  250. }
  251. return arr;
  252. }
  253. /**
  254. * 在patentApply/patentInfo表中找指定的专利
  255. * @param {Object} object xml的数据
  256. */
  257. async getPatent(object) {
  258. const { name, create_number } = object;
  259. let res;
  260. let needChange = false;
  261. res = await this.patentApply.findOne({ create_number });
  262. // 如果在申请中,使用名称找到,则之后需要将该数据的专利号改为xml中的专利号
  263. if (!res) {
  264. res = await this.patentApply.findOne({ name });
  265. needChange = true;
  266. }
  267. if (!res) {
  268. res = await this.patentInfo.findOne({ create_number });
  269. }
  270. if (!res) {
  271. res = await this.patentInfo.findOne({ name });
  272. }
  273. const returnObject = {};
  274. if (res) {
  275. if (needChange) {
  276. res.create_number = create_number;
  277. returnObject.needChange = needChange;
  278. }
  279. returnObject.patent = res;
  280. return returnObject;
  281. }
  282. return null;
  283. }
  284. /**
  285. * 根据路径找到xml,再找到该xml的申请号和专利名称
  286. * @param {String} filePath xml的文件路径
  287. */
  288. async dealXml(filePath) {
  289. const buffer = await fs.readFileSync(filePath);
  290. let str = encoding.convert(buffer, 'UTF8', 'GBK');
  291. if (!str) throw new BusinessError(ErrorCode.FILE_FAULT, '读取xml文件失败');
  292. str = str.toString();
  293. const json = fxp.parse(str);
  294. const create_number = _.get(json, 'data-bus.TONGZHISXJ.SHUXINGXX.SHENQINGH');
  295. const name = _.get(json, 'data-bus.TONGZHISXJ.SHUXINGXX.FAMINGMC');
  296. if (!name) return null;
  297. return { name, create_number };
  298. }
  299. /**
  300. * 找到深层的tif和xml
  301. * 因为命名方式都是以文件名,之后tif的文件夹也在深层fileName.套娃且文件名都是fileName
  302. * @param {String} uri temp路径
  303. */
  304. async findFileNameLoop(uri) {
  305. const filePath = `${_.trimEnd(uri, sep)}${sep}`;
  306. if (!fs.existsSync(filePath)) {
  307. return [];
  308. }
  309. const files = fs.readdirSync(filePath);
  310. let result = [];
  311. for (const file of files) {
  312. const curPath = `${filePath}${file}`;
  313. if (fs.statSync(curPath).isDirectory()) {
  314. const arr = await this.findFileNameLoop(curPath);
  315. result.push(...arr);
  316. } else {
  317. result.push(curPath);
  318. }
  319. }
  320. result = result.filter(f => f.includes('.tif') || f.includes('.xml'));
  321. return result;
  322. }
  323. /**
  324. * 删除路径下的所有内容
  325. * @param {String} filePath 路径
  326. */
  327. async dirDelete(filePath) {
  328. if (fs.existsSync(filePath)) {
  329. const files = fs.readdirSync(filePath);
  330. for (const file of files) {
  331. const curPath = `${filePath}${sep}${file}`;
  332. if (fs.statSync(curPath).isDirectory()) {
  333. // recurse
  334. this.dirDelete(curPath);
  335. } else {
  336. // delete file
  337. fs.unlinkSync(curPath);
  338. }
  339. }
  340. fs.rmdirSync(filePath);
  341. }
  342. }
  343. }
  344. module.exports = PatentwarningService;