helper.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. 'use strict';
  2. /**
  3. * 指标模板控制器
  4. *
  5. * @author Mai
  6. * @data 2018/4/19
  7. * @version
  8. */
  9. const fs = require('fs');
  10. const streamToArray = require('stream-to-array');
  11. const mathjs = require('mathjs');
  12. module.exports = {
  13. /**
  14. * 转换数据
  15. *
  16. * @param {Array} list - 数据库查找的数据
  17. * @param {Boolean} tree - 是否展示为树形结构
  18. * @param {String} id - id属性名
  19. * @param {String} pid - pid属性名
  20. * @return {Object} - 返回转换后的数据
  21. */
  22. convertData(list, tree = true, id, pid) {
  23. const rootData = [];
  24. const childData = {};
  25. let listData = [];
  26. for (const tmp of list) {
  27. if (tmp[pid] === 0 || tmp[pid] === -1) {
  28. rootData.push(tmp);
  29. continue;
  30. }
  31. if (childData[tmp[pid]] === undefined) {
  32. childData[tmp[pid]] = [];
  33. }
  34. childData[tmp[pid]].push(tmp);
  35. }
  36. // 递归组织数据
  37. if (tree) {
  38. this._recursionTreeData(rootData, childData, id);
  39. } else {
  40. this._recursionData(rootData, childData, listData, id);
  41. }
  42. return tree ? rootData : listData;
  43. },
  44. /**
  45. * 递归组织数组结构数据
  46. *
  47. * @param {Array} parent - 父节点
  48. * @param {Object} childData - 子元素(以pid为key的对象)
  49. * @param {Array} outputData - 处理后的数据
  50. * @param {String} id - id属性名
  51. * @return {void}
  52. */
  53. _recursionData(parent, childData, outputData = [], id) {
  54. for (const index in parent) {
  55. // 父节点先进数组
  56. outputData.push(parent[index]);
  57. // 判断是否存在子项
  58. if (childData[parent[index][id]] !== undefined) {
  59. // 子项作为父节点递归查找子项中是否还有子项
  60. const tmpParent = childData[parent[index][id]];
  61. // 已用的子项删除
  62. delete childData[parent[index][id]];
  63. // 递归元素
  64. this._recursionData(tmpParent, childData, outputData);
  65. }
  66. }
  67. },
  68. /**
  69. * 递归组织树结构数据
  70. *
  71. * @param {Array} parent - 父节点
  72. * @param {Object} childData - 子元素(以pid为key的对象)
  73. * @param {String} id - id属性名
  74. * @return {void}
  75. */
  76. _recursionTreeData(parent, childData = {}, id) {
  77. if (Object.keys(childData).length <= 0) {
  78. return;
  79. }
  80. for (const index in parent) {
  81. // 判断是否存在子项
  82. if (childData[parent[index][id]] !== undefined) {
  83. // 子项作为父节点递归查找子项中是否还有子项
  84. const tmpParent = childData[parent[index][id]];
  85. // 已用的子项删除
  86. delete childData[parent[index][id]];
  87. this._recursionTreeData(tmpParent, childData, id);
  88. // 递归完赋值回原数据
  89. parent[index].children = tmpParent;
  90. }
  91. }
  92. },
  93. /**
  94. * 将文件流的数据保存至本地文件
  95. * @param stream
  96. * @param fileName
  97. * @returns {Promise<void>}
  98. */
  99. async saveStreamFile(stream, fileName) {
  100. // 读取字节流
  101. const parts = await streamToArray(stream);
  102. // 转化为buffer
  103. const buffer = Buffer.concat(parts);
  104. // 写入文件
  105. await fs.writeFileSync(fileName, buffer);
  106. },
  107. /**
  108. * 检查code是否是指标模板数据
  109. * @param {String} code
  110. * @returns {boolean}
  111. */
  112. ValidTemplateCode(code) {
  113. const reg1 = /(^[a-z]+)([a-z0-9\-]*)/i;
  114. const reg2 = /([a-z0-9]+$)/i;
  115. return reg1.test(code) && reg2.test(code);
  116. },
  117. /**
  118. * 检查code 是否是 指标节点编号
  119. * @param {String} code
  120. * @returns {boolean}
  121. */
  122. ValidTemplateNodeCode(code) {
  123. const reg1 = /(^[a-z]+)([a-z0-9\-]*$)/i;
  124. const reg2 = /([a-z0-9]+$)/i;
  125. const reg3 = /([\-][0-9]+$)/i;
  126. return reg1.test(code) && reg2.test(code) && (!reg3.test(code));
  127. },
  128. /**
  129. * 检查code 是否是 指标节点编号
  130. * @param {String} code
  131. * @returns {boolean}
  132. */
  133. ValidTemplateIndexCode(code) {
  134. const reg1 = /(^[a-z]+)([a-z0-9\-]*$)/i;
  135. const reg2 = /([0-9]+$)/i;
  136. return reg1.test(code) && reg2.test(code);
  137. },
  138. /**
  139. * 检查code 是否 是有效的绑定分项编号
  140. * @param {String} code - 编号
  141. * @return {Boolean}
  142. */
  143. validMatchCode(code) {
  144. const parts = code.split('-');
  145. const reg1 = /(^[a-z]+$)/i;
  146. const reg2 = /(^[0-9]+$)/;
  147. for (const i in parts) {
  148. if (i == 0) {
  149. if (!reg2.test(parts[i])) {
  150. return false;
  151. }
  152. } else {
  153. if (!(reg1.test(parts[i]) || reg2.test(parts[i]))) {
  154. return false;
  155. }
  156. }
  157. }
  158. return true;
  159. },
  160. /**
  161. * 在数组arr中查找a.field=value的成员a
  162. *
  163. * @param {Array} arr - 数组
  164. * @param {String} field - 匹配属性名称
  165. * @param {any} value - 匹配属性值
  166. * @returns {*}
  167. */
  168. findObj(arr, field, value) {
  169. if (arr.length === 0) { return undefined; }
  170. for (const a of arr) {
  171. if (a[field] && a[field] === value) {
  172. return a;
  173. }
  174. }
  175. return undefined;
  176. },
  177. /**
  178. * 读取json文件并解析
  179. *
  180. * @param {String} fileName - json文件路径
  181. * @returns {any}
  182. */
  183. loadJsonFile(fileName) {
  184. const sJson = fs.readFileSync(fileName);
  185. return JSON.parse(sJson);
  186. },
  187. /**
  188. * 增删改单条json数据
  189. * @param {string} json 单条json值
  190. * @param {String} key 属性
  191. * @param {String} value 值
  192. * @returns {string}
  193. */
  194. operationJson(json,key, value) {
  195. if(typeof value === "undefined") {
  196. delete json[key];
  197. } else {
  198. json[key] = value;
  199. }
  200. return json;
  201. },
  202. /**
  203. * 判断value是否是四则运算符
  204. * @param {String} value - 判断字符串
  205. * @return {Boolean}
  206. */
  207. isOperator(value) {
  208. const operatorString = "+-*/()";
  209. return operatorString.indexOf(value) > -1;
  210. },
  211. /**
  212. * Rpn解析栈
  213. * @param exp
  214. * @returns {Array}
  215. */
  216. parse2Rpn(exp) {
  217. const getPriority = function (value){
  218. switch(value){
  219. case '+':
  220. case '-':
  221. return 1;
  222. case '*':
  223. case '/':
  224. return 2;
  225. default:
  226. return 0;
  227. }
  228. };
  229. const priority = function (o1, o2){
  230. return getPriority(o1) <= getPriority(o2);
  231. };
  232. const inputStack = [];
  233. const outputStack = [];
  234. const outputQueue = [];
  235. for (let i = 0, len = exp.length; i < len; i++) {
  236. const cur = exp[i];
  237. if (cur !== ' ' ) {
  238. inputStack.push(cur);
  239. }
  240. }
  241. let num = '', isNumPre = false, isOperatorPre = false;
  242. while(inputStack.length > 0){
  243. const cur = inputStack.shift();
  244. if (this.isOperator(cur) && !(cur === '-' && !isNumPre)) {
  245. if (isNumPre) {
  246. outputQueue.push(parseFloat(num));
  247. num = '';
  248. }
  249. if (cur === '(') {
  250. outputStack.push(cur);
  251. } else if (cur === ')') {
  252. let po = outputStack.pop();
  253. while (po !== '(' && outputStack.length > 0) {
  254. outputQueue.push(po);
  255. po = outputStack.pop();
  256. }
  257. if (po !== '(') {
  258. throw "error: unmatched ()";
  259. }
  260. } else {
  261. while(priority(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0){
  262. outputQueue.push(outputStack.pop());
  263. }
  264. outputStack.push(cur);
  265. }
  266. isNumPre = false;
  267. isOperatorPre = true;
  268. } else {
  269. num = num + cur;
  270. isNumPre = true;
  271. isOperatorPre = false;
  272. if (inputStack.length === 0) {
  273. outputQueue.push(parseFloat(num));
  274. }
  275. }
  276. }
  277. if (outputStack.length > 0) {
  278. if (outputStack[outputStack.length - 1] === ')' || outputStack[outputStack.length - 1] === '(') {
  279. throw "error: unmatched ()";
  280. }
  281. while (outputStack.length > 0) {
  282. outputQueue.push(outputStack.pop());
  283. }
  284. }
  285. return outputQueue;
  286. },
  287. /**
  288. * 计算Rpn解析栈
  289. * @param rpnQueue
  290. * @returns {*}
  291. */
  292. evalRpn(rpnQueue) {
  293. const getResult = function (num1, num2, opera) {
  294. switch (opera) {
  295. case '+':
  296. return num1 + num2;
  297. case '-':
  298. return num1 - num2;
  299. case '*':
  300. return num1 * num2;
  301. case '/':
  302. return num1 / num2;
  303. default:
  304. throw '参数错误';
  305. }
  306. };
  307. const outputStack = [];
  308. while(rpnQueue.length > 0){
  309. const cur = rpnQueue.shift();
  310. if (!this.isOperator(cur)) {
  311. outputStack.push(cur);
  312. } else {
  313. if (outputStack.length < 2) {
  314. throw "unvalid stack length";
  315. }
  316. const sec = outputStack.pop();
  317. const fir = outputStack.pop();
  318. outputStack.push(getResult(fir, sec, cur));
  319. }
  320. }
  321. if (outputStack.length !== 1) {
  322. throw "unvalid expression";
  323. } else {
  324. return outputStack[0];
  325. }
  326. },
  327. /**
  328. * 解析四则运算字符串并计算
  329. * @param {String} expr
  330. * @returns
  331. */
  332. calcExprStrRpn(expr) {
  333. try {
  334. if (!isNaN(Number(expr))) {
  335. return Number(expr);
  336. } else {
  337. const rpnArr = this.parse2Rpn(expr);
  338. const result = this.evalRpn(rpnArr);
  339. return result === Infinity ? null : result;
  340. }
  341. } catch (err) {
  342. return null;
  343. }
  344. },
  345. /**
  346. * 使用四则运算符分隔
  347. * @param expr
  348. * @returns {Array}
  349. */
  350. splitByOperator (expr) {
  351. const exprStack = [];
  352. const result = [];
  353. for(let i = 0, len = expr.length; i < len; i++){
  354. const cur = expr[i];
  355. if(cur !== ' ' ){
  356. exprStack.push(cur);
  357. }
  358. }
  359. let param = '', isParamBefore = false;
  360. while(exprStack.length > 0){
  361. const cur = exprStack.shift();
  362. if (this.isOperator(cur)) {
  363. if (isParamBefore) {
  364. result.push(param);
  365. param = '';
  366. }
  367. result.push(cur);
  368. isParamBefore = false;
  369. } else {
  370. param = param + cur;
  371. isParamBefore = true;
  372. if (exprStack.length === 0) {
  373. result.push(param);
  374. param = '';
  375. }
  376. }
  377. }
  378. return result;
  379. },
  380. /**
  381. * 解析 数学计算式(字符串) 并计算
  382. * @param expr
  383. * @returns {null}
  384. */
  385. calcExprStr(expr) {
  386. try {
  387. const result = mathjs.eval(expr);
  388. return result === Infinity ? null : result;
  389. } catch (err) {
  390. return null;
  391. }
  392. }
  393. };