'use strict'; /** * * * @author Mai * @date 2018/4/25 * @version */ const Service = require('egg').Service; module.exports = app => { const paramConst = app.paramConst; const nodeConst = app.nodeConst; class Match extends Service { /** * 初始化匹配所需变量 * @param {Array|Object} bills - 导入的清单数据 * @private */ _init(bills) { this.bills = bills instanceof Array ? bills : [bills]; this.bills[0].match_node = null; this.fixedBills = this.bills.filter(function (b) { return b.n_id < 100; }); this.nodes = []; this.indexes = []; this.params = []; } /** * 获取指标模板数据 * @param {Number} templateId - 指标模板Id * @returns {Promise} * @private */ async _getTemplateData (templateId) { this.templateNodes = await this.ctx.service.templateNode.getAllDataByCondition({ where: { template_id: templateId } }); this.templateIndexes = await this.ctx.service.templateIndex.getAllDataByCondition({ where: { template_id: templateId } }); this.templateParams = await this.ctx.service.templateParam.getAllDataByCondition({ where: { template_id: templateId } }); } /** * 检查code是否满足匹配规则rule * @param {String} code - 匹配编号 * @param {String} rule - 匹配规则 * @returns {boolean} * @private */ _matchCodeByRule(code, rule) { const codeParts = code.split('-'); const ruleParts = rule.split('-'); const numReg = /(^[0-9]+$)/, charReg = /(^[a-z]+$)/i; if (codeParts.length !== ruleParts.length) { return false; } for (let i = 0, iLen = codeParts.length; i < iLen; i++) { if (numReg.test(ruleParts[i])) { if (ruleParts[i] !== codeParts[i]) { return false; } } else if (!charReg.test(ruleParts[i])) { return false; } } return true; } _matchCodeByRules(code, rules, bills) { const ruleArr = rules.split(';'); for (const r of ruleArr) { if (this._matchCodeByRule(code, r)) { bills.match_key = r; return true; } } return false; } /** * 过滤符合指标节点node匹配规则的清单 * 已经匹配过的清单不再匹配 * * @param {Object} node - 指标节点 * @private */ _filterBills (node) { const self = this; return this.bills.filter(function (b) { if (!b.match_node) { if (node.match_type === nodeConst.matchType.code) { return self._matchCodeByRules(b.code, node.match_key, b); } else { if (node.match_key === b.name) { b.match_key = b.name; return true; } else { return false; } } } else { return false; } }) } /** * 根据指标参数取值类别,从清单取值 * * @param {Object} param - 指标参数 * @param {Object} bills - 清单节点 * @returns {number} * @private */ _getNodeBillsValue(param, bills) { if (bills) { switch (param.match_num) { case paramConst.matchNum.quantity: return bills.quantity; case paramConst.matchNum.total_price: return bills.total_price; case paramConst.matchNum.dgn_quantity1: return bills.dgn_quantity1; case paramConst.matchNum.dgn_quantity2: return bills.dgn_quantity2; default: return undefined; } } else { return undefined; } } _getChildrenBillsValue(param, bills) { if (bills) { const children = this.ctx.helper.filterObj(this.bills, 'n_pid', bills.n_id); switch(param.match_num) { case paramConst.matchNum.quantity: return this.ctx.helper.sumField(children, 'quantity'); case paramConst.matchNum.total_price: return this.ctx.helper.sumField(children, 'total_price'); case paramConst.matchNum.dgn_quantity1: return this.ctx.helper.sumField(children, 'dgn_quantity1'); case paramConst.matchNum.dgn_quantity2: return this.ctx.helper.sumField(children, 'dgn_quantity2'); } } else { return undefined; } } /** * 获取指标参数取值(固定Id匹配) * @param {Object} param - 指标参数 * @returns {*} * @private */ _getFixedIdParamValue(param) { for (const b of this.fixedBills) { if (b.n_id == param.match_key) { return this._getNodeBillsValue(param, b); } } return undefined; } /** * 获取指标参数取值(编号匹配) * @param param * @param nodeBills * @returns {*} * @private */ _getCodeParamValue(param) { for (const b of this.bills) { if (this._matchCodeByRules(b.code, param.match_key, b)) { return this._getNodeBillsValue(param, b); } } return undefined; } /** * 获取父项 * @param {Object} node - 分项清单节点 * @private */ _getParentBills(node) { return node ? this.ctx.helper.findObj(this.bills, 'n_id', node.n_pid) : null; } /** * 获取指标参数取值 * @param {Object} param - 指标参数 * @param {Object} nodeBills - 指标参数 所属 指标节点,绑定的 清单 * @returns {*} * @private */ _getParamValue(param, nodeBills) { if (!isNaN(Number(param.name))) { return Number(param.name); } else { switch (param.match_type) { case paramConst.matchType.fixed_id: return this._getFixedIdParamValue(param); case paramConst.matchType.node_default: return this._getNodeBillsValue(param, nodeBills); case paramConst.matchType.parent_default: return this._getNodeBillsValue(param, this._getParentBills(nodeBills)); case paramConst.matchType.child_gather: return this._getChildrenBillsValue(param, nodeBills); case paramConst.matchType.code: return this._getCodeParamValue(param); // to do 匹配属性 default: return undefined; } } } /** * 同步指标节点下的全部指标参数 * @param {Number} nodeId * @private */ _syncNodeParam(sourceId, nodeId, nodeBills) { const nodeParams = this.templateParams.filter(function (p) { return p.node_id === sourceId; }); for (const np of nodeParams) { const newParam = { node_id: nodeId, lib_id: nodeBills.lib_id, param_id: this.params.length + 1, source_id: np.param_id, code: np.code, name: np.name, match_type: np.match_type, match_key: np.match_key, match_num: np.match_num, }; newParam.match_value = this._getParamValue(newParam, nodeBills); newParam.calc_value = newParam.match_value; this.params.push(newParam); } } /** * 同步指标节点下的全部指标 * @param {Number} nodeId * @private */ _syncNodeIndex(node) { const nodeIndexes = this.templateIndexes.filter(function (i) { return i.node_id === node.source_id; }); for (const ni of nodeIndexes) { const newIndex = { node_id: node.node_id, lib_id: node.lib_id, index_id: this.indexes.length + 1, source_id: ni.index_id, code: ni.code, name: ni.name, unit1: ni.unit1, unit2: ni.unit2, rule: ni.rule, calc_rule: ni.calc_rule, parse_rule: ni.parse_rule, index_type: ni.index_type, } this.indexes.push(newIndex); } } _getXMatchId(bills) { //const reg = /-([a-z])-/i; const reg = /-([a-z])/i; if (reg.test(bills.match_key)) { const ruleParts = bills.match_key.split('-'); const charReg = /(^[a-z]+$)/i; let node = bills, parent = bills; for (let i = ruleParts.length - 1; i>= 0; i--) { if (parent) { if (charReg.test(ruleParts[i])) { return parent.n_id; } node = parent; parent = this.bills.find(function(x) { return x.n_id === node.n_pid; }); } else { return -1; } } } else { return -1; } } /** * 匹配指标节点 * @param node * @private */ _matchNode(node) { if (!node) { return; } const matchedBills = this._filterBills(node); for (const mb of matchedBills) { const newNode = { node_id: this.nodes.length + 1, lib_id: mb.lib_id, source_id: node.node_id, code: node.code, name: node.name, match_type: node.match_type, match_key: mb.match_key, bills_id: mb.n_id, bills_xid: node.match_type === nodeConst.matchType.code ? this._getXMatchId(mb) : -1, index_class: node.index_class, class_name: node.class_name, }; this.nodes.push(newNode); this._syncNodeParam(newNode.source_id, newNode.node_id, mb); this._syncNodeIndex(newNode); mb.match_node = newNode.node_id; } } /** * 按模板匹配规则,给清单匹配指标节点 * * @param {Array} bills - 匹配的清单 * @param {Number} templateId - 用户匹配的模板Id * @returns {Promise} */ async matchBills (bills, templateId = 1) { this._init(bills); // 获取指标模板全部数据 await this._getTemplateData(templateId); // 同步全局指标参数 this._syncNodeParam(paramConst.globalParamNodeId, paramConst.globalParamNodeId, this.bills[0]); // 遍历模板中所有指标节点,匹配清单 for (const node of this.templateNodes) { this._matchNode(node); } // 计算全部指标节点 const globalParams = this.params.filter(function (p) { return p.node_id === paramConst.globalParamNodeId; }); for (const node of this.nodes) { const nodeParams = this.params.filter(function (n) { return n.node_id === node.node_id; }); const nodeIndexes = this.indexes.filter(function (i) { return i.node_id === node.node_id; }); this.ctx.service.indexCalc.calculate(nodeIndexes, globalParams, nodeParams); } for (const b of this.bills) { delete b.match_key; } } _syncCustomData(data, custom) { if (!custom || custom.length === 0) return; for (const d of data) { const c = custom.find(function (x) {return x.name === d.name}); d.calc_value = c.calc_value; } } async reMatchBills(lib, templateId = 1) { const bills = await this.ctx.service.bills.getAllDataByCondition({ where: {lib_id: lib.id} }); this._init(bills); for (const b of bills) { b.match_node = null; } // 获取指标模板全部数据 await this._getTemplateData(templateId); // 同步全局指标参数 this._syncNodeParam(paramConst.globalParamNodeId, paramConst.globalParamNodeId, this.bills[0]); // 遍历模板中所有指标节点,匹配清单 for (const node of this.templateNodes) { this._matchNode(node); } // 计算全部指标节点 const globalParams = this.params.filter(function (p) { return p.node_id === paramConst.globalParamNodeId; }); const ogp = await this.ctx.service.tenderParam.getAllDataByCondition({ where: {lib_id: lib.id} }); this._syncCustomData(globalParams, ogp); for (const node of this.nodes) { const nodeParams = this.params.filter(function (n) { return n.node_id === node.node_id; }); const onp = await this.ctx.service.tenderParam.getParams(lib.id, node.bills_id); this._syncCustomData(nodeParams, onp); const nodeIndexes = this.indexes.filter(function (i) { return i.node_id === node.node_id; }); this.ctx.service.indexCalc.calculate(nodeIndexes, globalParams, nodeParams); } for (const b of this.bills) { delete b.match_key; } } } return Match; };