facades.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Zhong
  6. * @date 2018/6/1
  7. * @version
  8. */
  9. import mongoose from 'mongoose';
  10. import CompilationModel from "../../users/models/compilation_model";
  11. import moment from 'moment';
  12. const uuidV1 = require('uuid/v1');
  13. const billsLibModel = mongoose.model('std_bills_lib_list');
  14. const billsGuideLibModel = mongoose.model('std_billsGuidance_lib');
  15. const billsGuideItemsModel = mongoose.model('std_billsGuidance_items');
  16. const stdBillsLibModel = mongoose.model('std_bills_lib_list');
  17. const stdBillsModel = mongoose.model('std_bills_lib_bills');
  18. const stdBillsJobsModel = mongoose.model('std_bills_lib_jobContent');
  19. const stdRationModel = mongoose.model('std_ration_lib_ration_items');
  20. const engLibModel = mongoose.model('engineering_lib');
  21. const compilationModel = mongoose.model('compilation');
  22. const _ = require('lodash');
  23. module.exports = {
  24. handleCopyItems,
  25. getComBillsLibInfo,
  26. getBillsGuideLibs,
  27. initBillsGuideLib,
  28. updateBillsGuideLib,
  29. getLibWithBills,
  30. getItemsBybills,
  31. updateItems,
  32. testItems
  33. };
  34. function setChildren(bill, parentMap) {
  35. let children = parentMap[bill.ID];
  36. if (children) {
  37. for (let c of children) {
  38. setChildren(c, parentMap);
  39. }
  40. bill.children = children;
  41. } else {
  42. bill.children = [];
  43. }
  44. }
  45. function sortChildren(lists) {
  46. let IDMap = {},
  47. nextMap = {},
  48. firstNode = null,
  49. newList = [];
  50. for (let l of lists) {
  51. if (l.children && l.children.length > 0) l.children = sortChildren(l.children); //递规排序
  52. IDMap[l.ID] = l;
  53. if (l.NextSiblingID != -1) nextMap[l.NextSiblingID] = l;
  54. }
  55. for (let t of lists) {
  56. if (!nextMap[t.ID]) { //如果在下一节点映射没找到,则是第一个节点
  57. firstNode = t;
  58. break;
  59. }
  60. }
  61. if (firstNode) {
  62. newList.push(firstNode);
  63. delete IDMap[firstNode.ID];
  64. setNext(firstNode, newList);
  65. }
  66. //容错处理,如果链断了的情况,直接添加到后面
  67. for (let key in IDMap) {
  68. if (IDMap[key]) newList.push(IDMap[key])
  69. }
  70. return newList;
  71. function setNext(node, array) {
  72. if (node.NextSiblingID != -1) {
  73. let next = IDMap[node.NextSiblingID];
  74. if (next) {
  75. array.push(next);
  76. delete IDMap[next.ID];
  77. setNext(next, array);
  78. }
  79. }
  80. }
  81. }
  82. function getItemFromChildren(children, allItems) {
  83. for (let i = 0; i < children.length; i++) {
  84. const cur = children[i];
  85. const next = children[i + 1];
  86. cur.NextSiblingID = next && next.ID || -1;
  87. allItems.push(cur);
  88. if (cur.children && cur.children.length) {
  89. getItemFromChildren(cur.children, allItems);
  90. }
  91. }
  92. }
  93. function resetTreeData(billsList) {
  94. const idMapping = {};
  95. idMapping['-1'] = -1;
  96. // 建立新ID-旧ID映射
  97. billsList.forEach(bills => idMapping[bills.ID] = uuidV1());
  98. billsList.forEach(function (bills) {
  99. bills.ID = idMapping[bills.ID] ? idMapping[bills.ID] : -1;
  100. bills.ParentID = idMapping[bills.ParentID] ? idMapping[bills.ParentID] : -1;
  101. bills.NextSiblingID = idMapping[bills.NextSiblingID] ? idMapping[bills.NextSiblingID] : -1;
  102. });
  103. }
  104. // 从某库中根据清单编号拷贝工艺
  105. async function handleCopyItems(sourceGuideLibID, sourceBillsLibID, targetGuideLibID, targetBillsLibID) {
  106. const sourceBills = await stdBillsModel.find({ billsLibId: sourceBillsLibID, deleted: false }, '-_id ID code').lean();
  107. const targetBills = await stdBillsModel.find({ billsLibId: targetBillsLibID, deleted: false }, '-_id ID code').lean();
  108. const sourceItems = await billsGuideItemsModel.find({ libID: sourceGuideLibID, type: 0, deleted: false }, '-_id').lean(); // 过滤掉定额项
  109. const targetCodeIDMap = {};
  110. targetBills.forEach(bills => targetCodeIDMap[bills.code] = bills.ID);
  111. const toDeleteIDList = [];
  112. const insertBulks = [];
  113. sourceBills.forEach(bills => {
  114. const matchedItems = sourceItems.filter(item => item.billsID === bills.ID);
  115. // 重新将断缺的树结构数据整理好
  116. if (matchedItems.length) {
  117. const parentMap = {};
  118. matchedItems.forEach(item => {
  119. (parentMap[item.ParentID] || (parentMap[item.ParentID] = [])).push(item);
  120. });
  121. const roots = parentMap['-1'];
  122. roots.forEach(root => setChildren(root, parentMap));
  123. const sorted = sortChildren(roots);
  124. const newItems = [];
  125. getItemFromChildren(sorted, newItems);
  126. resetTreeData(newItems);
  127. const targetBillsID = targetCodeIDMap[bills.code];
  128. if (targetBillsID) {
  129. toDeleteIDList.push(targetBillsID);
  130. newItems.forEach(newItem => {
  131. newItem.libID = targetGuideLibID;
  132. newItem.billsID = targetBillsID;
  133. insertBulks.push({
  134. insertOne: { document: newItem }
  135. });
  136. })
  137. }
  138. }
  139. });
  140. if (toDeleteIDList.length) {
  141. await billsGuideItemsModel.remove({ libID: targetGuideLibID, billsID: { $in : toDeleteIDList } });
  142. }
  143. if (insertBulks.length) {
  144. await billsGuideItemsModel.bulkWrite(insertBulks);
  145. }
  146. }
  147. async function getCompilationList() {
  148. let compilationModel = new CompilationModel();
  149. return await compilationModel.getCompilationList();
  150. }
  151. async function getComBillsLibInfo() {
  152. let rst = {compilationList: [], billsLibs: []};
  153. let compilationList = await getCompilationList();
  154. if(compilationList.length <= 0){
  155. throw '没有数据';
  156. }
  157. else{
  158. for(let compilation of compilationList){
  159. rst.compilationList.push({_id: compilation._id, name: compilation.name});
  160. }
  161. rst.billsLibs = await billsLibModel.find({deleted: false}, '-_id billsLibId billsLibName');
  162. return rst;
  163. }
  164. }
  165. async function getBillsGuideLibs(findData) {
  166. return await billsGuideLibModel.find(findData);
  167. }
  168. // 如果是“重庆费用定额(2018)”,则默认叶子清单的项目指引窗口只有一条数据,取清单名称。
  169. async function genGuidanceItems_cq18(guidanceLibId, billsLibId) {
  170. let bills = await stdBillsModel.find({billsLibId: billsLibId, deleted: false, 'jobs.0': {$exists: true}});
  171. let insertArr = [];
  172. for(let bill of bills){
  173. let newItem = {
  174. libID: guidanceLibId,
  175. ID: uuidV1(), ParentID: -1,
  176. NextSiblingID: -1,
  177. name: bill.name,
  178. type: 0,
  179. billsID: bill.ID
  180. };
  181. insertArr.push({insertOne: {document: newItem}});
  182. }
  183. await billsGuideItemsModel.bulkWrite(insertArr);
  184. }
  185. //拷贝工作内容并转化为树结构,形成项目指引数据
  186. async function genGuidanceItems(guidanceLibId, billsLibId){
  187. let bills = await stdBillsModel.find({billsLibId: billsLibId, deleted: false, 'jobs.0': {$exists: true}});
  188. //设置工作内容数据
  189. let jobIds = [];
  190. let totalJobs = [];
  191. for(let bill of bills){
  192. for(let job of bill.jobs){
  193. jobIds.push(job.id);
  194. }
  195. }
  196. jobIds = Array.from(new Set(jobIds));
  197. if(jobIds.length > 0){
  198. totalJobs = await stdBillsJobsModel.find({deleted: false, id: {$in: jobIds}});
  199. }
  200. if(totalJobs.length > 0){
  201. let jobIdIndex = {};//id索引
  202. for(let job of totalJobs){
  203. jobIdIndex[job.id] = job;
  204. }
  205. let insertArr = [];
  206. for(let bill of bills){
  207. //排序后根据serialNo转换成NextSiblingID,倒序
  208. bill.jobs.sort(function (a, b) {
  209. let rst = 0;
  210. if(a.serialNo > b.serialNo){
  211. rst = -1;
  212. }
  213. else if(a.serialNo < b.serialNo){
  214. rst = 1;
  215. }
  216. return rst;
  217. });
  218. let jobNoIndex = {};//下标索引
  219. for(let i = 0; i < bill.jobs.length; i++){
  220. let newItem = {libID: guidanceLibId, ID: uuidV1(), ParentID: -1, NextSiblingID: jobNoIndex[i - 1] ? jobNoIndex[i - 1]['ID'] : -1,
  221. name: jobIdIndex[bill.jobs[i]['id']]['content'], type: 0, billsID: bill.ID};
  222. jobNoIndex[i] = newItem;
  223. insertArr.push({insertOne: {document: newItem}});
  224. }
  225. }
  226. await billsGuideItemsModel.bulkWrite(insertArr);
  227. }
  228. }
  229. async function initBillsGuideLib(updateData){
  230. await billsGuideLibModel.create(updateData);
  231. let compilation = await compilationModel.findOne({_id: mongoose.Types.ObjectId(updateData.compilationId)});
  232. if (compilation &&
  233. compilation.overWriteUrl &&
  234. compilation.overWriteUrl === '/web/over_write/js/chongqing_2018.js') {
  235. await genGuidanceItems_cq18(updateData.ID, updateData.billsLibId);
  236. } else {
  237. await genGuidanceItems(updateData.ID, updateData.billsLibId);
  238. }
  239. }
  240. async function updateBillsGuideLib(data) {
  241. if(data.updateType === 'delete'){
  242. //删除所有条目
  243. await billsGuideLibModel.remove(data.findData);
  244. await billsGuideItemsModel.remove({libID: data.findData.ID});
  245. }
  246. else {
  247. await billsGuideLibModel.update(data.findData, {$set: data.updateData});
  248. await engLibModel.update({'billsGuidance_lib.id': data.findData.ID}, {$set: {'billsGuidance_lib.$.name': data.updateData.name}}, {multi: true});
  249. }
  250. }
  251. async function getLibWithBills(libID){
  252. let guidanceLib = await getBillsGuideLibs({ID: libID});
  253. if(guidanceLib.length === 0){
  254. throw '不存在此指引库!';
  255. }
  256. let billsLib = await stdBillsLibModel.findOne({billsLibId: guidanceLib[0].billsLibId});
  257. if(!billsLib){
  258. throw '引用的清单规则库不存在!';
  259. }
  260. let bills = await stdBillsModel.find({billsLibId: billsLib.billsLibId}, '-_id code name ID NextSiblingID ParentID jobs items comment');
  261. return {guidanceLib: guidanceLib[0], bills};
  262. }
  263. function getAttrs(field, datas){
  264. let rst = [];
  265. for(let data of datas){
  266. if(data[field]){
  267. rst.push(data[field]);
  268. }
  269. }
  270. return rst;
  271. }
  272. //定额项目指所引用定额是否被删除
  273. function rationAllExist(rationItems, stdRationIdx) {
  274. for(let item of rationItems){
  275. if(!stdRationIdx[item.rationID]){
  276. return false;
  277. }
  278. }
  279. return true;
  280. }
  281. //将同层树结构转为顺序数组
  282. function chainToArr(nodes){
  283. let rst = [];
  284. let tempIdx = {};
  285. let nodeIdx = {};
  286. //建索引
  287. for(let node of nodes){
  288. tempIdx[node.ID] = {ID: node.ID, NextSiblingID: node.NextSiblingID, preSibling: null, nextSibling: null};
  289. nodeIdx[node.ID] = node;
  290. }
  291. //建链
  292. for(let i in tempIdx){
  293. let temp = tempIdx[i];
  294. if(temp.NextSiblingID != -1){
  295. let next = tempIdx[temp.NextSiblingID];
  296. temp.nextSibling = next;
  297. next.preSibling = temp;
  298. }
  299. }
  300. let firstNode = null;
  301. for(let i in tempIdx){
  302. if(!tempIdx[i].preSibling){
  303. firstNode = tempIdx[i];
  304. break;
  305. }
  306. }
  307. //获得顺序队列
  308. while(firstNode){
  309. rst.push(nodeIdx[firstNode.ID]);
  310. firstNode = firstNode.nextSibling;
  311. }
  312. return rst;
  313. }
  314. async function getItemsBybills(guidanceLibID, billsID){
  315. const type = {job: 0, ration: 1};
  316. let items = await billsGuideItemsModel.find({libID: guidanceLibID, billsID: billsID, deleted: false});
  317. let rationItems = _.filter(items, {type: type.ration});
  318. let rationIds = getAttrs('rationID', rationItems);
  319. let stdRations = await stdRationModel.find({ID: {$in: rationIds}, $or: [{isDeleted: null}, {isDeleted: false}]});
  320. let stdRationIndex = {};
  321. for(let stdRation of stdRations){
  322. stdRationIndex[stdRation.ID] = stdRation;
  323. }
  324. //判断定额完整性
  325. if(!rationAllExist(rationItems, stdRationIndex)){
  326. //建定额链, 排序后再清除不存在的定额,保证顺序正确性
  327. rationItems = chainToArr(rationItems);
  328. //清除已被删除的定额
  329. let removeIds = [];
  330. _.remove(rationItems, function (item) {
  331. if(!stdRationIndex[item.rationID]){
  332. removeIds.push(item.ID);
  333. return true;
  334. }
  335. return false;
  336. });
  337. _.remove(items, function (item) {
  338. return removeIds.includes(item.ID);
  339. });
  340. await billsGuideItemsModel.remove({ID: {$in: removeIds}});
  341. //重组树结构
  342. let bulkArr = [];
  343. for(let i = 0, len = rationItems.length; i < len; i++){
  344. rationItems[i].NextSiblingID = rationItems[i + 1] ? rationItems[i + 1].ID : -1;
  345. bulkArr.push({updateOne: {filter: {ID: rationItems[i].ID}, update: {$set: {NextSiblingID: rationItems[i].NextSiblingID}}}});
  346. }
  347. await billsGuideItemsModel.bulkWrite(bulkArr);
  348. }
  349. return items;
  350. }
  351. async function updateItems(updateDatas) {
  352. let bulkArr = [];
  353. for(let updateData of updateDatas){
  354. if(updateData.updateType === 'create'){
  355. bulkArr.push({insertOne: {document: updateData.updateData}});
  356. }
  357. else if(updateData.updateType === 'update'){
  358. bulkArr.push({updateOne: {filter: updateData.findData, update: {$set: updateData.updateData}}});
  359. }
  360. else{
  361. bulkArr.push({deleteOne: {filter: updateData.findData}});
  362. }
  363. }
  364. if(bulkArr.length > 0){
  365. await billsGuideItemsModel.bulkWrite(bulkArr);
  366. }
  367. }
  368. async function testItems(libID) {
  369. let items = await billsGuideItemsModel.find({libID: libID});
  370. //删除垃圾数据
  371. let delBulk = [];
  372. let itemsMapping = {};
  373. for(let item of items){
  374. itemsMapping[item.ID] = true;
  375. }
  376. for(let item of items){
  377. if(item.ParentID != -1 && !itemsMapping[item.ParentID]){
  378. delBulk.push({
  379. deleteOne: {
  380. filter: {ID: item.ID}
  381. }
  382. });
  383. }
  384. }
  385. if(delBulk.length > 0){
  386. console.log(`delBulk.length`);
  387. console.log(delBulk.length);
  388. await billsGuideItemsModel.bulkWrite(delBulk);
  389. }
  390. /* //查找同层节点含有相同NextSiblingID的节点
  391. let rst = [];
  392. let billsGroup = {};
  393. for(let item of items){
  394. if(!billsGroup[item.billsID]){
  395. billsGroup[item.billsID] = [item];
  396. }
  397. else {
  398. billsGroup[item.billsID].push(item);
  399. }
  400. }
  401. for(let bGroup in billsGroup){
  402. let group = billsGroup[bGroup];
  403. let parentGroup = {};
  404. for(let gItem of group){
  405. if(!parentGroup[gItem.ParentID]){
  406. parentGroup[gItem.ParentID] = [gItem]
  407. }
  408. else {
  409. parentGroup[gItem.ParentID].push(gItem);
  410. }
  411. }
  412. for(let pGroup in parentGroup){
  413. let pGroupData = parentGroup[pGroup];
  414. let nextGroup = {};
  415. for(let nItem of pGroupData){
  416. let sameNext = _.filter(pGroupData, {NextSiblingID: nItem.NextSiblingID});
  417. if(sameNext.length > 1){
  418. console.log(`sameNext`);
  419. console.log(sameNext);
  420. if(!nextGroup[nItem.ParentID + nItem.NextSiblingID]){
  421. rst.push({NextSiblingID: nItem.NextSiblingID, ParentID: nItem.ParentID});
  422. nextGroup[nItem.ParentID + nItem.NextSiblingID] = 1;
  423. }
  424. }
  425. }
  426. }
  427. }*/
  428. return delBulk.length;
  429. }