'use strict'; /** * 指标模板控制器 * * @author Mai * @data 2018/4/19 * @version */ const fs = require('fs'); const streamToArray = require('stream-to-array'); const mathjs = require('mathjs'); module.exports = { /** * 转换数据 * * @param {Array} list - 数据库查找的数据 * @param {Boolean} tree - 是否展示为树形结构 * @param {String} id - id属性名 * @param {String} pid - pid属性名 * @return {Object} - 返回转换后的数据 */ convertData(list, tree = true, id, pid) { const rootData = []; const childData = {}; let listData = []; for (const tmp of list) { if (tmp[pid] === 0 || tmp[pid] === -1) { rootData.push(tmp); continue; } if (childData[tmp[pid]] === undefined) { childData[tmp[pid]] = []; } childData[tmp[pid]].push(tmp); } // 递归组织数据 if (tree) { this._recursionTreeData(rootData, childData, id); } else { this._recursionData(rootData, childData, listData, id); } return tree ? rootData : listData; }, /** * 递归组织数组结构数据 * * @param {Array} parent - 父节点 * @param {Object} childData - 子元素(以pid为key的对象) * @param {Array} outputData - 处理后的数据 * @param {String} id - id属性名 * @return {void} */ _recursionData(parent, childData, outputData = [], id) { for (const index in parent) { // 父节点先进数组 outputData.push(parent[index]); // 判断是否存在子项 if (childData[parent[index][id]] !== undefined) { // 子项作为父节点递归查找子项中是否还有子项 const tmpParent = childData[parent[index][id]]; // 已用的子项删除 delete childData[parent[index][id]]; // 递归元素 this._recursionData(tmpParent, childData, outputData); } } }, /** * 递归组织树结构数据 * * @param {Array} parent - 父节点 * @param {Object} childData - 子元素(以pid为key的对象) * @param {String} id - id属性名 * @return {void} */ _recursionTreeData(parent, childData = {}, id) { if (Object.keys(childData).length <= 0) { return; } for (const index in parent) { // 判断是否存在子项 if (childData[parent[index][id]] !== undefined) { // 子项作为父节点递归查找子项中是否还有子项 const tmpParent = childData[parent[index][id]]; // 已用的子项删除 delete childData[parent[index][id]]; this._recursionTreeData(tmpParent, childData, id); // 递归完赋值回原数据 parent[index].children = tmpParent; } } }, /** * 将文件流的数据保存至本地文件 * @param stream * @param fileName * @returns {Promise} */ async saveStreamFile(stream, fileName) { // 读取字节流 const parts = await streamToArray(stream); // 转化为buffer const buffer = Buffer.concat(parts); // 写入文件 await fs.writeFileSync(fileName, buffer); }, /** * 检查code是否是指标模板数据 * @param {String} code * @returns {boolean} */ ValidTemplateCode(code) { const reg1 = /(^[a-z]+)([a-z0-9\-]*)/i; const reg2 = /([a-z0-9]+$)/i; return reg1.test(code) && reg2.test(code); }, /** * 检查code 是否是 指标节点编号 * @param {String} code * @returns {boolean} */ ValidTemplateNodeCode(code) { const reg1 = /(^[a-z]+)([a-z0-9\-]*$)/i; const reg2 = /([a-z0-9]+$)/i; const reg3 = /([\-][0-9]+$)/i; return reg1.test(code) && reg2.test(code) && (!reg3.test(code)); }, /** * 检查code 是否是 指标节点编号 * @param {String} code * @returns {boolean} */ ValidTemplateIndexCode(code) { const reg1 = /(^[a-z]+)([a-z0-9\-]*$)/i; const reg2 = /([0-9]+$)/i; return reg1.test(code) && reg2.test(code); }, /** * 检查code 是否 是有效的绑定分项编号 * @param {String} code - 编号 * @return {Boolean} */ validMatchCode(code) { const parts = code.split('-'); const reg1 = /(^[a-z]+$)/i; const reg2 = /(^[0-9]+$)/; for (const i in parts) { if (i == 0) { if (!reg2.test(parts[i])) { return false; } } else { if (!(reg1.test(parts[i]) || reg2.test(parts[i]))) { return false; } } } return true; }, /** * 在数组arr中查找a.field=value的成员a * * @param {Array} arr - 数组 * @param {String} field - 匹配属性名称 * @param {any} value - 匹配属性值 * @returns {*} */ findObj(arr, field, value) { if (arr.length === 0) { return undefined; } for (const a of arr) { if (a[field] && a[field] === value) { return a; } } return undefined; }, /** * 读取json文件并解析 * * @param {String} fileName - json文件路径 * @returns {any} */ loadJsonFile(fileName) { const sJson = fs.readFileSync(fileName); return JSON.parse(sJson); }, /** * 增删改单条json数据 * @param {string} json 单条json值 * @param {String} key 属性 * @param {String} value 值 * @returns {string} */ operationJson(json,key, value) { if(typeof value === "undefined") { delete json[key]; } else { json[key] = value; } return json; }, isOperator(value) { const operatorString = "+-*/()"; return operatorString.indexOf(value) > -1 }, parse2Rpn(exp) { const getPriority = function (value){ switch(value){ case '+': case '-': return 1; case '*': case '/': return 2; default: return 0; } }; const priority = function (o1, o2){ return getPriority(o1) <= getPriority(o2); }; const inputStack = []; const outputStack = []; const outputQueue = []; for (let i = 0, len = exp.length; i < len; i++) { const cur = exp[i]; if (cur !== ' ' ) { inputStack.push(cur); } } let num = '', isNumPre = false, isOperatorPre = false; while(inputStack.length > 0){ const cur = inputStack.shift(); if (this.isOperator(cur) && !(cur === '-' && !isNumPre)) { if (isNumPre) { outputQueue.push(parseFloat(num)); num = ''; } if (cur === '(') { outputStack.push(cur); } else if (cur === ')') { let po = outputStack.pop(); while (po !== '(' && outputStack.length > 0) { outputQueue.push(po); po = outputStack.pop(); } if (po !== '(') { throw "error: unmatched ()"; } } else { while(priority(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0){ outputQueue.push(outputStack.pop()); } outputStack.push(cur); } isNumPre = false; isOperatorPre = true; } else { num = num + cur; isNumPre = true; isOperatorPre = false; if (inputStack.length === 0) { outputQueue.push(parseFloat(num)); } } } if (outputStack.length > 0) { if (outputStack[outputStack.length - 1] === ')' || outputStack[outputStack.length - 1] === '(') { throw "error: unmatched ()"; } while (outputStack.length > 0) { outputQueue.push(outputStack.pop()); } } return outputQueue; }, evalRpn(rpnQueue) { const getResult = function (num1, num2, opera) { switch (opera) { case '+': return num1 + num2; case '-': return num1 - num2; case '*': return num1 * num2; case '/': return num1 / num2; default: throw '参数错误'; } }; const outputStack = []; while(rpnQueue.length > 0){ const cur = rpnQueue.shift(); if (!this.isOperator(cur)) { outputStack.push(cur); } else { if (outputStack.length < 2) { throw "unvalid stack length"; } const sec = outputStack.pop(); const fir = outputStack.pop(); outputStack.push(getResult(fir, sec, cur)); } } if (outputStack.length !== 1) { throw "unvalid expression"; } else { return outputStack[0]; } }, /** * 解析四则运算字符串并计算 * @param {String} expr * @returns */ calcExprStrRpn(expr) { try { if (!isNaN(Number(expr))) { return Number(expr); } else { const rpnArr = this.parse2Rpn(expr); const result = this.evalRpn(rpnArr); return result === Infinity ? NaN : result; } } catch (err) { return NaN; } }, calcExprStr(expr) { try { const result = mathjs.eval(expr); return result === Infinity ? NaN : result; } catch (err) { return NaN; } } };