match.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date 2018/4/25
  7. * @version
  8. */
  9. const Service = require('egg').Service;
  10. module.exports = app => {
  11. const paramConst = app.paramConst;
  12. const nodeConst = app.nodeConst;
  13. class Match extends Service {
  14. /**
  15. * 初始化匹配所需变量
  16. * @param {Array|Object} bills - 导入的清单数据
  17. * @private
  18. */
  19. _init(bills) {
  20. this.bills = bills instanceof Array ? bills : [bills];
  21. this.bills[0].match_node = null;
  22. this.fixedBills = this.bills.filter(function (b) {
  23. return b.n_id < 100;
  24. });
  25. this.nodes = [];
  26. this.indexes = [];
  27. this.params = [];
  28. }
  29. /**
  30. * 获取指标模板数据
  31. * @param {Number} templateId - 指标模板Id
  32. * @returns {Promise<void>}
  33. * @private
  34. */
  35. async _getTemplateData (templateId) {
  36. this.templateNodes = await this.ctx.service.templateNode.getAllDataByCondition({ where: { template_id: templateId } });
  37. this.templateIndexes = await this.ctx.service.templateIndex.getAllDataByCondition({ where: { template_id: templateId } });
  38. this.templateParams = await this.ctx.service.templateParam.getAllDataByCondition({ where: { template_id: templateId } });
  39. }
  40. /**
  41. * 检查code是否满足匹配规则rule
  42. * @param {String} code - 匹配编号
  43. * @param {String} rule - 匹配规则
  44. * @returns {boolean}
  45. * @private
  46. */
  47. _matchCode(code, rule) {
  48. const codeParts = code.split('-');
  49. const ruleParts = rule.split('-');
  50. const numReg = /(^[0-9]+$)/;
  51. if (codeParts.length !== ruleParts.length) { return false; }
  52. for (let i = 0, iLen = codeParts.length; i < iLen; i++) {
  53. if (numReg.test(ruleParts[i]) && ruleParts[i] !== codeParts[i]) {
  54. return false;
  55. }
  56. }
  57. return true;
  58. }
  59. /**
  60. * 过滤符合指标节点node匹配规则的清单
  61. * 已经匹配过的清单不再匹配
  62. *
  63. * @param {Object} node - 指标节点
  64. * @private
  65. */
  66. _filterBills (node) {
  67. const self = this;
  68. return this.bills.filter(function (b) {
  69. if (!b.match_node) {
  70. if (node.match_type === nodeConst.matchType.code) {
  71. return self._matchCode(b.code, node.match_key);
  72. } else {
  73. return node.match_key === b.name;
  74. }
  75. } else {
  76. return false;
  77. }
  78. })
  79. }
  80. /**
  81. * 根据指标参数取值类别,从清单取值
  82. *
  83. * @param {Object} param - 指标参数
  84. * @param {Object} bills - 清单节点
  85. * @returns {number}
  86. * @private
  87. */
  88. _getNodeBillsValue(param, bills) {
  89. if (bills) {
  90. switch (param.match_num) {
  91. case paramConst.matchNum.quantity: return bills.quantity;
  92. case paramConst.matchNum.total_price: return bills.total_price;
  93. case paramConst.matchNum.dgn_quantity1: return bills.dgn_quantity1;
  94. case paramConst.matchNum.dgn_quantity2: return bills.dgn_quantity2;
  95. default: return undefined;
  96. }
  97. } else {
  98. return undefined;
  99. }
  100. }
  101. /**
  102. * 获取指标参数取值(固定Id匹配)
  103. * @param {Object} param - 指标参数
  104. * @returns {*}
  105. * @private
  106. */
  107. _getFixedIdParamValue(param) {
  108. for (const b of this.fixedBills) {
  109. if (b.n_id == param.match_key) {
  110. return this._getNodeBillsValue(param, b);
  111. }
  112. }
  113. return undefined;
  114. }
  115. /**
  116. * 获取指标参数取值(编号匹配)
  117. * @param param
  118. * @param nodeBills
  119. * @returns {*}
  120. * @private
  121. */
  122. _getCodeParamValue(param) {
  123. for (const b of this.bills) {
  124. if (this._matchCode(b.code, param.match_key)) {
  125. return this._getNodeBillsValue(param, b);
  126. }
  127. }
  128. return undefined;
  129. }
  130. /**
  131. * 获取父项
  132. * @param {Object} node - 分项清单节点
  133. * @private
  134. */
  135. _getParentBills(node) {
  136. return node ? this.ctx.helper.findObj(this.bills, 'n_id', node.n_pid) : null;
  137. }
  138. /**
  139. * 获取指标参数取值
  140. * @param {Object} param - 指标参数
  141. * @param {Object} nodeBills - 指标参数 所属 指标节点,绑定的 清单
  142. * @returns {*}
  143. * @private
  144. */
  145. _getParamValue(param, nodeBills) {
  146. switch (param.match_type) {
  147. case paramConst.matchType.fixed_id: return this._getFixedIdParamValue(param);
  148. case paramConst.matchType.node_default: return this._getNodeBillsValue(param, nodeBills);
  149. case paramConst.matchType.parent_default: return this._getNodeBillsValue(param, this._getParentBills(nodeBills));
  150. case paramConst.matchType.code: return this._getCodeParamValue(param);
  151. // to do 匹配属性
  152. default: return undefined;
  153. }
  154. }
  155. /**
  156. * 同步指标节点下的全部指标参数
  157. * @param {Number} nodeId
  158. * @private
  159. */
  160. _syncNodeParam(sourceId, nodeId, nodeBills) {
  161. const nodeParams = this.templateParams.filter(function (p) {
  162. return p.node_id === sourceId;
  163. });
  164. for (const np of nodeParams) {
  165. const newParam = {
  166. node_id: nodeId,
  167. lib_id: nodeBills.lib_id,
  168. param_id: this.params.length + 1,
  169. source_id: np.param_id,
  170. code: np.code,
  171. name: np.name,
  172. match_type: np.match_type,
  173. match_key: np.match_key,
  174. match_num: np.match_num,
  175. }
  176. newParam.match_value = this._getParamValue(newParam, nodeBills);
  177. newParam.calc_value = newParam.match_value;
  178. this.params.push(newParam);
  179. }
  180. }
  181. /**
  182. * 同步指标节点下的全部指标
  183. * @param {Number} nodeId
  184. * @private
  185. */
  186. _syncNodeIndex(node) {
  187. const nodeIndexes = this.templateIndexes.filter(function (i) {
  188. return i.node_id === node.source_id;
  189. });
  190. for (const ni of nodeIndexes) {
  191. const newIndex = {
  192. node_id: node.node_id,
  193. lib_id: node.lib_id,
  194. index_id: this.indexes.length + 1,
  195. source_id: ni.index_id,
  196. code: ni.code,
  197. name: ni.name,
  198. unit1: ni.unit1,
  199. unit2: ni.unit2,
  200. rule: ni.rule,
  201. calc_rule: ni.calc_rule,
  202. parse_rule: ni.parse_rule,
  203. index_type: ni.index_type,
  204. }
  205. this.indexes.push(newIndex);
  206. }
  207. }
  208. /**
  209. * 匹配指标节点
  210. * @param node
  211. * @private
  212. */
  213. _matchNode(node) {
  214. if (!node) { return; }
  215. const matchedBills = this._filterBills(node);
  216. for (const mb of matchedBills) {
  217. const newNode = {
  218. node_id: this.nodes.length + 1,
  219. lib_id: mb.lib_id,
  220. source_id: node.node_id,
  221. code: node.code,
  222. name: node.name,
  223. match_type: node.match_type,
  224. match_key: node.match_key,
  225. bills_id: mb.n_id,
  226. }
  227. this.nodes.push(newNode);
  228. this._syncNodeParam(newNode.source_id, newNode.node_id, mb);
  229. this._syncNodeIndex(newNode);
  230. mb.match_node = newNode.node_id;
  231. }
  232. }
  233. /**
  234. * 按模板匹配规则,给清单匹配指标节点
  235. *
  236. * @param {Array} bills - 匹配的清单
  237. * @param {Number} templateId - 用户匹配的模板Id
  238. * @returns {Promise<void>}
  239. */
  240. async matchBills (bills, templateId = 1) {
  241. this._init(bills);
  242. // 获取指标模板全部数据
  243. await this._getTemplateData(templateId);
  244. // 同步全局指标参数
  245. this._syncNodeParam(paramConst.globalParamNodeId, paramConst.globalParamNodeId, this.bills[0]);
  246. // 遍历模板中所有指标节点,匹配清单
  247. for (const node of this.templateNodes) {
  248. this._matchNode(node);
  249. }
  250. // 计算全部指标节点
  251. const globalParams = this.params.filter(function (p) {
  252. return p.node_id === paramConst.globalParamNodeId;
  253. });
  254. for (const node of this.nodes) {
  255. const nodeParams = this.params.filter(function (n) {
  256. return n.node_id === node.node_id;
  257. });
  258. const nodeIndexes = this.indexes.filter(function (i) {
  259. return i.node_id === node.node_id;
  260. });
  261. this.ctx.service.indexCalc.calculate(nodeIndexes, globalParams, nodeParams);
  262. }
  263. }
  264. };
  265. return Match;
  266. }