helper.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. isOperator(value) {
  203. const operatorString = "+-*/()";
  204. return operatorString.indexOf(value) > -1
  205. },
  206. parse2Rpn(exp) {
  207. const getPriority = function (value){
  208. switch(value){
  209. case '+':
  210. case '-':
  211. return 1;
  212. case '*':
  213. case '/':
  214. return 2;
  215. default:
  216. return 0;
  217. }
  218. };
  219. const priority = function (o1, o2){
  220. return getPriority(o1) <= getPriority(o2);
  221. };
  222. const inputStack = [];
  223. const outputStack = [];
  224. const outputQueue = [];
  225. for (let i = 0, len = exp.length; i < len; i++) {
  226. const cur = exp[i];
  227. if (cur !== ' ' ) {
  228. inputStack.push(cur);
  229. }
  230. }
  231. let num = '', isNumPre = false, isOperatorPre = false;
  232. while(inputStack.length > 0){
  233. const cur = inputStack.shift();
  234. if (this.isOperator(cur) && !(cur === '-' && !isNumPre)) {
  235. if (isNumPre) {
  236. outputQueue.push(parseFloat(num));
  237. num = '';
  238. }
  239. if (cur === '(') {
  240. outputStack.push(cur);
  241. } else if (cur === ')') {
  242. let po = outputStack.pop();
  243. while (po !== '(' && outputStack.length > 0) {
  244. outputQueue.push(po);
  245. po = outputStack.pop();
  246. }
  247. if (po !== '(') {
  248. throw "error: unmatched ()";
  249. }
  250. } else {
  251. while(priority(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0){
  252. outputQueue.push(outputStack.pop());
  253. }
  254. outputStack.push(cur);
  255. }
  256. isNumPre = false;
  257. isOperatorPre = true;
  258. } else {
  259. num = num + cur;
  260. isNumPre = true;
  261. isOperatorPre = false;
  262. if (inputStack.length === 0) {
  263. outputQueue.push(parseFloat(num));
  264. }
  265. }
  266. }
  267. if (outputStack.length > 0) {
  268. if (outputStack[outputStack.length - 1] === ')' || outputStack[outputStack.length - 1] === '(') {
  269. throw "error: unmatched ()";
  270. }
  271. while (outputStack.length > 0) {
  272. outputQueue.push(outputStack.pop());
  273. }
  274. }
  275. return outputQueue;
  276. },
  277. evalRpn(rpnQueue) {
  278. const getResult = function (num1, num2, opera) {
  279. switch (opera) {
  280. case '+':
  281. return num1 + num2;
  282. case '-':
  283. return num1 - num2;
  284. case '*':
  285. return num1 * num2;
  286. case '/':
  287. return num1 / num2;
  288. default:
  289. throw '参数错误';
  290. }
  291. };
  292. const outputStack = [];
  293. while(rpnQueue.length > 0){
  294. const cur = rpnQueue.shift();
  295. if (!this.isOperator(cur)) {
  296. outputStack.push(cur);
  297. } else {
  298. if (outputStack.length < 2) {
  299. throw "unvalid stack length";
  300. }
  301. const sec = outputStack.pop();
  302. const fir = outputStack.pop();
  303. outputStack.push(getResult(fir, sec, cur));
  304. }
  305. }
  306. if (outputStack.length !== 1) {
  307. throw "unvalid expression";
  308. } else {
  309. return outputStack[0];
  310. }
  311. },
  312. /**
  313. * 解析四则运算字符串并计算
  314. * @param {String} expr
  315. * @returns
  316. */
  317. calcExprStrRpn(expr) {
  318. try {
  319. if (!isNaN(Number(expr))) {
  320. return Number(expr);
  321. } else {
  322. const rpnArr = this.parse2Rpn(expr);
  323. const result = this.evalRpn(rpnArr);
  324. return result === Infinity ? NaN : result;
  325. }
  326. } catch (err) {
  327. return NaN;
  328. }
  329. },
  330. calcExprStr(expr) {
  331. try {
  332. const result = mathjs.eval(expr);
  333. return result === Infinity ? NaN : result;
  334. } catch (err) {
  335. return NaN;
  336. }
  337. }
  338. };