match.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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]+$)/, charReg = /(^[a-z]+$)/i;
  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])) {
  54. if (ruleParts[i] !== codeParts[i]) {
  55. return false;
  56. }
  57. } else if (!charReg.test(ruleParts[i])) {
  58. return false;
  59. }
  60. }
  61. return true;
  62. }
  63. /**
  64. * 过滤符合指标节点node匹配规则的清单
  65. * 已经匹配过的清单不再匹配
  66. *
  67. * @param {Object} node - 指标节点
  68. * @private
  69. */
  70. _filterBills (node) {
  71. const self = this;
  72. return this.bills.filter(function (b) {
  73. if (!b.match_node) {
  74. if (node.match_type === nodeConst.matchType.code) {
  75. return self._matchCode(b.code, node.match_key);
  76. } else {
  77. return node.match_key === b.name;
  78. }
  79. } else {
  80. return false;
  81. }
  82. })
  83. }
  84. /**
  85. * 根据指标参数取值类别,从清单取值
  86. *
  87. * @param {Object} param - 指标参数
  88. * @param {Object} bills - 清单节点
  89. * @returns {number}
  90. * @private
  91. */
  92. _getNodeBillsValue(param, bills) {
  93. if (bills) {
  94. switch (param.match_num) {
  95. case paramConst.matchNum.quantity: return bills.quantity;
  96. case paramConst.matchNum.total_price: return bills.total_price;
  97. case paramConst.matchNum.dgn_quantity1: return bills.dgn_quantity1;
  98. case paramConst.matchNum.dgn_quantity2: return bills.dgn_quantity2;
  99. default: return undefined;
  100. }
  101. } else {
  102. return undefined;
  103. }
  104. }
  105. /**
  106. * 获取指标参数取值(固定Id匹配)
  107. * @param {Object} param - 指标参数
  108. * @returns {*}
  109. * @private
  110. */
  111. _getFixedIdParamValue(param) {
  112. for (const b of this.fixedBills) {
  113. if (b.n_id == param.match_key) {
  114. return this._getNodeBillsValue(param, b);
  115. }
  116. }
  117. return undefined;
  118. }
  119. /**
  120. * 获取指标参数取值(编号匹配)
  121. * @param param
  122. * @param nodeBills
  123. * @returns {*}
  124. * @private
  125. */
  126. _getCodeParamValue(param) {
  127. for (const b of this.bills) {
  128. if (this._matchCode(b.code, param.match_key)) {
  129. return this._getNodeBillsValue(param, b);
  130. }
  131. }
  132. return undefined;
  133. }
  134. /**
  135. * 获取父项
  136. * @param {Object} node - 分项清单节点
  137. * @private
  138. */
  139. _getParentBills(node) {
  140. return node ? this.ctx.helper.findObj(this.bills, 'n_id', node.n_pid) : null;
  141. }
  142. /**
  143. * 获取指标参数取值
  144. * @param {Object} param - 指标参数
  145. * @param {Object} nodeBills - 指标参数 所属 指标节点,绑定的 清单
  146. * @returns {*}
  147. * @private
  148. */
  149. _getParamValue(param, nodeBills) {
  150. if (!isNaN(Number(param.name))) {
  151. return Number(param.name);
  152. } else {
  153. switch (param.match_type) {
  154. case paramConst.matchType.fixed_id: return this._getFixedIdParamValue(param);
  155. case paramConst.matchType.node_default: return this._getNodeBillsValue(param, nodeBills);
  156. case paramConst.matchType.parent_default: return this._getNodeBillsValue(param, this._getParentBills(nodeBills));
  157. case paramConst.matchType.code: return this._getCodeParamValue(param);
  158. // to do 匹配属性
  159. default: return undefined;
  160. }
  161. }
  162. }
  163. /**
  164. * 同步指标节点下的全部指标参数
  165. * @param {Number} nodeId
  166. * @private
  167. */
  168. _syncNodeParam(sourceId, nodeId, nodeBills) {
  169. const nodeParams = this.templateParams.filter(function (p) {
  170. return p.node_id === sourceId;
  171. });
  172. for (const np of nodeParams) {
  173. const newParam = {
  174. node_id: nodeId,
  175. lib_id: nodeBills.lib_id,
  176. param_id: this.params.length + 1,
  177. source_id: np.param_id,
  178. code: np.code,
  179. name: np.name,
  180. match_type: np.match_type,
  181. match_key: np.match_key,
  182. match_num: np.match_num,
  183. }
  184. newParam.match_value = this._getParamValue(newParam, nodeBills);
  185. newParam.calc_value = newParam.match_value;
  186. this.params.push(newParam);
  187. }
  188. }
  189. /**
  190. * 同步指标节点下的全部指标
  191. * @param {Number} nodeId
  192. * @private
  193. */
  194. _syncNodeIndex(node) {
  195. const nodeIndexes = this.templateIndexes.filter(function (i) {
  196. return i.node_id === node.source_id;
  197. });
  198. for (const ni of nodeIndexes) {
  199. const newIndex = {
  200. node_id: node.node_id,
  201. lib_id: node.lib_id,
  202. index_id: this.indexes.length + 1,
  203. source_id: ni.index_id,
  204. code: ni.code,
  205. name: ni.name,
  206. unit1: ni.unit1,
  207. unit2: ni.unit2,
  208. rule: ni.rule,
  209. calc_rule: ni.calc_rule,
  210. parse_rule: ni.parse_rule,
  211. index_type: ni.index_type,
  212. }
  213. this.indexes.push(newIndex);
  214. }
  215. }
  216. /**
  217. * 匹配指标节点
  218. * @param node
  219. * @private
  220. */
  221. _matchNode(node) {
  222. if (!node) { return; }
  223. const matchedBills = this._filterBills(node);
  224. for (const mb of matchedBills) {
  225. const newNode = {
  226. node_id: this.nodes.length + 1,
  227. lib_id: mb.lib_id,
  228. source_id: node.node_id,
  229. code: node.code,
  230. name: node.name,
  231. match_type: node.match_type,
  232. match_key: node.match_key,
  233. bills_id: mb.n_id,
  234. }
  235. this.nodes.push(newNode);
  236. this._syncNodeParam(newNode.source_id, newNode.node_id, mb);
  237. this._syncNodeIndex(newNode);
  238. mb.match_node = newNode.node_id;
  239. }
  240. }
  241. /**
  242. * 按模板匹配规则,给清单匹配指标节点
  243. *
  244. * @param {Array} bills - 匹配的清单
  245. * @param {Number} templateId - 用户匹配的模板Id
  246. * @returns {Promise<void>}
  247. */
  248. async matchBills (bills, templateId = 1) {
  249. this._init(bills);
  250. // 获取指标模板全部数据
  251. await this._getTemplateData(templateId);
  252. // 同步全局指标参数
  253. this._syncNodeParam(paramConst.globalParamNodeId, paramConst.globalParamNodeId, this.bills[0]);
  254. // 遍历模板中所有指标节点,匹配清单
  255. for (const node of this.templateNodes) {
  256. this._matchNode(node);
  257. }
  258. // 计算全部指标节点
  259. const globalParams = this.params.filter(function (p) {
  260. return p.node_id === paramConst.globalParamNodeId;
  261. });
  262. for (const node of this.nodes) {
  263. const nodeParams = this.params.filter(function (n) {
  264. return n.node_id === node.node_id;
  265. });
  266. const nodeIndexes = this.indexes.filter(function (i) {
  267. return i.node_id === node.node_id;
  268. });
  269. this.ctx.service.indexCalc.calculate(nodeIndexes, globalParams, nodeParams);
  270. }
  271. }
  272. };
  273. return Match;
  274. }