'use strict'; /** * * * @author Zhong * @date 2018/6/1 * @version */ import mongoose from 'mongoose'; import CompilationModel from "../../users/models/compilation_model"; import moment from 'moment'; const uuidV1 = require('uuid/v1'); const billsLibModel = mongoose.model('std_bills_lib_list'); const billsGuideLibModel = mongoose.model('std_billsGuidance_lib'); const billsGuideItemsModel = mongoose.model('std_billsGuidance_items'); const stdBillsLibModel = mongoose.model('std_bills_lib_list'); const stdBillsModel = mongoose.model('std_bills_lib_bills'); const stdBillsJobsModel = mongoose.model('std_bills_lib_jobContent'); const stdRationModel = mongoose.model('std_ration_lib_ration_items'); const engLibModel = mongoose.model('engineering_lib'); const compilationModel = mongoose.model('compilation'); const _ = require('lodash'); const zhLibID = 'cf851660-3534-11ec-9641-2da8021b8e4e'; const cqLibID = '90c51220-a740-11e8-a354-ab5db7d42428'; module.exports = { handleCopyItems, getComBillsLibInfo, getBillsGuideLibs, initBillsGuideLib, updateBillsGuideLib, getLibWithBills, getItemsBybills, updateItems, testItems }; function setChildren(bill, parentMap) { let children = parentMap[bill.ID]; if (children) { for (let c of children) { setChildren(c, parentMap); } bill.children = children; } else { bill.children = []; } } function sortChildren(lists) { let IDMap = {}, nextMap = {}, firstNode = null, newList = []; for (let l of lists) { if (l.children && l.children.length > 0) l.children = sortChildren(l.children); //递规排序 IDMap[l.ID] = l; if (l.NextSiblingID != -1) nextMap[l.NextSiblingID] = l; } for (let t of lists) { if (!nextMap[t.ID]) { //如果在下一节点映射没找到,则是第一个节点 firstNode = t; break; } } if (firstNode) { newList.push(firstNode); delete IDMap[firstNode.ID]; setNext(firstNode, newList); } //容错处理,如果链断了的情况,直接添加到后面 for (let key in IDMap) { if (IDMap[key]) newList.push(IDMap[key]) } return newList; function setNext(node, array) { if (node.NextSiblingID != -1) { let next = IDMap[node.NextSiblingID]; if (next) { array.push(next); delete IDMap[next.ID]; setNext(next, array); } } } } function getItemFromChildren(children, allItems) { for (let i = 0; i < children.length; i++) { const cur = children[i]; const next = children[i + 1]; cur.NextSiblingID = next && next.ID || -1; allItems.push(cur); if (cur.children && cur.children.length) { getItemFromChildren(cur.children, allItems); } } } function resetTreeData(billsList) { const idMapping = {}; idMapping['-1'] = -1; // 建立新ID-旧ID映射 billsList.forEach(bills => idMapping[bills.ID] = uuidV1()); billsList.forEach(function (bills) { bills.ID = idMapping[bills.ID] ? idMapping[bills.ID] : -1; bills.ParentID = idMapping[bills.ParentID] ? idMapping[bills.ParentID] : -1; bills.NextSiblingID = idMapping[bills.NextSiblingID] ? idMapping[bills.NextSiblingID] : -1; }); } // 从某库中根据清单编号拷贝工艺 async function handleCopyItems(sourceGuideLibID, sourceBillsLibID, targetGuideLibID, targetBillsLibID) { const sourceBills = await stdBillsModel.find({ billsLibId: sourceBillsLibID, deleted: false }, '-_id ID code').lean(); const targetBills = await stdBillsModel.find({ billsLibId: targetBillsLibID, deleted: false }, '-_id ID code').lean(); const sourceItems = await billsGuideItemsModel.find({ libID: sourceGuideLibID, type: 0, deleted: false }, '-_id').lean(); // 过滤掉定额项 const targetCodeIDMap = {}; targetBills.forEach(bills => targetCodeIDMap[bills.code] = bills.ID); const toDeleteIDList = []; const insertBulks = []; sourceBills.forEach(bills => { const matchedItems = sourceItems.filter(item => item.billsID === bills.ID); // 重新将断缺的树结构数据整理好 if (matchedItems.length) { const parentMap = {}; matchedItems.forEach(item => { (parentMap[item.ParentID] || (parentMap[item.ParentID] = [])).push(item); }); const roots = parentMap['-1']; roots.forEach(root => setChildren(root, parentMap)); const sorted = sortChildren(roots); const newItems = []; getItemFromChildren(sorted, newItems); resetTreeData(newItems); const targetBillsID = targetCodeIDMap[bills.code]; if (targetBillsID) { toDeleteIDList.push(targetBillsID); newItems.forEach(newItem => { newItem.libID = targetGuideLibID; newItem.billsID = targetBillsID; insertBulks.push({ insertOne: { document: newItem } }); }) } } }); if (toDeleteIDList.length) { await billsGuideItemsModel.remove({ libID: targetGuideLibID, billsID: { $in : toDeleteIDList } }); } if (insertBulks.length) { await billsGuideItemsModel.bulkWrite(insertBulks); } } async function getCompilationList() { let compilationModel = new CompilationModel(); return await compilationModel.getCompilationList(); } async function getComBillsLibInfo() { let rst = {compilationList: [], billsLibs: []}; let compilationList = await getCompilationList(); if(compilationList.length <= 0){ throw '没有数据'; } else{ for(let compilation of compilationList){ rst.compilationList.push({_id: compilation._id, name: compilation.name}); } rst.billsLibs = await billsLibModel.find({deleted: false}, '-_id billsLibId billsLibName'); return rst; } } async function getBillsGuideLibs(findData, isTemporary) { if (isTemporary) { const libs = await billsGuideLibModel.find({ ID: { $in: [zhLibID, cqLibID] } }).lean(); return libs; } return await billsGuideLibModel.find(findData); } // 如果是“重庆费用定额(2018)”,则默认叶子清单的项目指引窗口只有一条数据,取清单名称。 async function genGuidanceItems_cq18(guidanceLibId, billsLibId) { let bills = await stdBillsModel.find({billsLibId: billsLibId, deleted: false, 'jobs.0': {$exists: true}}); let insertArr = []; for(let bill of bills){ let newItem = { libID: guidanceLibId, ID: uuidV1(), ParentID: -1, NextSiblingID: -1, name: bill.name, type: 0, billsID: bill.ID }; insertArr.push({insertOne: {document: newItem}}); } await billsGuideItemsModel.bulkWrite(insertArr); } //拷贝工作内容并转化为树结构,形成项目指引数据 async function genGuidanceItems(guidanceLibId, billsLibId){ let bills = await stdBillsModel.find({billsLibId: billsLibId, deleted: false, 'jobs.0': {$exists: true}}); //设置工作内容数据 let jobIds = []; let totalJobs = []; for(let bill of bills){ for(let job of bill.jobs){ jobIds.push(job.id); } } jobIds = Array.from(new Set(jobIds)); if(jobIds.length > 0){ totalJobs = await stdBillsJobsModel.find({deleted: false, id: {$in: jobIds}}); } if(totalJobs.length > 0){ let jobIdIndex = {};//id索引 for(let job of totalJobs){ jobIdIndex[job.id] = job; } let insertArr = []; for(let bill of bills){ //排序后根据serialNo转换成NextSiblingID,倒序 bill.jobs.sort(function (a, b) { let rst = 0; if(a.serialNo > b.serialNo){ rst = -1; } else if(a.serialNo < b.serialNo){ rst = 1; } return rst; }); let jobNoIndex = {};//下标索引 for(let i = 0; i < bill.jobs.length; i++){ let newItem = {libID: guidanceLibId, ID: uuidV1(), ParentID: -1, NextSiblingID: jobNoIndex[i - 1] ? jobNoIndex[i - 1]['ID'] : -1, name: jobIdIndex[bill.jobs[i]['id']]['content'], type: 0, billsID: bill.ID}; jobNoIndex[i] = newItem; insertArr.push({insertOne: {document: newItem}}); } } await billsGuideItemsModel.bulkWrite(insertArr); } } async function initBillsGuideLib(updateData){ await billsGuideLibModel.create(updateData); let compilation = await compilationModel.findOne({_id: mongoose.Types.ObjectId(updateData.compilationId)}); if (compilation && compilation.overWriteUrl && compilation.overWriteUrl === '/web/over_write/js/chongqing_2018.js') { await genGuidanceItems_cq18(updateData.ID, updateData.billsLibId); } else { await genGuidanceItems(updateData.ID, updateData.billsLibId); } } async function updateBillsGuideLib(data) { if(data.updateType === 'delete'){ //删除所有条目 await billsGuideLibModel.remove(data.findData); await billsGuideItemsModel.remove({libID: data.findData.ID}); } else { await billsGuideLibModel.update(data.findData, {$set: data.updateData}); await engLibModel.update({'billsGuidance_lib.id': data.findData.ID}, {$set: {'billsGuidance_lib.$.name': data.updateData.name}}, {multi: true}); } } async function getLibWithBills(libID){ let guidanceLib = await getBillsGuideLibs({ID: libID}); if(guidanceLib.length === 0){ throw '不存在此指引库!'; } let billsLib = await stdBillsLibModel.findOne({billsLibId: guidanceLib[0].billsLibId}); if(!billsLib){ throw '引用的清单规则库不存在!'; } let bills = await stdBillsModel.find({billsLibId: billsLib.billsLibId}, '-_id code name ID NextSiblingID ParentID jobs items comment').lean(); const guideItems = await billsGuideItemsModel.find({ libID: guidanceLib[0].ID }, '-_id billsID').lean(); const billsMap = {}; for (const item of guideItems) { billsMap[item.billsID] = true; } for (const item of bills) { if (billsMap[item.ID]) { item.hasGuide = true; } } return {guidanceLib: guidanceLib[0], bills}; } function getAttrs(field, datas){ let rst = []; for(let data of datas){ if(data[field]){ rst.push(data[field]); } } return rst; } //定额项目指所引用定额是否被删除 function rationAllExist(rationItems, stdRationIdx) { for(let item of rationItems){ if(!stdRationIdx[item.rationID]){ return false; } } return true; } //将同层树结构转为顺序数组 function chainToArr(nodes){ let rst = []; let tempIdx = {}; let nodeIdx = {}; //建索引 for(let node of nodes){ tempIdx[node.ID] = {ID: node.ID, NextSiblingID: node.NextSiblingID, preSibling: null, nextSibling: null}; nodeIdx[node.ID] = node; } //建链 for(let i in tempIdx){ let temp = tempIdx[i]; if(temp.NextSiblingID != -1){ let next = tempIdx[temp.NextSiblingID]; temp.nextSibling = next; next.preSibling = temp; } } let firstNode = null; for(let i in tempIdx){ if(!tempIdx[i].preSibling){ firstNode = tempIdx[i]; break; } } //获得顺序队列 while(firstNode){ rst.push(nodeIdx[firstNode.ID]); firstNode = firstNode.nextSibling; } return rst; } async function getItemsBybills(guidanceLibID, billsID){ const type = {job: 0, ration: 1}; let items = await billsGuideItemsModel.find({libID: guidanceLibID, billsID: billsID, deleted: false}); let rationItems = _.filter(items, {type: type.ration}); let rationIds = getAttrs('rationID', rationItems); let stdRations = await stdRationModel.find({ID: {$in: rationIds}, $or: [{isDeleted: null}, {isDeleted: false}]}); let stdRationIndex = {}; for(let stdRation of stdRations){ stdRationIndex[stdRation.ID] = stdRation; } //判断定额完整性 if(!rationAllExist(rationItems, stdRationIndex)){ //建定额链, 排序后再清除不存在的定额,保证顺序正确性 rationItems = chainToArr(rationItems); //清除已被删除的定额 let removeIds = []; _.remove(rationItems, function (item) { if(!stdRationIndex[item.rationID]){ removeIds.push(item.ID); return true; } return false; }); _.remove(items, function (item) { return removeIds.includes(item.ID); }); await billsGuideItemsModel.remove({ID: {$in: removeIds}}); //重组树结构 let bulkArr = []; for(let i = 0, len = rationItems.length; i < len; i++){ rationItems[i].NextSiblingID = rationItems[i + 1] ? rationItems[i + 1].ID : -1; bulkArr.push({updateOne: {filter: {ID: rationItems[i].ID}, update: {$set: {NextSiblingID: rationItems[i].NextSiblingID}}}}); } await billsGuideItemsModel.bulkWrite(bulkArr); } return items; } async function updateItems(updateDatas) { let bulkArr = []; for(let updateData of updateDatas){ if(updateData.updateType === 'create'){ bulkArr.push({insertOne: {document: updateData.updateData}}); } else if(updateData.updateType === 'update'){ bulkArr.push({updateOne: {filter: updateData.findData, update: {$set: updateData.updateData}}}); } else{ bulkArr.push({deleteOne: {filter: updateData.findData}}); } } if(bulkArr.length > 0){ await billsGuideItemsModel.bulkWrite(bulkArr); } } async function testItems(libID) { let items = await billsGuideItemsModel.find({libID: libID}); //删除垃圾数据 let delBulk = []; let itemsMapping = {}; for(let item of items){ itemsMapping[item.ID] = true; } for(let item of items){ if(item.ParentID != -1 && !itemsMapping[item.ParentID]){ delBulk.push({ deleteOne: { filter: {ID: item.ID} } }); } } if(delBulk.length > 0){ console.log(`delBulk.length`); console.log(delBulk.length); await billsGuideItemsModel.bulkWrite(delBulk); } /* //查找同层节点含有相同NextSiblingID的节点 let rst = []; let billsGroup = {}; for(let item of items){ if(!billsGroup[item.billsID]){ billsGroup[item.billsID] = [item]; } else { billsGroup[item.billsID].push(item); } } for(let bGroup in billsGroup){ let group = billsGroup[bGroup]; let parentGroup = {}; for(let gItem of group){ if(!parentGroup[gItem.ParentID]){ parentGroup[gItem.ParentID] = [gItem] } else { parentGroup[gItem.ParentID].push(gItem); } } for(let pGroup in parentGroup){ let pGroupData = parentGroup[pGroup]; let nextGroup = {}; for(let nItem of pGroupData){ let sameNext = _.filter(pGroupData, {NextSiblingID: nItem.NextSiblingID}); if(sameNext.length > 1){ console.log(`sameNext`); console.log(sameNext); if(!nextGroup[nItem.ParentID + nItem.NextSiblingID]){ rst.push({NextSiblingID: nItem.NextSiblingID, ParentID: nItem.ParentID}); nextGroup[nItem.ParentID + nItem.NextSiblingID] = 1; } } } } }*/ return delBulk.length; }