ledger.js 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. 'use strict';
  2. /**
  3. * 标段--台账 数据模型
  4. *
  5. * @author CaiAoLin
  6. * @date 2017/12/1
  7. * @version
  8. */
  9. const needField = {
  10. id: 'ledger_id',
  11. pid: 'ledger_pid',
  12. order: 'order',
  13. level: 'level',
  14. fullPath: 'full_path',
  15. isLeaf: 'is_leaf',
  16. };
  17. const keyFields = {
  18. table: ['id'],
  19. index: ['tender_id', 'ledger_id'],
  20. };
  21. // 以下字段仅可通过树结构操作改变,不可直接通过update方式从接口提交,发现时过滤
  22. const readOnlyFields = ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'order', 'level', 'full_path', 'is_leaf'];
  23. const calcFields = ['quantity', 'unit_price', 'total_price'];
  24. const zeroRange = 0.0000000001;
  25. module.exports = app => {
  26. class Ledger extends app.BaseService {
  27. /**
  28. * 构造函数
  29. *
  30. * @param {Object} ctx - egg全局变量
  31. * @return {void}
  32. */
  33. constructor(ctx) {
  34. super(ctx);
  35. this.tableName = 'ledger';
  36. }
  37. /**
  38. * 新增数据
  39. *
  40. * @param {Object} data - 新增的数据(可批量)
  41. * @param {Number} tenderId - 标段id
  42. * @return {Boolean} - 返回新增的结果
  43. */
  44. async add(data, tenderId) {
  45. this.transaction = await this.db.beginTransaction();
  46. let result = false;
  47. try {
  48. if (tenderId <= 0) {
  49. throw '标段id错误';
  50. }
  51. if (data instanceof Array) {
  52. // 数组则为批量插入
  53. if (data.length <= 0) {
  54. throw '插入数据为空';
  55. }
  56. // 整理数据
  57. const insertData = [];
  58. for (const tmp of data) {
  59. tmp.ledger_id = tmp.id;
  60. tmp.ledger_pid = tmp.pid;
  61. tmp.tender_id = tenderId;
  62. delete tmp.id;
  63. delete tmp.pid;
  64. insertData.push(tmp);
  65. }
  66. const operate = await this.transaction.insert(this.tableName, insertData);
  67. this.transaction.commit();
  68. result = operate.affectedRows > 0;
  69. } else {
  70. // 对象则单个插入
  71. }
  72. } catch (error) {
  73. result = false;
  74. }
  75. return result;
  76. }
  77. /**
  78. * 根据层级获取数据
  79. *
  80. * @param {Number} tenderId - 标段id
  81. * @param {Number} showLevel - 显示层数
  82. * @return {Array} - 返回数据
  83. */
  84. async getDataByTenderId(tenderId, showLevel = 4) {
  85. if (tenderId <= 0) {
  86. return [];
  87. }
  88. this.initSqlBuilder();
  89. this.sqlBuilder.setAndWhere('tender_id', {
  90. value: tenderId,
  91. operate: '=',
  92. });
  93. if (showLevel > 0) {
  94. this.sqlBuilder.setAndWhere('level', {
  95. value: showLevel,
  96. operate: '<=',
  97. });
  98. }
  99. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  100. const data = await this.db.query(sql, sqlParam);
  101. return data;
  102. }
  103. /**
  104. * 根据节点Id获取数据
  105. *
  106. * @param {Number} tenderId - 标段id
  107. * @param {Number} nodeId - 项目节/工程量清单节点id
  108. * @return {Object} - 返回查询到的节点数据
  109. */
  110. async getDataByNodeId(tenderId, nodeId) {
  111. if ((nodeId <= 0) || (tenderId <= 0)) {
  112. return undefined;
  113. }
  114. this.initSqlBuilder();
  115. this.sqlBuilder.setAndWhere('tender_id', {
  116. value: tenderId,
  117. operate: '=',
  118. });
  119. this.sqlBuilder.setAndWhere('ledger_id', {
  120. value: nodeId,
  121. operate: '=',
  122. });
  123. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  124. const data = await this.db.queryOne(sql, sqlParam);
  125. return data;
  126. }
  127. /**
  128. * 根据节点Id获取数据
  129. * @param {Number} tenderId - 标段Id
  130. * @param {Array} nodesIds - 节点Id
  131. * @return {Array}
  132. */
  133. async getDataByNodeIds(tenderId, nodesIds) {
  134. if (tenderId <= 0) {
  135. return [];
  136. }
  137. this.initSqlBuilder();
  138. this.sqlBuilder.setAndWhere('tender_id', {
  139. value: tenderId,
  140. operate: '=',
  141. });
  142. this.sqlBuilder.setAndWhere('ledger_id', {
  143. value: nodesIds,
  144. operate: 'in',
  145. });
  146. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  147. const data = await this.db.query(sql, sqlParam);
  148. return data;
  149. }
  150. /**
  151. * 根据主键id获取数据
  152. * @param {Array|Number} id - 主键id
  153. * @returns {Promise<*>}
  154. */
  155. async getDataByIds(id) {
  156. const ids = id instanceof Array ? id : [id];
  157. this.initSqlBuilder();
  158. this.sqlBuilder.setAndWhere('id', {
  159. value: ids,
  160. operate: 'in',
  161. });
  162. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  163. const data = await this.db.query(sql, sqlParam);
  164. return data;
  165. }
  166. /**
  167. * 获取最末的子节点
  168. * @param {Number} tenderId - 标段id
  169. * @param {Number} pid - 父节点id
  170. * @return {Object}
  171. */
  172. async getLastChildData(tenderId, pid) {
  173. this.initSqlBuilder();
  174. this.sqlBuilder.setAndWhere('tender_id', {
  175. value: tenderId,
  176. operate: '=',
  177. });
  178. this.sqlBuilder.setAndWhere('ledger_pid', {
  179. value: pid,
  180. operate: '=',
  181. });
  182. this.sqlBuilder.orderBy = [['order', 'DESC']];
  183. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  184. const resultData = this.db.queryOne(sql, sqlParam);
  185. return resultData;
  186. }
  187. /**
  188. * 根据 父节点id 和 节点排序order 获取数据
  189. *
  190. * @param {Number} tenderId - 标段id
  191. * @param {Number} pid - 父节点id
  192. * @param {Number|Array} order - 排序
  193. * @return {Object|Array} - 查询结果
  194. */
  195. async getDataByParentAndOrder(tenderId, pid, order) {
  196. if ((tenderId <= 0) || (pid <= 0) || (order <= 0)) {
  197. return undefined;
  198. }
  199. this.initSqlBuilder();
  200. this.sqlBuilder.setAndWhere('tender_id', {
  201. value: tenderId,
  202. operate: '=',
  203. });
  204. this.sqlBuilder.setAndWhere('ledger_pid', {
  205. value: pid,
  206. operate: '=',
  207. });
  208. if (order instanceof Array) {
  209. this.sqlBuilder.setAndWhere('order', {
  210. value: order,
  211. operate: 'in',
  212. });
  213. } else {
  214. this.sqlBuilder.setAndWhere('order', {
  215. value: order,
  216. operate: '=',
  217. });
  218. }
  219. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  220. let data;
  221. if (order instanceof Array) {
  222. data = await this.db.query(sql, sqlParam);
  223. } else {
  224. data = await this.db.queryOne(sql, sqlParam);
  225. }
  226. return data;
  227. }
  228. /**
  229. * 根据 父节点id 获取子节点
  230. * @param tenderId
  231. * @param nodeId
  232. * @return {Promise<*>}
  233. */
  234. async getChildrenByParentId(tenderId, nodeId) {
  235. if ((nodeId <= 0) || (tenderId <= 0)) {
  236. return undefined;
  237. }
  238. this.initSqlBuilder();
  239. this.sqlBuilder.setAndWhere('tender_id', {
  240. value: tenderId,
  241. operate: '=',
  242. });
  243. this.sqlBuilder.setAndWhere('ledger_pid', {
  244. value: nodeId,
  245. operate: '=',
  246. });
  247. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  248. const data = await this.db.query(sql, sqlParam);
  249. return data;
  250. }
  251. /**
  252. * 根据 父节点ID 和 节点排序order 获取全部后节点数据
  253. * @param {Number} tenderId - 标段id
  254. * @param {Number} pid - 父节点id
  255. * @param {Number} order - 排序
  256. * @return {Array}
  257. */
  258. async getNextsData(tenderId, pid, order) {
  259. if ((tenderId <= 0) || (pid <= 0) || (order < 0)) {
  260. return undefined;
  261. }
  262. this.initSqlBuilder();
  263. this.sqlBuilder.setAndWhere('tender_id', {
  264. value: tenderId,
  265. operate: '=',
  266. });
  267. this.sqlBuilder.setAndWhere('ledger_pid', {
  268. value: pid,
  269. operate: '=',
  270. });
  271. this.sqlBuilder.setAndWhere('order', {
  272. value: order,
  273. operate: '>',
  274. });
  275. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  276. const data = await this.db.query(sql, sqlParam);
  277. return data;
  278. }
  279. /**
  280. * 根据full_path获取数据 full_path Like ‘1.2.3%’(传参full_path = '1.2.3%')
  281. * @param {Number} tenderId - 标段id
  282. * @param {String} full_path - 路径
  283. * @return {Promise<void>}
  284. */
  285. async getDataByFullPath(tenderId, full_path) {
  286. this.initSqlBuilder();
  287. this.sqlBuilder.setAndWhere('tender_id', {
  288. value: tenderId,
  289. operate: '=',
  290. });
  291. this.sqlBuilder.setAndWhere('full_path', {
  292. value: this.db.escape(full_path),
  293. operate: 'Like',
  294. });
  295. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  296. const resultData = await this.db.query(sql, sqlParam);
  297. return resultData;
  298. }
  299. /**
  300. * 根据full_path检索自己及所有父项
  301. * @param {Number} tenderId - 标段id
  302. * @param {Array|String} fullPath - 节点完整路径
  303. * @returns {Promise<*>}
  304. * @private
  305. */
  306. async getFullLevelDataByFullPath(tenderId, fullPath) {
  307. const explodePath = this.ctx.helper.explodePath(fullPath);
  308. this.initSqlBuilder();
  309. this.sqlBuilder.setAndWhere('tender_id', {
  310. value: tenderId,
  311. operate: '=',
  312. });
  313. this.sqlBuilder.setAndWhere('full_path', {
  314. value: explodePath,
  315. operate: 'in'
  316. });
  317. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  318. const data = await this.db.query(sql, sqlParam);
  319. return data;
  320. };
  321. /**
  322. * 统计子节点total_price
  323. * @param {Number} tenderId - 标段id
  324. * @param {Number} pid - 父节点id
  325. * @param {Number} order - order取值
  326. * @param {String} orderOperate - order比较操作符
  327. * @returns {Promise<void>}
  328. */
  329. async addUpChildren(tenderId, pid, order, orderOperate) {
  330. this.initSqlBuilder();
  331. const sql = ['SELECT SUM(??) As value FROM ?? ', ' WHERE ']
  332. const sqlParam = ['total_price', this.tableName];
  333. sql.push(' ?? = ' + tenderId);
  334. sqlParam.push('tender_id');
  335. sql.push(' And ?? = ' + pid);
  336. sqlParam.push('ledger_pid');
  337. sql.push(' And ?? ' + orderOperate + ' ' + order);
  338. sqlParam.push('order');
  339. const result = await this.db.queryOne(sql.join(''), sqlParam);
  340. return result.value;
  341. }
  342. /**
  343. * select的全部后兄弟节点,Order自增
  344. *
  345. * @param {Object} select - 选中的节点
  346. * @param {Number} incre - 自增值
  347. * @return {Array} - 自增后的数据
  348. * @private
  349. */
  350. async _updateSelectNextsOrder(select, incre = 1) {
  351. this.initSqlBuilder();
  352. this.sqlBuilder.setAndWhere('tender_id', {
  353. value: select.tender_id,
  354. operate: '=',
  355. });
  356. this.sqlBuilder.setAndWhere('order', {
  357. value: select.order + 1,
  358. operate: '>=',
  359. });
  360. this.sqlBuilder.setAndWhere('ledger_pid', {
  361. value: select.ledger_pid,
  362. operate: '=',
  363. });
  364. this.sqlBuilder.setUpdateData('order', {
  365. value: Math.abs(incre),
  366. selfOperate: incre > 0 ? '+' : '-',
  367. });
  368. // sql = update this.tableName set order = order + 1 where (tender_id = select.tender_id) && (pid = select.pid) && (order >= select.order+1)
  369. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  370. const data = await this.transaction.query(sql, sqlParam);
  371. return data;
  372. }
  373. /**
  374. * 从数据库获取标段的最大节点id
  375. *
  376. * @param {Number} tenderId - 标段id
  377. * @return {Number}
  378. * @private
  379. */
  380. async _getMaxNodeId(tenderId) {
  381. const sql = 'SELECT Max(??) As max_id FROM ?? Where tender_id = ' + tenderId;
  382. const sqlParam = ['ledger_id', this.tableName];
  383. const queryResult = await this.db.queryOne(sql, sqlParam);
  384. return queryResult.max_id;
  385. }
  386. /**
  387. * 根据selectData, data 新增数据
  388. *
  389. * @param {Number} tenderId - 标段id
  390. * @param {Object} selectData - 选中节点的数据
  391. * @param {Object} data - 新增节点的初始数据
  392. * @return {Object} - 新增结果
  393. * @private
  394. */
  395. async _addNodeData(tenderId, selectData, data) {
  396. if (tenderId <= 0) {
  397. return undefined;
  398. }
  399. if (!data) {
  400. data = {};
  401. }
  402. const cacheKey = 'tender_node_maxId:' + tenderId;
  403. let maxId = parseInt(await this.cache.get(cacheKey));
  404. if (!maxId) {
  405. maxId = await this._getMaxNodeId(tenderId);
  406. this.cache.set(cacheKey, maxId, 'EX', this.ctx.app.config.cacheTime);
  407. }
  408. data.tender_id = tenderId;
  409. data.ledger_id = maxId + 1;
  410. data.ledger_pid = selectData.ledger_pid;
  411. data.level = selectData.level;
  412. data.order = selectData.order + 1;
  413. data.full_path = selectData.full_path.replace(selectData.ledger_id, data.ledger_id);
  414. data.is_leaf = true;
  415. const result = await this.transaction.insert(this.tableName, data);
  416. this.cache.set(cacheKey, maxId + 1, 'EX', this.ctx.app.config.cacheTime);
  417. return result;
  418. }
  419. /**
  420. * tenderId标段中, 在selectId后新增一个节点
  421. *
  422. * @param {Number} tenderId - 标段id
  423. * @param {Number} selectId - 选中节点id
  424. * @param {Object} data - 新增节点初始化数据
  425. * @return {Array} 新增后的数据,其他被修改的数据
  426. */
  427. async addNode(tenderId, selectId, data) {
  428. if ((tenderId <= 0) || (selectId <= 0)) {
  429. return [];
  430. }
  431. const selectData = await this.getDataByNodeId(tenderId, selectId);
  432. if (!selectData) {
  433. throw '新增节点数据错误';
  434. }
  435. this.transaction = await this.db.beginTransaction();
  436. try {
  437. // 选中节点的所有后兄弟节点,order+1
  438. await this._updateSelectNextsOrder(selectData);
  439. // 数据库创建新增节点数据
  440. await this._addNodeData(tenderId, selectData, data);
  441. await this.transaction.commit();
  442. } catch (err) {
  443. await this.transaction.rollback();
  444. throw err;
  445. }
  446. // 查询应返回的结果
  447. const createData = await this.getDataByParentAndOrder(selectData.tender_id, selectData.ledger_pid, [selectData.order + 1]);
  448. const updateData = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order + 1);
  449. return { create: createData, update: updateData };
  450. }
  451. async _deleteNodeData(tenderId, deleteData) {
  452. this.initSqlBuilder();
  453. this.sqlBuilder.setAndWhere('tender_id', {
  454. value: tenderId,
  455. operate: '=',
  456. });
  457. this.sqlBuilder.setAndWhere('full_path', {
  458. value: this.db.escape(deleteData.full_path + '%'),
  459. operate: 'Like',
  460. });
  461. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'delete');
  462. const result = await this.transaction.query(sql, sqlParam);
  463. return result;
  464. }
  465. /**
  466. * tenderId标段中, 删除选中节点及其子节点
  467. *
  468. * @param {Number} tenderId - 标段id
  469. * @param {Number} selectId - 选中节点id
  470. * @return {Array} - 被删除的数据
  471. */
  472. async deleteNode(tenderId, selectId) {
  473. if ((tenderId <= 0) || (selectId <= 0)) {
  474. return [];
  475. }
  476. const selectData = await this.getDataByNodeId(tenderId, selectId);
  477. if (!selectData) {
  478. throw '删除节点数据错误';
  479. }
  480. const parentData = await this.getDataByNodeId(tenderId, selectData.ledger_pid);
  481. this.transaction = await this.db.beginTransaction();
  482. let deleteData = [];
  483. try {
  484. // 获取将要被删除的数据
  485. deleteData = await this.getDataByFullPath(tenderId, selectData.full_path + '%');
  486. // 删除
  487. const operate = await this._deleteNodeData(tenderId, selectData);
  488. // 选中节点--父节点 只有一个子节点时,应升级is_leaf
  489. if (parentData) {
  490. const count = this.db.count(this.tableName, { ledger_pid: selectData.ledger_pid });
  491. if (count === 1) {
  492. await this.transaction.update({
  493. id: parentData.id,
  494. is_leaf: true,
  495. });
  496. }
  497. }
  498. // 选中节点--全部后节点 order--
  499. await this._updateSelectNextsOrder(selectData, -1);
  500. // 更新父项金额
  501. if (selectData.total_price && Math.abs(selectData.total_price) > zeroRange) {
  502. const parentFullPath = selectData.full_path.replace('.' + selectData.ledger_id, '');
  503. const updateMap = {};
  504. updateMap[parentFullPath] = -selectData.total_price;
  505. await this._increCalcParent(tenderId, updateMap);
  506. }
  507. await this.transaction.commit();
  508. } catch (err) {
  509. deleteData = [];
  510. await this.transaction.rollback();
  511. throw err;
  512. }
  513. // 查询结果
  514. let updateData = [];
  515. if (deleteData.length > 0) {
  516. updateData = await this.getNextsData(tenderId, selectData.ledger_pid, selectData.order - 1);
  517. updateData = updateData ? updateData : [];
  518. const updateData1 = await this.getDataByNodeId(tenderId, selectData.ledger_pid);
  519. if (selectData.total_price && Math.abs(selectData.total_price) > zeroRange) {
  520. const updateData2 = await this.getFullLevelDataByFullPath(tenderId, updateData1.full_path);
  521. updateData = updateData.concat(updateData2);
  522. } else if (updateData1.is_leaf !== parentData.is_leaf) {
  523. updateData.push(updateData1);
  524. }
  525. }
  526. return { delete: deleteData, update: updateData };
  527. }
  528. /**
  529. * tenderId标段中, 选中节点selectId上移
  530. *
  531. * @param {Number} tenderId - 标段id
  532. * @param {Number} selectId - 选中节点id
  533. * @return {Array} - 发生改变的数据
  534. */
  535. async upMoveNode(tenderId, selectId) {
  536. if ((tenderId <= 0) || (selectId <= 0)) {
  537. return [];
  538. }
  539. const selectData = await this.getDataByNodeId(tenderId, selectId);
  540. if (!selectData) {
  541. throw '上移节点数据错误';
  542. }
  543. const preData = await this.getDataByParentAndOrder(tenderId, selectData.ledger_pid, selectData.order - 1);
  544. if (!preData) {
  545. throw '节点不可上移';
  546. }
  547. this.transaction = await this.db.beginTransaction();
  548. try {
  549. const sData = await this.transaction.update(this.tableName, { id: selectData.id, order: selectData.order - 1 });
  550. const pData = await this.transaction.update(this.tableName, { id: preData.id, order: preData.order + 1 });
  551. this.transaction.commit();
  552. } catch (err) {
  553. await this.transaction.rollback();
  554. throw err;
  555. }
  556. const resultData = await this.getDataByParentAndOrder(tenderId, selectData.ledger_pid, [selectData.order, preData.order]);
  557. return { update: resultData };
  558. }
  559. /**
  560. * tenderId标段中, 选中节点selectId下移
  561. *
  562. * @param {Number} tenderId - 标段id
  563. * @param {Number} selectId - 选中节点id
  564. * @return {Array} - 发生改变的数据
  565. */
  566. async downMoveNode(tenderId, selectId) {
  567. if ((tenderId <= 0) || (selectId <= 0)) {
  568. return [];
  569. }
  570. const selectData = await this.getDataByNodeId(tenderId, selectId);
  571. if (!selectData) {
  572. throw '下移节点数据错误';
  573. }
  574. const nextData = await this.getDataByParentAndOrder(tenderId, selectData.ledger_pid, selectData.order + 1);
  575. if (!nextData) {
  576. throw '节点不可下移';
  577. }
  578. this.transaction = await this.db.beginTransaction();
  579. try {
  580. const sData = await this.transaction.update(this.tableName, { id: selectData.id, order: selectData.order + 1 });
  581. const pData = await this.transaction.update(this.tableName, { id: nextData.id, order: nextData.order - 1 });
  582. this.transaction.commit();
  583. } catch (err) {
  584. await this.transaction.rollback();
  585. throw err;
  586. }
  587. const resultData = await this.getDataByParentAndOrder(tenderId, selectData.ledger_pid, [selectData.order, nextData.order]);
  588. return { update: resultData };
  589. }
  590. /**
  591. * 升级selectData, 同步修改所有子节点
  592. * @param {Object} selectData - 升级操作,选中节点
  593. * @return {Object}
  594. * @private
  595. */
  596. async _syncUplevelChildren(selectData) {
  597. this.initSqlBuilder();
  598. this.sqlBuilder.setAndWhere('tender_id', {
  599. value: selectData.tender_id,
  600. operate: '=',
  601. });
  602. this.sqlBuilder.setAndWhere('full_path', {
  603. value: this.db.escape(selectData.full_path + '.%'),
  604. operate: 'like',
  605. });
  606. this.sqlBuilder.setUpdateData('level', {
  607. value: 1,
  608. selfOperate: '-',
  609. });
  610. this.sqlBuilder.setUpdateData('full_path', {
  611. value: ['`full_path`', this.db.escape(selectData.ledger_pid + '.'), this.db.escape('')],
  612. literal: 'Replace',
  613. });
  614. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  615. const data = this.transaction.query(sql, sqlParam);
  616. return data;
  617. }
  618. /**
  619. * 选中节点的后兄弟节点,全部变为当前节点的子节点
  620. * @param {Object} selectData - 选中节点
  621. * @return {Object}
  622. * @private
  623. */
  624. async _syncUpLevelNexts(selectData) {
  625. // 查询selectData的lastChild
  626. const lastChildData = await this.getLastChildData(selectData.tender_id, selectData.ledger_id);
  627. const nextsData = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order);
  628. if (nextsData && nextsData.length > 0) {
  629. // 修改nextsData pid, 排序
  630. this.initSqlBuilder();
  631. this.sqlBuilder.setUpdateData('ledger_pid', {
  632. value: selectData.ledger_id,
  633. });
  634. const orderInc = lastChildData ? lastChildData.order - selectData.order : -selectData.order;
  635. this.sqlBuilder.setUpdateData('order', {
  636. value: Math.abs(orderInc),
  637. selfOperate: orderInc > 0 ? '+' : '-',
  638. });
  639. this.sqlBuilder.setAndWhere('ledger_pid', {
  640. value: selectData.ledger_pid,
  641. operate: '=',
  642. });
  643. this.sqlBuilder.setAndWhere('order', {
  644. value: selectData.order,
  645. operate: '>',
  646. });
  647. const [sql1, sqlParam1] = this.sqlBuilder.build(this.tableName, 'update');
  648. await this.transaction.query(sql1, sqlParam1);
  649. // 选中节点 is_leaf应为false
  650. if (selectData.is_leaf) {
  651. const updateData = { id: selectData.id,
  652. is_leaf: false,
  653. };
  654. await this.transaction.update(this.tableName, updateData);
  655. }
  656. // 修改nextsData及其子节点的full_path
  657. const oldSubStr = this.db.escape(selectData.ledger_pid + '.');
  658. const newSubStr = this.db.escape(selectData.ledger_id + '.');
  659. const sqlArr = [];
  660. sqlArr.push('Update ?? SET `full_path` = Replace(`full_path`,' + oldSubStr + ',' + newSubStr + ') Where');
  661. sqlArr.push('(`tender_id` = ' + selectData.tender_id + ')');
  662. sqlArr.push(' And (');
  663. for (const data of nextsData) {
  664. sqlArr.push('`full_path` Like ' + this.db.escape(data.full_path + '%'));
  665. if (nextsData.indexOf(data) < nextsData.length - 1) {
  666. sqlArr.push(' Or ');
  667. }
  668. }
  669. sqlArr.push(')');
  670. const sql = sqlArr.join('');
  671. const resultData = await this.transaction.query(sql, [this.tableName]);
  672. return resultData;
  673. }
  674. }
  675. /**
  676. * 升级节点
  677. *
  678. * @param {Number} tenderId - 标段id
  679. * @param {Number} selectId - 选中节点id
  680. * @return {Array} - 发生改变的数据
  681. */
  682. async upLevelNode(tenderId, selectId) {
  683. if ((tenderId <= 0) || (selectId <= 0)) {
  684. return [];
  685. }
  686. const selectData = await this.getDataByNodeId(tenderId, selectId);
  687. if (!selectData) {
  688. throw '升级节点数据错误';
  689. }
  690. const parentData = await this.getDataByNodeId(tenderId, selectData.ledger_pid);
  691. if (!parentData) {
  692. throw '升级节点数据错误';
  693. }
  694. this.transaction = await this.db.beginTransaction();
  695. const newFullPath = selectData.full_path.replace(selectData.ledger_pid + '.', '');
  696. try {
  697. // 选中节点--父节点 选中节点为firstChild时,修改is_leaf
  698. if (selectData.order === 1) {
  699. this.transaction.update(this.tableName, {
  700. id: parentData.id,
  701. is_leaf: true,
  702. total_price: 0
  703. });
  704. } else {
  705. this.transaction.update(this.tableName, {
  706. id: parentData.id,
  707. total_price: await this.addUpChildren(tenderId, selectData.ledger_pid, selectData.order, '<')
  708. });
  709. }
  710. // 选中节点--父节点--全部后兄弟节点 order+1
  711. await this._updateSelectNextsOrder(parentData);
  712. // 选中节点 修改pid, order, full_path
  713. let totalPrice = selectData.total_price ? selectData.total_price : 0;
  714. const plus = await this.addUpChildren(tenderId, selectData.ledger_pid, selectData.order, '>');
  715. totalPrice = plus ? totalPrice + plus : totalPrice;
  716. const updateData = { id: selectData.id,
  717. ledger_pid: parentData.ledger_pid,
  718. order: parentData.order + 1,
  719. level: selectData.level - 1,
  720. full_path: newFullPath,
  721. total_price: totalPrice
  722. };
  723. await this.transaction.update(this.tableName, updateData);
  724. // 选中节点--全部子节点(含孙) level-1, full_path变更
  725. await this._syncUplevelChildren(selectData);
  726. // 选中节点--全部后兄弟节点 收编为子节点 修改pid, order, full_path
  727. await this._syncUpLevelNexts(selectData);
  728. this.transaction.commit();
  729. } catch (err) {
  730. await this.transaction.rollback();
  731. throw err;
  732. }
  733. // 查询修改的数据
  734. const resultData1 = await this.getDataByFullPath(tenderId, newFullPath + '%');
  735. const resultData2 = await this.getNextsData(tenderId, parentData.ledger_pid, parentData.order + 1);
  736. // 默认原Parent被刷新过,不核对total_price修改
  737. const preParent = await this.getDataByNodeId(tenderId, parentData.ledger_id);
  738. resultData2.push(preParent);
  739. return { update: resultData1.concat(resultData2) };
  740. }
  741. /**
  742. * 降级selectData, 同步修改所有子节点
  743. * @param {Object} selectData - 选中节点
  744. * @param {Object} preData - 选中节点的前一节点(降级后为父节点)
  745. * @return {Promise<*>}
  746. * @private
  747. */
  748. async _syncDownlevelChildren(selectData, preData) {
  749. this.initSqlBuilder();
  750. this.sqlBuilder.setAndWhere('tender_id', {
  751. value: selectData.tender_id,
  752. operate: '=',
  753. });
  754. this.sqlBuilder.setAndWhere('full_path', {
  755. value: this.db.escape(selectData.full_path + '.%'),
  756. operate: 'like',
  757. });
  758. this.sqlBuilder.setUpdateData('level', {
  759. value: 1,
  760. selfOperate: '+',
  761. });
  762. this.sqlBuilder.setUpdateData('full_path', {
  763. value: ['`full_path`', this.db.escape('.' + selectData.ledger_id), this.db.escape('.' + preData.ledger_id + '.' + selectData.ledger_id)],
  764. literal: 'Replace',
  765. });
  766. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  767. const data = this.transaction.query(sql, sqlParam);
  768. return data;
  769. }
  770. /**
  771. * 降级节点
  772. *
  773. * @param {Number} tenderId - 标段id
  774. * @param {Number} selectId - 选中节点id
  775. * @return {Array} - 发生改变的数据
  776. */
  777. async downLevelNode(tenderId, selectId) {
  778. if ((tenderId <= 0) || (selectId <= 0)) {
  779. return [];
  780. }
  781. const selectData = await this.getDataByNodeId(tenderId, selectId);
  782. if (!selectData) {
  783. throw '降级节点数据错误';
  784. }
  785. const preData = await this.getDataByParentAndOrder(tenderId, selectData.ledger_pid, selectData.order - 1);
  786. if (!preData) {
  787. throw '节点不可降级';
  788. }
  789. const preLastChildData = await this.getLastChildData(tenderId, preData.ledger_id);
  790. this.transaction = await this.db.beginTransaction();
  791. const orgLastPath = selectData.level === 1 ? selectData.ledger_id : '.' + selectData.ledger_id;
  792. const newLastPath = selectData.level === 1 ? preData.ledger_id + '.' + selectData.ledger_id : '.' + preData.ledger_id + '.' + selectData.ledger_id;
  793. const newFullPath = selectData.full_path.replace(orgLastPath, newLastPath);
  794. try {
  795. // 选中节点--全部后节点 order--
  796. await this._updateSelectNextsOrder(selectData, -1);
  797. // 选中节点 修改pid, level, order, full_path
  798. const updateData = {
  799. id: selectData.id,
  800. ledger_pid: preData.ledger_id,
  801. order: preLastChildData ? preLastChildData.order + 1 : 1,
  802. level: selectData.level + 1,
  803. full_path: newFullPath,
  804. };
  805. await this.transaction.update(this.tableName, updateData);
  806. // 选中节点--全部子节点(含孙) level++, full_path
  807. await this._syncDownlevelChildren(selectData, preData);
  808. // 选中节点--前兄弟节点 is_leaf应为false
  809. if (preData.is_leaf || this.ctx.helper.checkZero(selectData.total_price)) {
  810. const updateData2 = {
  811. id: preData.id,
  812. is_leaf: false
  813. };
  814. if (this.ctx.helper.checkZero(selectData.total_price)) {
  815. updateData2['total_price'] = preData.total_price ? preData.total_price + selectData.total_price : selectData.total_price;
  816. }
  817. await this.transaction.update(this.tableName, updateData2);
  818. }
  819. this.transaction.commit();
  820. } catch (err) {
  821. this.transaction.rollback();
  822. throw err;
  823. }
  824. // 查询修改的数据
  825. // 选中节点及子节点
  826. const resultData1 = await this.getDataByFullPath(tenderId, newFullPath + '%');
  827. // 选中节点--原前兄弟节点&全部后兄弟节点
  828. const queryOrder = (preData.is_leaf || this.ctx.helper.checkZero(selectData.total_price)) ? preData.order - 1 : preData.order;
  829. const resultData2 = await this.getNextsData(tenderId, preData.ledger_pid, queryOrder);
  830. return { update: resultData1.concat(resultData2) };
  831. }
  832. /**
  833. * 过滤data中update方式不可提交的字段
  834. * @param {Number} id - 主键key
  835. * @param {Object} data
  836. * @return {Object<{id: *}>}
  837. * @private
  838. */
  839. _filterUpdateInvalidField(id, data) {
  840. const result = {
  841. id,
  842. };
  843. for (const prop in data) {
  844. if (readOnlyFields.indexOf(prop) === -1) {
  845. result[prop] = data[prop];
  846. }
  847. }
  848. return result;
  849. }
  850. /**
  851. * newData中,以orgData为基准,过滤掉orgData中未定义或值相等的部分
  852. * @param {Object} orgData
  853. * @param {Object} newData
  854. * @private
  855. */
  856. _filterChangedField(orgData, newData) {
  857. const result= {};
  858. let bChanged = false;
  859. for (const prop in orgData) {
  860. if (newData[prop] && newData[prop] !== orgData[prop]) {
  861. result[prop] = newData[prop];
  862. bChanged = true;
  863. }
  864. }
  865. return bChanged ? result : undefined;
  866. }
  867. /**
  868. * 检查data中是否含有计算字段
  869. * @param {Object} data
  870. * @returns {boolean}
  871. * @private
  872. */
  873. _checkCalcField(data) {
  874. for (const prop in data) {
  875. if (calcFields.indexOf(prop) >= 0) {
  876. return true;
  877. }
  878. }
  879. return false;
  880. }
  881. /**
  882. * 提交数据 - 不影响计算等未提交项
  883. * @param {Number} tenderId - 标段id
  884. * @param {Object} data - 提交数据
  885. * @return {Object} - 提交后的数据
  886. */
  887. async updateInfo(tenderId, data) {
  888. // 简单校验数据
  889. if (tenderId <= 0) {
  890. throw '标段不存在';
  891. }
  892. if (tenderId !== data.tender_id) {
  893. throw '提交数据错误';
  894. }
  895. try {
  896. // 过滤不可提交字段
  897. const updateNode = await this.getDataById(data.id);
  898. if (!updateNode || tenderId !== updateNode.tender_id || data.ledger_id !== updateNode.ledger_id) {
  899. throw '提交数据错误';
  900. }
  901. const updateData = this._filterUpdateInvalidField(updateNode.id, data);
  902. await this.db.update(this.tableName, updateData);
  903. } catch (err) {
  904. throw err;
  905. }
  906. const result = await this.getDataByNodeId(tenderId, data.ledger_id);
  907. return result;
  908. }
  909. /**
  910. * 提交多条数据 - 不影响计算等未提交项
  911. * @param {Number} tenderId - 标段id
  912. * @param {Array} datas - 提交数据
  913. * @return {Array} - 提交后的数据
  914. */
  915. async updateInfos(tenderId, datas) {
  916. if (tenderId <= 0) {
  917. throw '标段不存在';
  918. }
  919. for (const data of datas) {
  920. if (tenderId !== data.tender_id) {
  921. throw '提交数据错误';
  922. }
  923. }
  924. this.transaction = await this.db.beginTransaction();
  925. try {
  926. for (const data of datas) {
  927. const updateNode = await this.getDataById(data.id);
  928. if (!updateNode || tenderId !== updateNode.tender_id || data.ledger_id !== updateNode.ledger_id) {
  929. throw '提交数据错误';
  930. }
  931. const updateData = this._filterUpdateInvalidField(updateNode.id, data);
  932. await this.transaction.update(this.tableName, updateData);
  933. }
  934. this.transaction.commit();
  935. } catch (err) {
  936. this.transaction.rollback();
  937. throw err;
  938. }
  939. const filter = [];
  940. for (const data of datas) {
  941. filter.push(data.id);
  942. }
  943. this.initSqlBuilder();
  944. this.sqlBuilder.setAndWhere('id', {
  945. value: filter,
  946. operate: 'in',
  947. });
  948. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
  949. const resultData = await this.db.query(sql, sqlParam);
  950. return resultData;
  951. }
  952. /**
  953. * 复制粘贴整块
  954. * @param {Number} tenderId - 标段Id
  955. * @param {Number} selectId - 选中几点Id
  956. * @param {Array} block - 复制节点Id
  957. * @return {Object} - 提价后的数据(其中新增粘贴数据,只返回第一层)
  958. */
  959. async pasteBlock(tenderId, selectId, block) {
  960. if ((tenderId <= 0) || (selectId <= 0)) {
  961. return [];
  962. }
  963. const selectData = await this.getDataByNodeId(tenderId, selectId);
  964. if (!selectData) {
  965. throw '位置数据错误';
  966. }
  967. const newParentPath = selectData.full_path.replace(selectData.ledger_id, '');
  968. const copyNodes = await this.getDataByNodeIds(tenderId, block);
  969. if (!copyNodes || copyNodes.length <= 0) {
  970. throw '复制数据错误';
  971. }
  972. let bSameParent = true;
  973. for (const node of copyNodes) {
  974. if (node.ledger_pid !== copyNodes[0].ledger_pid) {
  975. bSameParent = false;
  976. break;
  977. }
  978. }
  979. if (!bSameParent) {
  980. throw '复制数据错误:仅可操作同层节点';
  981. }
  982. const orgParentPath = copyNodes[0].full_path.replace(copyNodes[0].ledger_id, '');
  983. let incre = 0;
  984. this.transaction = await this.db.beginTransaction();
  985. try {
  986. // 选中节点的所有后兄弟节点,order+粘贴节点个数
  987. await this._updateSelectNextsOrder(selectData, copyNodes.length);
  988. // 数据库创建新增节点数据
  989. for (const node of copyNodes) {
  990. incre += node.total_price ? node.total_price : 0;
  991. const datas = await this.getDataByFullPath(tenderId, node.full_path + '%');
  992. const cacheKey = 'tender_node_maxId:' + tenderId;
  993. let maxId = parseInt(await this.cache.get(cacheKey));
  994. if (!maxId) {
  995. maxId = await this._getMaxNodeId(tenderId);
  996. }
  997. this.cache.set(cacheKey, maxId + datas.length, 'EX', this.ctx.app.config.cacheTime);
  998. // 计算粘贴数据中需更新部分
  999. for (let index = 0; index < datas.length; index++) {
  1000. const data = datas[index];
  1001. const newId = maxId + index + 1;
  1002. delete data.id;
  1003. if (!data.is_leaf) {
  1004. for (const children of datas) {
  1005. children.full_path = children.full_path.replace('.' + data.ledger_id, '.' + newId);
  1006. if (children.ledger_pid === data.ledger_id) {
  1007. children.ledger_pid = newId;
  1008. }
  1009. }
  1010. } else {
  1011. data.full_path = data.full_path.replace('.' + data.ledger_id, '.' + newId);
  1012. }
  1013. data.ledger_id = newId;
  1014. data.full_path = data.full_path.replace(orgParentPath, newParentPath);
  1015. if (data.ledger_pid === copyNodes[0].ledger_pid) {
  1016. data.ledger_pid = selectData.ledger_pid;
  1017. data.order = selectData.order + index + 1;
  1018. }
  1019. data.level = data.level + selectData.level - copyNodes[0].level;
  1020. }
  1021. // 插入粘贴数据
  1022. await this.transaction.insert(this.tableName, datas);
  1023. }
  1024. // 更新父节点金额
  1025. if (Math.abs(incre) > zeroRange) {
  1026. const updateMap = {};
  1027. updateMap[newParentPath] = incre;
  1028. await this._increCalcParent(tenderId, updateMap);
  1029. }
  1030. await this.transaction.commit();
  1031. } catch (err) {
  1032. await this.transaction.rollback();
  1033. throw err;
  1034. }
  1035. // 查询应返回的结果
  1036. const order = [];
  1037. for (let i = 1; i <= copyNodes.length; i++) {
  1038. order.push(selectData.order + i);
  1039. }
  1040. const createData = await this.getDataByParentAndOrder(selectData.tender_id, selectData.ledger_pid, order);
  1041. const updateData = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order + copyNodes.length);
  1042. if (Math.abs(incre) > zeroRange) {
  1043. const updateData1 = await this.getFullLevelDataByFullPath(selectData.tender_id, newParentPath);
  1044. return { create: createData, update: updateData.concat(updateData1) };
  1045. } else {
  1046. return { create: createData, update: updateData };
  1047. }
  1048. }
  1049. /**
  1050. * 增量更新父项金额
  1051. * @param {Number} tenderId - 标段id
  1052. * @param {Object} updateMap - 增量更新数,使用更新父项的full_path为索引
  1053. * e.g: {'1.2.6.8': 30, '1.2.6.10': 40}表示'1.2.6.8'增量30,'1.2.6.10'增量40(此处同步更新'1.2.6', '1.2', '1')
  1054. * @returns {Promise<void>}
  1055. * @private
  1056. */
  1057. async _increCalcParent(tenderId, updateMap) {
  1058. for (const prop in updateMap) {
  1059. this.initSqlBuilder();
  1060. this.sqlBuilder.setAndWhere('tender_id', {
  1061. value: tenderId,
  1062. operate: '='
  1063. });
  1064. const fullPath = this.ctx.helper.explodePath(prop);
  1065. this.sqlBuilder.setAndWhere('full_path', {
  1066. value: this.ctx.helper.explodePath(prop),
  1067. operate: 'in'
  1068. });
  1069. this.sqlBuilder.setUpdateData('total_price', {
  1070. value: updateMap[prop] > 0 ? updateMap[prop] : -updateMap[prop],
  1071. selfOperate: updateMap[prop] > 0 ? '+' : '-'
  1072. });
  1073. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  1074. await this.transaction.query(sql, sqlParam);
  1075. }
  1076. }
  1077. async updateCalc(tenderId, data) {
  1078. const findData = function (id, datas) {
  1079. for (const d of datas) {
  1080. if (d.id === id) {
  1081. return d;
  1082. }
  1083. }
  1084. return undefined;
  1085. }
  1086. // 简单验证数据
  1087. if (tenderId <= 0) {
  1088. throw '标段不存在';
  1089. }
  1090. if (!data) {
  1091. throw '提交数据错误1';
  1092. }
  1093. const datas = data instanceof Array ? data : [data];
  1094. const ids = [];
  1095. for (const row of datas) {
  1096. if (tenderId !== row.tender_id) {
  1097. throw '提交数据错误2';
  1098. }
  1099. ids.push(row.id);
  1100. }
  1101. const updateMap = {}, updateFullPath = [];
  1102. this.transaction = await this.db.beginTransaction();
  1103. try {
  1104. for (const row of datas) {
  1105. const updateNode = await this.getDataById(row.id);
  1106. if (!updateNode || tenderId !== updateNode.tender_id || row.ledger_id !== updateNode.ledger_id) {
  1107. throw '提交数据错误3';
  1108. }
  1109. let updateData;
  1110. if (this._checkCalcField(row)) {
  1111. const calcData = this.ctx.helper.updateObj(updateNode, row);
  1112. if (updateNode.is_leaf) {
  1113. calcData.total_price = calcData.quantity * calcData.unit_price;
  1114. }
  1115. if (updateNode.total_price === undefined || Math.abs(calcData.total_price - updateNode.total_price) > zeroRange) {
  1116. const pfp = updateNode.full_path.replace('.' + updateNode.ledger_id, '');
  1117. if (updateMap[pfp]) {
  1118. updateMap[pfp] = updateMap[pfp] + calcData.total_price - updateNode.total_price;
  1119. } else {
  1120. updateMap[pfp] = calcData.total_price - updateNode.total_price;
  1121. updateFullPath.push(pfp);
  1122. }
  1123. }
  1124. const data1 = this._filterChangedField(updateNode, calcData);
  1125. updateData = this._filterUpdateInvalidField(updateNode.id, data1);
  1126. } else {
  1127. updateData = this._filterUpdateInvalidField(updateNode.id, row);
  1128. }
  1129. await this.transaction.update(this.tableName, updateData);
  1130. }
  1131. await this._increCalcParent(tenderId, updateMap);
  1132. this.transaction.commit();
  1133. } catch (err) {
  1134. this.transaction.rollback();
  1135. throw err;
  1136. }
  1137. const result1 = await this.getDataByIds(ids);
  1138. if (updateFullPath.length > 0) {
  1139. const result2 = await this.getFullLevelDataByFullPath(tenderId, updateFullPath);
  1140. return result1.concat(result2);
  1141. } else {
  1142. return result1;
  1143. }
  1144. }
  1145. }
  1146. return Ledger;
  1147. };