helper.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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. const Decimal = require('decimal.js');
  13. Decimal.set({precision: 50, defaults: true});
  14. const bc = require('../lib/base_calc.js');
  15. module.exports = {
  16. /**
  17. * 转换数据
  18. *
  19. * @param {Array} list - 数据库查找的数据
  20. * @param {Boolean} tree - 是否展示为树形结构
  21. * @param {String} id - id属性名
  22. * @param {String} pid - pid属性名
  23. * @return {Object} - 返回转换后的数据
  24. */
  25. convertData(list, tree = true, id, pid) {
  26. const rootData = [];
  27. const childData = {};
  28. let listData = [];
  29. for (const tmp of list) {
  30. if (tmp[pid] === 0 || tmp[pid] === -1) {
  31. rootData.push(tmp);
  32. continue;
  33. }
  34. if (childData[tmp[pid]] === undefined) {
  35. childData[tmp[pid]] = [];
  36. }
  37. childData[tmp[pid]].push(tmp);
  38. }
  39. // 递归组织数据
  40. if (tree) {
  41. this._recursionTreeData(rootData, childData, id);
  42. } else {
  43. this._recursionData(rootData, childData, listData, id);
  44. }
  45. return tree ? rootData : listData;
  46. },
  47. /**
  48. * 递归组织数组结构数据
  49. *
  50. * @param {Array} parent - 父节点
  51. * @param {Object} childData - 子元素(以pid为key的对象)
  52. * @param {Array} outputData - 处理后的数据
  53. * @param {String} id - id属性名
  54. * @return {void}
  55. */
  56. _recursionData(parent, childData, outputData = [], id) {
  57. for (const index in parent) {
  58. // 父节点先进数组
  59. outputData.push(parent[index]);
  60. // 判断是否存在子项
  61. if (childData[parent[index][id]] !== undefined) {
  62. // 子项作为父节点递归查找子项中是否还有子项
  63. const tmpParent = childData[parent[index][id]];
  64. // 已用的子项删除
  65. delete childData[parent[index][id]];
  66. // 递归元素
  67. this._recursionData(tmpParent, childData, outputData);
  68. }
  69. }
  70. },
  71. /**
  72. * 递归组织树结构数据
  73. *
  74. * @param {Array} parent - 父节点
  75. * @param {Object} childData - 子元素(以pid为key的对象)
  76. * @param {String} id - id属性名
  77. * @return {void}
  78. */
  79. _recursionTreeData(parent, childData = {}, id) {
  80. if (Object.keys(childData).length <= 0) {
  81. return;
  82. }
  83. for (const index in parent) {
  84. // 判断是否存在子项
  85. if (childData[parent[index][id]] !== undefined) {
  86. // 子项作为父节点递归查找子项中是否还有子项
  87. const tmpParent = childData[parent[index][id]];
  88. // 已用的子项删除
  89. delete childData[parent[index][id]];
  90. this._recursionTreeData(tmpParent, childData, id);
  91. // 递归完赋值回原数据
  92. parent[index].children = tmpParent;
  93. }
  94. }
  95. },
  96. /**
  97. * 将文件流的数据保存至本地文件
  98. * @param stream
  99. * @param fileName
  100. * @returns {Promise<void>}
  101. */
  102. async saveStreamFile(stream, fileName) {
  103. // 读取字节流
  104. const parts = await streamToArray(stream);
  105. // 转化为buffer
  106. const buffer = Buffer.concat(parts);
  107. // 写入文件
  108. await fs.writeFileSync(fileName, buffer);
  109. },
  110. /**
  111. * 检查code是否是指标模板数据
  112. * @param {String} code
  113. * @returns {boolean}
  114. */
  115. ValidTemplateCode(code) {
  116. const reg1 = /(^[a-z]+)([a-z0-9\-]*)/i;
  117. const reg2 = /([a-z0-9]+$)/i;
  118. return reg1.test(code) && reg2.test(code);
  119. },
  120. /**
  121. * 检查code 是否是 指标节点编号
  122. * @param {String} code
  123. * @returns {boolean}
  124. */
  125. ValidTemplateNodeCode(code) {
  126. const reg1 = /(^[a-z]+)([a-z0-9\-]*$)/i;
  127. const reg2 = /([a-z0-9]+$)/i;
  128. const reg3 = /([\-][0-9]+$)/i;
  129. return reg1.test(code) && reg2.test(code) && (!reg3.test(code));
  130. },
  131. /**
  132. * 检查code 是否是 指标编号
  133. * @param {String} code
  134. * @returns {boolean}
  135. */
  136. ValidTemplateIndexCode(code) {
  137. const reg1 = /(^[a-z]+)([a-z0-9\-]*$)/i;
  138. const reg2 = /([0-9]+$)/i;
  139. return reg1.test(code) && reg2.test(code);
  140. },
  141. /**
  142. * 检查code 是否 是有效的绑定分项编号
  143. * @param {String} code - 编号
  144. * @return {Boolean}
  145. */
  146. validMatchCode(code) {
  147. const codes = code.split(';');
  148. for (const c of codes) {
  149. const parts = c.split('-');
  150. const reg1 = /(^[a-z]+$)/i;
  151. const reg2 = /(^[0-9]+$)/;
  152. for (const i in parts) {
  153. if (i == 0) {
  154. if (!reg2.test(parts[i])) {
  155. return false;
  156. }
  157. } else {
  158. if (!(reg1.test(parts[i]) || reg2.test(parts[i]))) {
  159. return false;
  160. }
  161. }
  162. }
  163. }
  164. return true;
  165. },
  166. /**
  167. * 在数组arr中查找a.field=value的成员a
  168. *
  169. * @param {Array} arr - 数组
  170. * @param {String} field - 匹配属性名称
  171. * @param {any} value - 匹配属性值
  172. * @returns {*}
  173. */
  174. findObj(arr, field, value) {
  175. if (arr.length === 0) { return undefined; }
  176. for (const a of arr) {
  177. if (a[field] && a[field] === value) {
  178. return a;
  179. }
  180. }
  181. return undefined;
  182. },
  183. filterObj(arr, field, value) {
  184. if (arr.length === 0) { return undefined; }
  185. const newArr = [];
  186. for (const a of arr) {
  187. if (a[field] && a[field] === value) {
  188. newArr.push(a);
  189. }
  190. }
  191. return newArr;
  192. },
  193. /**
  194. * 读取json文件并解析
  195. *
  196. * @param {String} fileName - json文件路径
  197. * @returns {any}
  198. */
  199. loadJsonFile(fileName) {
  200. const sJson = fs.readFileSync(fileName);
  201. return JSON.parse(sJson);
  202. },
  203. /**
  204. * 增删改单条json数据
  205. * @param {string} json 单条json值
  206. * @param {String} key 属性
  207. * @param {String} value 值
  208. * @returns {string}
  209. */
  210. operationJson(json,key, value) {
  211. if(typeof value === "undefined") {
  212. delete json[key];
  213. } else {
  214. json[key] = value;
  215. }
  216. return json;
  217. },
  218. /**
  219. * 判断value是否是四则运算符
  220. * @param {String} value - 判断字符串
  221. * @return {Boolean}
  222. */
  223. isOperator(value) {
  224. const operatorString = "+-*/()";
  225. return operatorString.indexOf(value) > -1;
  226. },
  227. /**
  228. * Rpn解析栈
  229. * @param exp
  230. * @returns {Array}
  231. */
  232. parse2Rpn(exp) {
  233. const getPriority = function (value){
  234. switch(value){
  235. case '+':
  236. case '-':
  237. return 1;
  238. case '*':
  239. case '/':
  240. return 2;
  241. default:
  242. return 0;
  243. }
  244. };
  245. const priority = function (o1, o2){
  246. return getPriority(o1) <= getPriority(o2);
  247. };
  248. const inputStack = [];
  249. const outputStack = [];
  250. const outputQueue = [];
  251. for (let i = 0, len = exp.length; i < len; i++) {
  252. const cur = exp[i];
  253. if (cur !== ' ' ) {
  254. inputStack.push(cur);
  255. }
  256. }
  257. let num = '', isNumPre = false, isOperatorPre = false;
  258. while(inputStack.length > 0){
  259. const cur = inputStack.shift();
  260. if (this.isOperator(cur) && !(cur === '-' && !isNumPre)) {
  261. if (isNumPre) {
  262. outputQueue.push(parseFloat(num));
  263. num = '';
  264. }
  265. if (cur === '(') {
  266. outputStack.push(cur);
  267. } else if (cur === ')') {
  268. let po = outputStack.pop();
  269. while (po !== '(' && outputStack.length > 0) {
  270. outputQueue.push(po);
  271. po = outputStack.pop();
  272. }
  273. if (po !== '(') {
  274. throw "error: unmatched ()";
  275. }
  276. } else {
  277. while(priority(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0){
  278. outputQueue.push(outputStack.pop());
  279. }
  280. outputStack.push(cur);
  281. }
  282. isNumPre = false;
  283. isOperatorPre = true;
  284. } else {
  285. num = num + cur;
  286. isNumPre = true;
  287. isOperatorPre = false;
  288. if (inputStack.length === 0) {
  289. outputQueue.push(parseFloat(num));
  290. }
  291. }
  292. }
  293. if (outputStack.length > 0) {
  294. if (outputStack[outputStack.length - 1] === ')' || outputStack[outputStack.length - 1] === '(') {
  295. throw "error: unmatched ()";
  296. }
  297. while (outputStack.length > 0) {
  298. outputQueue.push(outputStack.pop());
  299. }
  300. }
  301. return outputQueue;
  302. },
  303. /**
  304. * 计算Rpn解析栈
  305. * @param rpnQueue
  306. * @returns {*}
  307. */
  308. evalRpn(rpnQueue) {
  309. const getResult = function (num1, num2, opera) {
  310. switch (opera) {
  311. case '+':
  312. return num1 + num2;
  313. case '-':
  314. return num1 - num2;
  315. case '*':
  316. return num1 * num2;
  317. case '/':
  318. return num1 / num2;
  319. default:
  320. throw '参数错误';
  321. }
  322. };
  323. const outputStack = [];
  324. while(rpnQueue.length > 0){
  325. const cur = rpnQueue.shift();
  326. if (!this.isOperator(cur)) {
  327. outputStack.push(cur);
  328. } else {
  329. if (outputStack.length < 2) {
  330. throw "unvalid stack length";
  331. }
  332. const sec = outputStack.pop();
  333. const fir = outputStack.pop();
  334. outputStack.push(getResult(fir, sec, cur));
  335. }
  336. }
  337. if (outputStack.length !== 1) {
  338. throw "unvalid expression";
  339. } else {
  340. return outputStack[0];
  341. }
  342. },
  343. /**
  344. * 解析四则运算字符串并计算
  345. * @param {String} expr
  346. * @returns
  347. */
  348. calcExprStrRpn(expr) {
  349. try {
  350. if (!isNaN(Number(expr))) {
  351. return Number(expr);
  352. } else {
  353. const rpnArr = this.parse2Rpn(expr);
  354. const result = this.evalRpn(rpnArr);
  355. return result === Infinity ? null : result;
  356. }
  357. } catch (err) {
  358. return null;
  359. }
  360. },
  361. /**
  362. * 使用四则运算符分隔
  363. * @param expr
  364. * @returns {Array}
  365. */
  366. splitByOperator (expr) {
  367. const exprStack = [];
  368. const result = [];
  369. for(let i = 0, len = expr.length; i < len; i++){
  370. const cur = expr[i];
  371. if(cur !== ' ' ){
  372. exprStack.push(cur);
  373. }
  374. }
  375. let param = '', isParamBefore = false;
  376. while(exprStack.length > 0){
  377. const cur = exprStack.shift();
  378. if (this.isOperator(cur)) {
  379. if (isParamBefore) {
  380. result.push(param);
  381. param = '';
  382. }
  383. result.push(cur);
  384. isParamBefore = false;
  385. } else {
  386. param = param + cur;
  387. isParamBefore = true;
  388. if (exprStack.length === 0) {
  389. result.push(param);
  390. param = '';
  391. }
  392. }
  393. }
  394. return result;
  395. },
  396. /**
  397. * 解析 数学计算式(字符串) 并计算
  398. * @param expr
  399. * @returns {null}
  400. */
  401. calcExprStr(expr) {
  402. try {
  403. const result = mathjs.eval(expr);
  404. return result === Infinity ? null : result;
  405. } catch (err) {
  406. return null;
  407. }
  408. },
  409. // 加减乘除方法,为方便调用,兼容num为空的情况
  410. // 加减法使用base_calc,乘除法使用Decimal(原因详见demo/calc_test)
  411. /**
  412. * 加法 num1 + num2
  413. * @param num1
  414. * @param num2
  415. * @returns {number}
  416. */
  417. add(num1, num2) {
  418. return bc.add(num1 ? num1 : 0, num2 ? num2: 0);
  419. },
  420. /**
  421. * 减法 num1 - num2
  422. * @param num1
  423. * @param num2
  424. * @returns {number}
  425. */
  426. sub(num1, num2) {
  427. return bc.sub(num1 ? num1 : 0, num2 ? num2 : 0);
  428. },
  429. /**
  430. * 乘法 num1 * num2
  431. * @param num1
  432. * @param num2
  433. * @returns {*}
  434. */
  435. mul(num1, num2, digit = 6) {
  436. return Decimal.mul(num1 ? num1 : 0, num2 ? num2 : 0).toDecimalPlaces(digit).toNumber();
  437. },
  438. /**
  439. * 除法 num1 / num2
  440. * @param num1 - 被除数
  441. * @param num2 - 除数
  442. * @returns {*}
  443. */
  444. div(num1, num2, digit = 6) {
  445. if (num2 && !this.checkZero(num2)) {
  446. return Decimal.div(num1 ? num1: 0, num2).toDecimalPlaces(digit).toNumber();
  447. } else {
  448. return null;
  449. }
  450. },
  451. /**
  452. * 四舍五入(统一,方便以后万一需要置换)
  453. * @param {Number} value - 舍入的数字
  454. * @param {Number} decimal - 要保留的小数位数
  455. * @returns {*}
  456. */
  457. round(value, decimal) {
  458. //return value ? bc.round(value, decimal) : null;
  459. return value ? new Decimal(value).toDecimalPlaces(decimal).toNumber() : null;
  460. },
  461. /**
  462. * 汇总
  463. * @param array
  464. * @returns {number}
  465. */
  466. sum(array) {
  467. let result = 0;
  468. for (const a of array) {
  469. result = this.add(result, a);
  470. }
  471. return result;
  472. },
  473. /**
  474. * 汇总
  475. * @param array
  476. * @returns {number}
  477. */
  478. sumField(array, field) {
  479. let result = 0;
  480. for (const a of array) {
  481. result = this.add(result, a[field]);
  482. }
  483. return result;
  484. },
  485. };