| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381 | /** * Created by zhang on 2018/4/17. */const projectType = {    folder: 'Folder',    tender: 'Tender',    project: 'Project',    engineering: 'Engineering',};//先导出后require可以解决循环引用问题module.exports={    prepareShareList,    getShareList,    addShareList,    updateShareList,    deleteShareList,    getShareInfoMap,    getRecentShareList,    moveProject:moveProject,    copyProject:copyProject,    copyExample: copyExample,    getSummaryInfo: getSummaryInfo,    getSummaryInfoByTender: getSummaryInfoByTender,    getIndexReportData: getIndexReportData,    getTendersFeeInfo: getTendersFeeInfo,    getConstructionProject: getConstructionProject,    getFullPath: getFullPath,    projectType: projectType,    getPosterityProjects: getPosterityProjects,    isShare: isShare,    isFirst: isFirst,    getShareInfo: getShareInfo,    prepareInitialData: prepareInitialData,    changeFile:changeFile,    getBasicInfo: getBasicInfo,    getProjectFeature: getProjectFeature,    getProjectByGranularity: getProjectByGranularity,    importProject: importProject,    getProjectPlaceholder: getProjectPlaceholder,    exportProject:exportProject,    importProjects:importProjects,//建筑这里重名了,这比养护加多了个s    initOverHeightItems: initOverHeightItems,    uploadToken:uploadToken,    downLoadProjectFile:downLoadProjectFile,    importChongqingProject:importChongqingProject,    importProcessChecking:importProcessChecking,    importInterface,    isTenderOverrun,    getWelcomeInfo:getWelcomeInfo};let mongoose = require('mongoose');let _ = require("lodash");let feeRate_facade = require('../../fee_rates/facade/fee_rates_facade');let glj_facade = require('../../glj/facade/glj_facade');let project_facade = require('../../main/facade/project_facade');let logger = require("../../../logs/log_helper").logger;const uuidV1 = require('uuid/v1');let projectModel = mongoose.model('projects');let projectSettingModel =  mongoose.model('proj_setting');let billsModel = mongoose.model('bills');let rationModel = mongoose.model('ration');let gljListModel = mongoose.model("glj_list");let calcProgramsModel = mongoose.model('calc_programs');let labourCoesModel = mongoose.model('labour_coes');let feeRateModel = mongoose.model('fee_rates');let feeRateFileModel = mongoose.model('fee_rate_file');let unitPriceFileModel = mongoose.model("unit_price_file");let mixRatioModel = mongoose.model("mix_ratio");let unitPriceModel = mongoose.model("unit_price");let installationFeeModel = mongoose.model("installation_fee");let rationGLJModel = mongoose.model('ration_glj');let rationCoeModel = mongoose.model('ration_coe');let rationInstallationModel = mongoose.model('ration_installation');let quantityDetailModel = mongoose.model('quantity_detail');let rationTemplateModel = mongoose.model('ration_template');let userModel = mongoose.model('user');let compleGljSectionModel = mongoose.model('complementary_glj_section');let compleGljSectionTModel = mongoose.model('complementary_glj_section_templates');let compilationModel = mongoose.model('compilation');let engineeringModel = mongoose.model('engineering_lib');let basicInfoModel = mongoose.model('std_basic_info_lib');let projectFeatureModel = mongoose.model('std_project_feature_lib');let productModel = mongoose.model('product');let stdRationItemModel = mongoose.model('std_ration_lib_ration_items');let stdGljItemModel = mongoose.model('std_glj_lib_gljList');import BillsTemplateModel from "../models/templates/bills_template_model";let evaluateListModel = mongoose.model("evaluate_list");let bidListModel = mongoose.model("bid_evaluation_list");let contractorListModel = mongoose.model("contractor_list");let featureLibModel =  mongoose.model("std_project_feature_lib");let importLogsModel = mongoose.model("import_logs");const overHeightLibModel = mongoose.model('std_over_height_lib');const shareListModel = mongoose.model('share_list');let welcomeModel = mongoose.model("welcome_setting");let scMathUtil = require('../../../public/scMathUtil').getUtil();let counter = require('../../../public/counter/counter');import SectionTreeDao from '../../complementary_ration_lib/models/sectionTreeModel';let sectionTreeDao = new SectionTreeDao();import CounterModel from "../../glj/models/counter_model";import moment from 'moment-timezone';import billsFlags from '../../common/const/bills_fixed';const notDeleted = [{deleteInfo: null}, {'deleteInfo.deleted': false}];import {    defaultDecimal,    billsQuantityDecimal,    displaySetting,    calcOptions,    tenderSetting,    G_FILE_VER} from '../models/project_property_template';let labourCoeFacade = require('../../main/facade/labour_coe_facade');let calcProgramFacade = require('../../main/facade/calc_program_facade');let mainColLibModel = mongoose.model('std_main_col_lib');import EngineeringLibModel from "../../users/models/engineering_lib_model";let installationFacade = require('../../main/facade/installation_facade');let cipher = require('../../../public/cipher');let qiniu = require("qiniu");let fs = require("fs");let path = require("path");let request = require("request");const systemSettingMiddleware = require('../../main/middleware/system_setting');let qiniu_config = {    "AccessKey": "_gR1ed4vi1vT2G2YITGSf4_H0fJu_nRS9Tzk3T4z",    "SecretKey": "ty4zd0FHqgEDaiVzSLC8DfHlai99aS7bspLkw6s6",    "Bucket": "yun-update",    "Domain": "http://serverupdate.smartcost.com.cn"};let mac = new qiniu.auth.digest.Mac(qiniu_config.AccessKey, qiniu_config.SecretKey);// 重构了分享的数据结构,原本存在projects表的shareInfo数组中// 为了更好地适应新功能及以后拓展功能,现将分享信息存放在单独的share_list表中// 为了适应旧数据,需要在share_list没有数据的时候,将原本的分享信息复制到share_list中,同时生成联系人// TODO: 上线后这个方法执行过后,应该删除此方法async function prepareShareList() {    const shareListCount = await shareListModel.count({});    console.log(shareListCount);    if (shareListCount) {        return;    }    logger.info('================================enterPrepareShareList================================================');    // 将项目的shareInfo数据复制到share_list表中    const projects = await projectModel.find({'shareInfo.0': {$exists: true}}, '-_id ID userID shareInfo').lean();    if (!projects.length) {        return;    }    const shareList = [];    const userMap = {}; // 给生成联系人做准备    projects.forEach(project => {        const ownerContacts = userMap[project.userID] || (userMap[project.userID] = []);        project.shareInfo.forEach(shareItem => {            if (!shareItem.shareDate) {                return;            }            const receiverContacts = userMap[shareItem.userID] || (userMap[shareItem.userID] = []);            receiverContacts.push(project.userID);            const newItem = {                ID: uuidV1(),                projectID: project.ID,                owner: project.userID,                receiver: shareItem.userID,                allowCopy: shareItem.allowCopy,                allowCooperate: shareItem.allowCooperate,                shareDate: shareItem.shareDate,                updateDate: shareItem.shareDate            };            ownerContacts.push(shareItem.userID);            shareList.push(newItem);        });    });    // 插入分享信息    const shareListPromise = shareListModel.insertMany(shareList);    const tasks = [shareListPromise];    // 更新用户信息    const userTasks = [];    Object.entries(userMap).forEach(([userID, contacts]) => {        contacts = [...new Set(contacts)]            .map(user => ({ userID: user }));        const task = {            updateOne: {                filter: {                    _id: mongoose.Types.ObjectId(userID)                },                update: {                    contacts                }            }        };        userTasks.push(task);    });    if (userTasks.length) {        tasks.push(userModel.bulkWrite(userTasks));    }    await Promise.all(tasks);}// 获取分享列表async function getShareList(query) {    return await shareListModel.find(query, '-_id').lean();}async function addShareList(docs) {    await shareListModel.insertMany(docs);}async function deleteShareList(query) {    await shareListModel.deleteMany(query);}async function updateShareList(updateData) {    const bulks = updateData.map(item => (        {            updateOne: {                filter: item.query,                update: item.update            }        }    ));    if (bulks.length) {        await shareListModel.bulkWrite(bulks);    }}// 根据项目ID获取项目shareInfo字段的数据映射(前端旧逻辑依赖项目本身的shareInfo字段)async function getShareInfoMap(projectIDs, shareList = null) {    if (!shareList) {        shareList = await getShareList({projectID: {$in: projectIDs}});    }    // 将相同项目的分享信息进行映射    const map = {};    shareList.forEach(item => {        const shareInfo = map[item.projectID] || (map[item.projectID] = []);        shareInfo.push({            userID: item.receiver,            shareDate: item.shareDate,            updateDate: item.updateDate,            allowCopy: item.allowCopy,            allowCooperate: item.allowCooperate        });    });    return map;}// 获取最近分享(只算分享出去的记录)async function getRecentShareList(userID, count) {    const shareList = await getShareList({owner: userID});    shareList.sort((a, b) => Date.parse(b.shareDate) - Date.parse(a.shareDate));    const set = new Set();    for(const item of shareList) {        if (set.size === count) {            break;        }        set.add(item.receiver);    }    const sortedIDList = [...set];    const userIDList = sortedIDList.map(userID => mongoose.Types.ObjectId(userID));    const users = await userModel.find({_id: {$in: userIDList}}, 'real_name mobile company').lean();    users.sort((a, b) => sortedIDList.indexOf(a._id.toString()) - sortedIDList.indexOf(b._id.toString()));    return users;}//拷贝例题项目//@param {String}userID {Array}projIDs拷贝的例题项目ID(建设项目、文件夹)@return {Boolean}async function copyExample(userID, compilation, projIDs){    let allProjs = [],        IDMapping = {},        projMapping = {};    //例题项目不可为单项工程、单位工程、只能是建设项目、文件夹,否则不自动新建例题项目(这里不报错,让用户没有感觉)    let parentExample = await projectModel.find({ID: {$in: projIDs}, $or: notDeleted}).lean();    if (parentExample.length === 0) {        return false;    }    allProjs = allProjs.concat(parentExample);    for (let i = 0; i < parentExample.length; i++) {        let data = parentExample[i];        if (data.projType === projectType.tender || data.projType === projectType.engineering) {            return false;        }        //设置成顶节点,最后一个节点设置成末节        data.ParentID = -1;        if (i === parentExample.length - 1) {            data.NextSiblingID = -1;        }    }    //获取所有的子项目    let posterityProjs = await getPosterityProjects(projIDs);    allProjs = allProjs.concat(posterityProjs);    let projCounter = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.project, allProjs.length);    //旧ID与新ID映射    for (let i = 0; i < allProjs.length; i++) {        let data = allProjs[i];        let newID = projCounter.sequence_value - (allProjs.length - 1) + i;        IDMapping[data.ID] = newID;        projMapping[newID] = data;    }    //return;    //设置新的树结构数据    let newDate = new Date(),        parentBulks = [];    for (let data of allProjs) {        let orgID = data.ID;        data.ID = IDMapping[data.ID];        data.ParentID = IDMapping[data.ParentID] ? IDMapping[data.ParentID] : -1;        data.NextSiblingID = IDMapping[data.NextSiblingID] ? IDMapping[data.NextSiblingID] : -1;        data.createDateTime = newDate;        data.userID = userID;        data.compilation = compilation;        data.shareInfo = [];        if (data.projType !== projectType.tender) {            let newData = _.cloneDeep(data);            delete newData._id;          //  await projectModel.create(newData);            parentBulks.push({insertOne: {document: newData}});        } else {            //拷贝单位工程            let rootProjectID = projMapping[data.ParentID].ParentID;            let projectMap = {                copy: {                    document: {userID: userID, ID: orgID, NextSiblingID: data.NextSiblingID, ParentID: data.ParentID, name: data.name, shareInfo: [],                                compilation: compilation, fileVer: data.fileVer, projType: data.projType, property: {rootProjectID: rootProjectID}}                }            };            await copyProject(userID, compilation, {projectMap}, data.ID);        }    }    //最末顶层项目(兼容测试时已存在有项目,正常用户第一次进费用定额,该费用定额不存在项目)    let lastProj = await projectModel.findOne({userID: userID, compilation: compilation, ParentID: -1, NextSiblingID: -1, $or: notDeleted});    if (lastProj) {        parentBulks.push({updateOne: {filter: {ID: lastProj.ID}, update: {$set: {NextSiblingID: parentExample[0].ID}}}});    }    //拷贝父级文件    await projectModel.bulkWrite(parentBulks);    return true;}async function copyProject(userID, compilationID,data,newProjectID = null) {    let projectMap = data.projectMap;    let originalID = projectMap['copy'].document.ID;    if (!newProjectID) {        newProjectID = await getCounterID("projects");    }    let newFeeName = null,newUnitName = null;    let calcProgramFileID = uuidV1();//新的计算程序文件ID    let labourCoeFileID = uuidV1();//新的人工调整系数文件ID    let feeRateFileID = uuidV1();//新的费率文件ID    let unitPriceFileID = await getCounterID("unit_price_file");//新的单价文件ID    let originalProject = await projectModel.findOne({'ID':originalID});//查找最新的那项目信息,前端缓存的数据有可能不是最新的    if(!originalProject){        throw new Error('没有找到对应的项目,复制失败!');    }    let property = originalProject.property;    if(projectMap['copy'].document.property){//更新项目属性信息        setProperty(property,projectMap['copy'].document.property);    }    let originalProperty = _.cloneDeep(property);    projectMap['copy'].document.property = property;    logger.info("复制项目: 旧项目ID: "+originalID+ " ------- 新项目ID: "+newProjectID);    //费率文件、单价文件重名检查    let feeRate =  await feeRateFileModel.findOne({rootProjectID:originalProperty.rootProjectID,name:originalProperty.feeFile.name,deleteInfo:null});    if(feeRate){//存在重名的文件        newFeeName = originalProperty.feeFile.name + '(' + moment(Date.now()).tz("Asia/Shanghai").format('MM-DD HH:mm:ss') + '复制)';        projectMap['copy'].document.property.feeFile.name = newFeeName;    }    let unitPriceFile =  await unitPriceFileModel.findOne({root_project_id: originalProperty.rootProjectID,name:originalProperty.unitPriceFile.name,deleteInfo: null});    if(unitPriceFile){//存在重名的文件        newUnitName = originalProperty.unitPriceFile.name + '(' + moment(Date.now()).tz("Asia/Shanghai").format('MM-DD HH:mm:ss') + '复制)';        projectMap['copy'].document.property.unitPriceFile.name = newUnitName;    }    //更新项目的属性;    projectMap['copy'].document.ID = newProjectID;    if(projectMap['copy'].document.property.calcProgramFile){        projectMap['copy'].document.property.calcProgramFile.ID = calcProgramFileID;    }    if(projectMap['copy'].document.property.labourCoeFile){        projectMap['copy'].document.property.labourCoeFile.ID = labourCoeFileID;    }    if(projectMap['copy'].document.property.feeFile){        projectMap['copy'].document.property.feeFile.id = feeRateFileID;    }    if(projectMap['copy'].document.property.unitPriceFile){        projectMap['copy'].document.property.unitPriceFile.id = unitPriceFileID;    }    projectMap['copy'].document.userID = userID;    projectMap['copy'].document.compilation = compilationID;    projectMap['copy'].document.createDateTime = new Date();    //清单、定额ID生成任务    let IDtasks = [        createIDsAndReturn(originalID,billsModel),        createIDsAndReturn(originalID,rationModel),        getProjectGLJIDAndReturn(originalID,newProjectID)    ];    let [billMap,rationMap,projectGLJMap] = await Promise.all(IDtasks);    //所有复制任务异步处理,提升效率  复制清单,定额,4个文件,项目工料机, 定额工料机,人工系数,工程量明细,定额安装增加费,安装增加费    let copyTasks = [        createProject(projectMap),        copyProjectSetting(originalID,newProjectID),        copyBills(newProjectID,billMap),        copyRations(newProjectID,billMap.uuidMaping,rationMap,projectGLJMap.IDMap),        copyProjectGLJ(projectGLJMap.datas),        copyInstallFee(originalID,newProjectID),        copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationGLJModel),        copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationCoeModel),        copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,quantityDetailModel),        copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationInstallationModel),        copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationTemplateModel),        copyMaterialList(originalID,newProjectID,projectGLJMap.IDMap,evaluateListModel),        copyMaterialList(originalID,newProjectID,projectGLJMap.IDMap,bidListModel),        copyMaterialList(originalID,newProjectID,projectGLJMap.IDMap,contractorListModel),    ];    if(originalProperty.calcProgramFile){        copyTasks.push(commonCopy(newProjectID,originalProperty.calcProgramFile.ID,calcProgramFileID,calcProgramsModel));    }    if(originalProperty.labourCoeFile){        copyTasks.push(commonCopy(newProjectID,originalProperty.labourCoeFile.ID,labourCoeFileID,labourCoesModel));    }    if(originalProperty.feeFile){        copyTasks.push(copyFeeRate(originalProperty.rootProjectID,userID,originalProperty.feeFile.id,feeRateFileID,newFeeName));    }    if(originalProperty.unitPriceFile){        copyTasks.push(copyUnitPriceFile(newProjectID,originalProperty.rootProjectID,userID,originalProperty.unitPriceFile.id,unitPriceFileID,newUnitName));    }    let p = await Promise.all(copyTasks);    return p[0];}async function createIDsAndReturn(originalID,model) {    let uuidMaping = {};//做ID映射    let datas = [];    let result = await model.find({"projectID": originalID}, '-_id');    uuidMaping['-1'] = -1;    //建立uuid-ID映射    for(let d of result){        uuidMaping[d.ID] = uuidV1();        datas.push(d._doc);    }    return{uuidMaping:uuidMaping,datas:datas};}async function getProjectGLJIDAndReturn(originalID,newProjectID) {    let IDMap = {};    let datas = [];    let result = await gljListModel.find({project_id:originalID}, '-_id');    let gljCount = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.glj_list, result.length);    for(let i = 0; i < result.length; i++){        let d = result[i];        let newID = gljCount.sequence_value - (result.length - 1) + i;        //let newID = await getCounterID("glj_list");        IDMap[d.id] = newID;        d._doc.project_id = newProjectID;        d._doc.id = newID;        datas.push(d._doc);    }    return{IDMap:IDMap,datas:datas};}async function createProject(projectMap) {//复制项目    let tasks = [];    if(projectMap['update']){//如果复制后存在前一个节点,则更新其NextSiblingID        tasks.push({updateOne:{filter : projectMap['update'].query, update : {NextSiblingID:projectMap['copy'].document.ID}}});    }    tasks.push({insertOne: {document: projectMap['copy'].document}});//复制任务    await projectModel.bulkWrite(tasks);    return projectMap;}async function copyProjectSetting(originalID,newProjectID) {    let result = null;    let setting = await projectSettingModel.findOne({"projectID": originalID}, '-_id');    if(setting){        setting._doc.projectID =newProjectID;        result = await projectSettingModel.create(setting._doc);    }    return result;}async function copyBills(newProjectID,billMap) {     let uuidMaping = billMap.uuidMaping;//做ID映射    for(let doc of billMap.datas){        doc = getCopyBillDatas(doc,newProjectID,uuidMaping);    }    await insertMany(billMap.datas,billsModel);    return billMap.datas;}function getCopyBillDatas(doc,newProjectID,uuidMaping) {    doc.projectID = newProjectID;    doc.ID = uuidMaping[doc.ID] ? uuidMaping[doc.ID] : -1;    doc.ParentID = uuidMaping[doc.ParentID] ? uuidMaping[doc.ParentID] : -1;    doc.NextSiblingID = uuidMaping[doc.NextSiblingID] ? uuidMaping[doc.NextSiblingID] : -1;    //对于有基数计算的节点,需要替换计算基数中引用的ID为新的ID    if(doc.calcBase && doc.calcBase != ""){        let idArr = scMathUtil.getFIDArr(doc.calcBase);        for(let re of idArr){            let oID = re.substr(1);//去掉开头的@字符            if(!uuidMaping[oID]) continue;            doc.calcBase = doc.calcBase.replace(new RegExp(oID, "g"),uuidMaping[oID]);        }    }    return doc;}async function copyRations(newProjectID,billsIDMap,rationMap,projectGLJIDMap) {    let uuidMaping = rationMap.uuidMaping;    for(let doc of rationMap.datas){        doc = getCopyRationData(doc,newProjectID,billsIDMap,uuidMaping,projectGLJIDMap);    }    if(rationMap.datas.length > 0){        await insertMany(rationMap.datas,rationModel);    }    return rationMap.datas;}function getCopyRationData(doc,newProjectID,billsIDMap,uuidMaping,projectGLJIDMap){    doc.projectID = newProjectID;    doc.ID = uuidMaping[doc.ID] ? uuidMaping[doc.ID] : -1;    if(doc.billsItemID){        doc.billsItemID = billsIDMap[doc.billsItemID]?billsIDMap[doc.billsItemID]:-1;    }    if(doc.referenceRationID) doc.referenceRationID = uuidMaping[doc.referenceRationID]?uuidMaping[doc.referenceRationID]:undefined;    //绑定定类型的工料机 项目工料机ID    doc.type==3&&doc.projectGLJID&&projectGLJIDMap[doc.projectGLJID]?doc.projectGLJID = projectGLJIDMap[doc.projectGLJID]:'';    return doc;}async function copyProjectGLJ(gljList) {    await insertMany(gljList,gljListModel);}async  function commonCopy(newProjectID,oldID,newID,model) { //对于只需更新ID和projectID的文件的复制    let result = null;    let file = await model.findOne({"ID": oldID}, '-_id');    if(file){        file._doc.ID = newID;        file._doc.projectID =newProjectID;        result = await model.create(file._doc);    }    return result;}async function copyFeeRate(rootProjectID,userID,originalFeeRateFileID,feeRateFileID,newName) {//复制费率和费率文件    let [feeRateFile,feeRate] =await feeRate_facade.getFeeRateByID(originalFeeRateFileID);    let newFeeRateID = uuidV1();    if(feeRate){        feeRate._doc.ID = newFeeRateID;        await feeRateModel.create(feeRate._doc);    }    if(feeRateFile){        feeRateFile._doc.ID = feeRateFileID;        newName?feeRateFile._doc.name = newName:'';        feeRateFile._doc.userID = userID;        feeRateFile._doc.rootProjectID = rootProjectID;        feeRateFile._doc.feeRateID = newFeeRateID;        await feeRateFileModel.create(feeRateFile._doc);    }}async function copyUnitPriceFile(newProjectID,rootProjectID,userID,originaluUnitPriceFileID,unitPriceFileID,newName) {//复制单价文件、组成物    let taskList = [        copyFile(newProjectID,rootProjectID,userID,originaluUnitPriceFileID,unitPriceFileID,newName),        copySubList(originaluUnitPriceFileID,unitPriceFileID,mixRatioModel),        copySubList(originaluUnitPriceFileID,unitPriceFileID,unitPriceModel)    ];    return await Promise.all(taskList);    async function copyFile(newProjectID,rootProjectID,userID,originaluUnitPriceFileID,unitPriceFileID,newName){        let unitPriceFile = await unitPriceFileModel.findOne({id:originaluUnitPriceFileID}, '-_id');        if(unitPriceFile){            unitPriceFile._doc.id = unitPriceFileID;            newName?unitPriceFile._doc.name = newName:'';            unitPriceFile._doc.project_id = newProjectID;            unitPriceFile._doc.user_id = userID;            unitPriceFile._doc.root_project_id = rootProjectID        }        await unitPriceFileModel.create(unitPriceFile._doc);    }    async function copySubList(srcFID,newFID,model) {        let mList = await model.find({unit_price_file_id: srcFID}, '-_id');        let rList = [];        for(let m of mList){            m._doc.unit_price_file_id = newFID;            m._doc.id = await getCounterID(model.modelName);            rList.push(m._doc);        }        await insertMany(rList,model)    }}async function copyInstallFee(originalPID,newProjectID) {    let result = null;    let installationFee = await installationFeeModel.find({projectID:originalPID}, '-_id');    let newList = [];    for(let i of installationFee ){        i._doc.ID = uuidV1();        i._doc.projectID = newProjectID;        newList.push(i._doc);    }    if(newList.length >0){        result =  await installationFeeModel.insertMany(newList);    }    return result;}async function copyRationSubList(originalPID,newProjectID,billIDMap,rationIDMap,projectGLJIDMap,model) {// 定额工料机,附注条件,工程量明细,定额安装增加费,模板子目    let subList = await model.find({projectID:originalPID}, '-_id');    let newList =[];    for(let s of subList){        s._doc = getCopyRationSubData(s._doc,newProjectID,billIDMap,rationIDMap,projectGLJIDMap);  /*      s._doc.ID = uuidV1();        s._doc.projectID = newProjectID;        s._doc.rationID&&rationIDMap[s._doc.rationID]?s._doc.rationID = rationIDMap[s._doc.rationID]:'';        s._doc.billID&&billIDMap[s._doc.billID]?s._doc.billID = billIDMap[s._doc.billID]:'';        s._doc.billsItemID&&billIDMap[s._doc.billsItemID]?s._doc.billsItemID = billIDMap[s._doc.billsItemID]:'';        s._doc.projectGLJID&&projectGLJIDMap[s._doc.projectGLJID]?s._doc.projectGLJID = projectGLJIDMap[s._doc.projectGLJID]:'';        if(s._doc.templateList && s._doc.templateList.length > 0 ){            for(let t of s._doc.templateList){                if(t.billID && billIDMap[t.billID]) t.billID =  billIDMap[t.billID];                if(t.fxID && billIDMap[t.fxID]) t.fxID =  billIDMap[t.fxID];            }        }*/        newList.push(s._doc);    }    await insertMany(newList,model);}async function copyMaterialList(originalPID,newProjectID,projectGLJIDMap,model) {//暂估材料等信息    let materialList = await model.find({projectID:originalPID}, '-_id').lean();    let newList = [];    for(let m of materialList){        newList.push(getNewMaterial(m,newProjectID,projectGLJIDMap))    }    await insertMany(newList,model);}function getNewMaterial(m,newProjectID,projectGLJIDMap) {    m.ID = uuidV1();    m.projectID = newProjectID;    if(projectGLJIDMap[m.projectGLJID]) m.projectGLJID = projectGLJIDMap[m.projectGLJID];    return m;}function getCopyRationSubData(doc,newProjectID,billIDMap,rationIDMap,projectGLJIDMap){    doc.ID = uuidV1();    doc.projectID = newProjectID;    doc.rationID&&rationIDMap[doc.rationID]?doc.rationID = rationIDMap[doc.rationID]:'';    doc.billID&&billIDMap[doc.billID]?doc.billID = billIDMap[doc.billID]:'';    doc.billsItemID&&billIDMap[doc.billsItemID]?doc.billsItemID = billIDMap[doc.billsItemID]:'';    doc.projectGLJID&&projectGLJIDMap[doc.projectGLJID]?doc.projectGLJID = projectGLJIDMap[doc.projectGLJID]:'';    if(doc.templateList && doc.templateList.length > 0 ){        for(let t of doc.templateList){            if(t.billID && billIDMap[t.billID]) t.billID =  billIDMap[t.billID];            if(t.fxID && billIDMap[t.fxID]) t.fxID =  billIDMap[t.fxID];        }    }    return doc;}async function moveProject(data) {    data = JSON.parse(data);    let projectMap = data.projectMap,feeRateMap = data.feeRateMap,unitPriceMap = data.unitPriceMap;    let projectTasks = [],feeRateTask=[],feeRateFileTask=[],unitPriceTask=[],unitPriceFileTask=[],mixRatioTask=[];    let feeUniqMap=[],priceUniqMap={};//费率、单价文件唯一映射表当移动多个项目时,任务有可能出现重复的情况    if(!_.isEmpty(feeRateMap)&&data.rootProjectID){//如果费率有修改        let feeRates =await feeRate_facade.getFeeRatesByProject(data.rootProjectID);//取目标建设项目的所有费率文件,重名检查        for(let projectID in feeRateMap){            let rename = feeRateMap[projectID].name;            let feeRateID =  feeRateMap[projectID].query.ID;            let checkFee = _.find(feeRates,{'name':rename});            let newID = feeRateID;            if(checkFee){//说明费率名字已存在,需要重命名                rename = feeUniqMap[feeRateID]?feeUniqMap[feeRateID].name:rename + '(' + new Date().Format('MM-dd hh:mm:ss') + '移动)';                projectMap[projectID]['update']['property.feeFile.name'] = rename;                if(feeRateMap[projectID].action == 'move'){                    feeRateMap[projectID].update.name = rename;                }            }            if(feeRateMap[projectID].action == 'copy'){                newID = feeUniqMap[feeRateID]?feeUniqMap[feeRateID].ID:uuidV1();                feeRateMap[projectID].name = rename;                feeRateMap[projectID].ID =  newID;                projectMap[projectID]['update']['property.feeFile.id'] = newID;            }            if(!feeUniqMap[feeRateID]){                await generateFeeRateTask(feeRateMap[projectID],feeRateTask,feeRateFileTask);                feeUniqMap[feeRateID] = {name:rename,ID:newID};            }        }    }    if(!_.isEmpty(unitPriceMap)&&data.rootProjectID) {//如果单价文件有修改        let unitPriceFiles =  await unitPriceFileModel.find({root_project_id: data.rootProjectID, deleteInfo: null});        for(let projectID in unitPriceMap){            let checkUn = _.find(unitPriceFiles,{'name':unitPriceMap[projectID].name});            let rename = unitPriceMap[projectID].name;            let unitPriceID = unitPriceMap[projectID].query.id;            let newID = unitPriceID;            if(checkUn){//重名检查                rename = priceUniqMap[unitPriceID]?priceUniqMap[unitPriceID].name:rename + '(' + new Date().Format('MM-dd hh:mm:ss') + '移动)';                projectMap[projectID]['update']['property.unitPriceFile.name'] = rename;                if(unitPriceMap[projectID].action =='move'){                    unitPriceMap[projectID].update.name = rename;                }            }            if(unitPriceMap[projectID].action =='copy'){                newID =  priceUniqMap[unitPriceID]?priceUniqMap[unitPriceID].id: await getCounterID("unit_price_file");                unitPriceMap[projectID].name = rename;                unitPriceMap[projectID].id = newID;                projectMap[projectID]['update']['property.unitPriceFile.id'] = newID;            }            if(!priceUniqMap[unitPriceID]){                await generateUnitPriceTask(unitPriceMap[projectID],projectID,data.user_id,unitPriceTask,unitPriceFileTask,mixRatioTask);                priceUniqMap[unitPriceID] = {name:rename,id:newID};            }        }    }    if(!_.isEmpty(projectMap)){        for(let projectID in projectMap){            projectTasks.push({updateOne:{filter : projectMap[projectID].query, update : projectMap[projectID].update}});        }    }    projectTasks.length>0?await projectModel.bulkWrite(projectTasks):'';    feeRateTask.length>0?await feeRateModel.bulkWrite(feeRateTask):'';    feeRateFileTask.length>0?await feeRateFileModel.bulkWrite(feeRateFileTask):'';    unitPriceFileTask.length>0?await unitPriceFileModel.bulkWrite(unitPriceFileTask):'';    unitPriceTask.length>0?await insertMany(unitPriceTask,unitPriceModel):'';    mixRatioTask.length>0?await insertMany(mixRatioTask,mixRatioModel):'';    return projectMap;}async function generateFeeRateTask(data,feeRateTask,feeRateFileTask) {    if(data.action == 'move'){        let task = {            updateOne:{                filter : data.query,                update : data.update            }        };        feeRateFileTask.push(task);    }    if(data.action == 'copy'){//copy 费率的时候用insertOne方法        let [feeRateFile,feeRate] =await feeRate_facade.getFeeRateByID(data.query.ID);        let newFeeRateID =  uuidV1();        let newFeeRate = {            ID:newFeeRateID,            rates:feeRate.rates        };        let newFeeRateFile = {            ID:data.ID,            rootProjectID:data.rootProjectID,            userID:feeRateFile.userID,            name:data.name,            libID:feeRateFile.libID,            libName:feeRateFile.libName,            feeRateID:newFeeRateID        };        feeRateTask.push({insertOne :{document:newFeeRate}});        feeRateFileTask.push({insertOne :{document:newFeeRateFile}});    }}async function generateUnitPriceTask(data,projectID,user_id,unitPriceTask,unitPriceFileTask,mixRatioTask){    if(data.action == 'move'){        let task = {            updateOne:{                filter : data.query,                update : data.update            }        };        unitPriceFileTask.push(task);    }    if(data.action == 'copy'){        let newUnitFile = {            id:data.id,            name: data.name,            project_id:projectID,            user_id:user_id,            root_project_id: data.rootProjectID        };        unitPriceFileTask.push({insertOne :{document:newUnitFile}});        let mixRatios = await mixRatioModel.find({unit_price_file_id: data.query.id});        mixRatios = JSON.stringify(mixRatios);        mixRatios = JSON.parse(mixRatios);        for(let m of mixRatios){            delete m._id;  // 删除原有id信息            delete m.id;            m.unit_price_file_id = data.id;            m.id = await getCounterID('mix_ratio');            mixRatioTask.push(m);        }        let unitPrices = await unitPriceModel.find({unit_price_file_id: data.query.id});//unit_price_file_id        unitPrices = JSON.stringify(unitPrices);        unitPrices = JSON.parse(unitPrices);        for(let u of unitPrices){            delete u._id;  // 删除原有id信息            delete u.id;            u.unit_price_file_id = data.id;            u.id = await getCounterID('unit_price');            unitPriceTask.push(u);        }    }}async function getCounterID(collectionName){    let counterModel = new CounterModel();    return  await counterModel.getId(collectionName);}async function insertMany(datas,model) {    while (datas.length>1000){//因为mongoose限制了批量插入的条数为1000.所以超出限制后需要分批插入        let newList = datas.splice(0,1000);//一次插入1000条        await model.insertMany(newList);    }    await model.insertMany(datas);}function setProperty(Obj,updateData) {    for(let ukey in updateData){        if(_.isObject(updateData[ukey]) && _.isObject(Obj[ukey])){            setProperty(Obj[ukey],updateData[ukey]);        }else {            Obj[ukey] = updateData[ukey];        }    }}function isDef(v){    return typeof v !== 'undefined' && v !== null;}function getTotalFee(bills, feeName) {    if(!isDef(bills)){        return 0;    }    if(!isDef(bills.fees) || bills.fees.length <= 0){        return 0;    }    for(let fee of bills.fees){        if(isDef(fee.fieldName) && fee.fieldName === feeName){            return isDef(fee.tenderTotalFee)                ? fee.tenderTotalFee                : isDef(fee.totalFee)                    ? fee.totalFee                    : 0;        }    }    return 0;}function summarizeToParent(parent, child, fields = null) {    const decimal = -2;    if (!fields) {        fields = ['engineeringCost', 'subEngineering', 'measure', 'safetyConstruction', 'other', 'charge', 'tax', 'estimate'];    }    for (let field of fields) {        parent[field] = scMathUtil.roundTo(parseFloat(parent[field]) + parseFloat(child[field]), decimal);    }}function getBuildingArea(projFeature){    if(!projFeature || projFeature.length === 0){        return null;    }    for(let f of projFeature){        if(f.key === 'buildingArea'){            return f.value;        }    }    return null;}//根据单位工程ID获取经济指标信息//@param {Number}prj_id @return {Object}async function getIndexReportData(prj_id, filters) {    return await project_facade.getIndexReportData(prj_id, filters);}// 根据树结构数据排序function getSortedDataByTree(data) {    // NextSiblingID-数据映射    const mapping = {};    data.forEach(item => {        // 错误数据兼容处理        // 由于之前的导入建设项目,会导致最后的一个项目的NextSiblingID为空,为非正确的-1        // 因此把项目中NextSibling为空的数据设置为NextSiblingID为-1        if (!item.NextSiblingID) {            item.NextSiblingID = -1;        }        mapping[item.NextSiblingID] = item;    });    let lastItem = data.find(item => +item.NextSiblingID === -1);    if (!lastItem) {        return data;    }    const rst = [lastItem];    let preItem = mapping[lastItem.ID];    while (preItem) {        rst.unshift(preItem);        preItem = mapping[preItem.ID];    }    // 根据树结构排序形成的新数组与旧数组项数不同,说明树结构数据有问题(断链),则返回原本数据。    return rst.length === data.length ? rst : data;}//根据单位工程ID获取汇总信息//@param {Number}tenderID {String}summaryType @return {Object}async function getSummaryInfoByTender(tenderID, summaryType) {    const notDeleted = [{deleteInfo: null}, {'deleteInfo.deleted': false}];    let tender = await projectModel.findOne({ID: tenderID, $or: notDeleted});    let parent,        parentName,        compilationIllustration;    let summaryList = [];    if(!tender){        return null;    }    let engineering = await projectModel.findOne({ID: tender.ParentID, $or: notDeleted});    if(!engineering){        return null;    }    let project = await projectModel.findOne({ID: engineering.ParentID, $or: notDeleted});    if(!project){        return null;    }    let summaryInfo = await getSummaryInfo([project.ID]);    if(summaryType === projectType.engineering){ // 汇总到单项工程级别        parent = engineering;        let tenders = await projectModel.find({ParentID: engineering.ID, $or: notDeleted}).lean();        tenders = getSortedDataByTree(tenders);        for(let t of tenders){            if(summaryInfo[t.ID]){                summaryInfo[t.ID]['name'] = t.name ? t.name : '';                summaryList.push(summaryInfo[t.ID]);            }        }    } else { // 汇总到建设项目级别        parent = project;        let engs = await projectModel.find({ParentID: project.ID, $or: notDeleted}).lean();        engs = getSortedDataByTree(engs);        for(let e of engs){            if(summaryInfo[e.ID]){                summaryInfo[e.ID]['name'] = e.name ? e.name : '';                summaryList.push(summaryInfo[e.ID]);            }        }    }    // 项目名称    parentName = parent && parent.name                    ? parent.name                    : '';    // 编制说明    compilationIllustration = parent && parent.property && parent.property.compilationIllustration                                ? parent.property.compilationIllustration                                : '';    return {        parent: {            name: parentName,            compilationIllustration: compilationIllustration},        subList: summaryList    };}//获取单位工程的各标段费用信息(不进行汇总)async function getTendersFeeInfo(tenders) {    let IDMapping = {};    //固定清单类别与汇总金额字段映射    let flagFieldMapping = {};    flagFieldMapping[billsFlags.ENGINEERINGCOST] = 'engineeringCost';    flagFieldMapping[billsFlags.SUB_ENGINERRING] = 'subEngineering';    flagFieldMapping[billsFlags.MEASURE] = 'measure';    flagFieldMapping[billsFlags.SAFETY_CONSTRUCTION] = 'safetyConstruction';    flagFieldMapping[billsFlags.OTHER] = 'other';    flagFieldMapping[billsFlags.CHARGE] = 'charge';    flagFieldMapping[billsFlags.TAX] = 'tax';    let tenderIDs = [];    if(tenders.length > 0){        for(let tender of tenders){            tenderIDs.push(tender.ID);            IDMapping[tender.ID] = {engineeringCost: 0, subEngineering: 0, measure: 0, safetyConstruction: 0, other: 0, charge: 0, tax: 0, estimate: 0, rate: 0, buildingArea: '', perCost: ''};            IDMapping[tender.ID]['buildingArea'] = '';        }        //需要获取的清单固定类别综合合价:工程造价、分部分项、措施项目、安全文明施工专项、规费、其他项目、税金        let needFlags = [billsFlags.ENGINEERINGCOST, billsFlags.SUB_ENGINERRING, billsFlags.MEASURE,            billsFlags.SAFETY_CONSTRUCTION, billsFlags.CHARGE, billsFlags.OTHER, billsFlags.TAX];        //获取单位工程汇总金额需要用到的所有清单        let allBills = await billsModel.find({projectID: {$in: tenderIDs}, 'flags.flag': {$in: needFlags}, $or: notDeleted},            '-_id projectID fees flags');        //进行单位工程级别的汇总        for(let bills of allBills){            let billsFlag = bills.flags[0]['flag'];            let costField = flagFieldMapping[billsFlag];            IDMapping[bills.projectID][costField] = getTotalFee(bills, 'common');            //暂估合价(工程造价暂估合价)            if (billsFlag === billsFlags.ENGINEERINGCOST){                IDMapping[bills.projectID]['estimate'] = getTotalFee(bills, 'estimate');            }        }        //占造价比例、单方造价        for(let tender of tenders){            let tenderInfo = IDMapping[tender.ID];            tenderInfo.rate = '';            //单方造价            tenderInfo.perCost = '';        }    }    return IDMapping;}async function getSummaryInfo(projectIDs, feeFields = null, engineeringCostFields = null){    function initFees(obj, feeFields, engineeringCostFields) {        for (let data of feeFields) {            obj[data.v] = 0;        }        for (let data of engineeringCostFields) {            obj[data.k] = 0;         }    }    if (!feeFields) {        feeFields = [            {k: billsFlags.ENGINEERINGCOST, v: 'engineeringCost'},            {k: billsFlags.SUB_ENGINERRING, v: 'subEngineering'},            {k: billsFlags.MEASURE, v: 'measure'},            {k: billsFlags.SAFETY_CONSTRUCTION, v: 'safetyConstruction'},            {k: billsFlags.OTHER, v: 'other'},            {k: billsFlags.CHARGE, v: 'charge'},            {k: billsFlags.TAX, v: 'tax'}        ];    }    // 项目总造价的各个费用,k为汇总的字段,v为工程造价的费用字段    if (!engineeringCostFields) {        engineeringCostFields = [{ k: 'estimate', v: 'estimate' }];    }    //ID与汇总信息映射    let IDMapping = {};    //固定清单类别与汇总金额字段映射    let flagFieldMapping = {};    for (let data of feeFields) {        flagFieldMapping[data.k] = data.v;    }    let projects = await projectModel.find({ID: {$in : projectIDs}, projType: projectType.project, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});    //设置建设项目的总建筑面积    for(let project of projects){        let grossArea = '';        if(project.property && project.property.basicInformation){            for(let basicInfo of project.property.basicInformation){                if(basicInfo.key === 'basicInfo'){                    for(let k of basicInfo.items){                        if(k.key === 'grossArea'){                            grossArea = k.value;                        }                    }                }            }        }        IDMapping[project.ID] = {rate: 0, buildingArea: grossArea, perCost: ''};        initFees(IDMapping[project.ID], feeFields, engineeringCostFields);        console.log(IDMapping[project.ID]);    }    //单项工程    let engineerings = await projectModel.find({ParentID: {$in : projectIDs}, projType: projectType.engineering, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});    let tenders = [];    let engIDs = [];        for(let eng of engineerings){        engIDs.push(eng.ID);        IDMapping[eng.ID] = {rate: 0, buildingArea: '', perCost: ''};        initFees(IDMapping[eng.ID], feeFields, engineeringCostFields);    }    //单位工程    if(engIDs.length > 0){        tenders = await projectModel.find({ParentID: {$in : engIDs}, projType: projectType.tender, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});    }    let tenderIDs = [];    if(tenders.length > 0){        for(let tender of tenders){            tenderIDs.push(tender.ID);            IDMapping[tender.ID] = {rate: 0, buildingArea: '', perCost: '', changeMark:tender.changeMark,property:tender.property};            initFees(IDMapping[tender.ID], feeFields, engineeringCostFields);            let buildingArea = getBuildingArea(tender.property.projectFeature);            if(buildingArea){                IDMapping[tender.ID]['buildingArea'] = buildingArea;            }        }        //需要获取的清单固定类别综合合价:工程造价、分部分项、措施项目、安全文明施工专项、规费、其他项目、税金...        let needFlags = feeFields.map(data => data.k);        //获取单位工程汇总金额需要用到的所有清单        let allBills = await billsModel.find({projectID: {$in: tenderIDs}, 'flags.flag': {$in: needFlags}, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]},                                            '-_id projectID fees flags');        //进行单位工程级别的汇总        for(let bills of allBills){            let billsFlag = bills.flags[0]['flag'];            let costField = flagFieldMapping[billsFlag];            IDMapping[bills.projectID][costField] = getTotalFee(bills, 'common');            // 工程造价各费用            if (billsFlag === billsFlags.ENGINEERINGCOST){                engineeringCostFields.forEach(({k, v}) => {                    IDMapping[bills.projectID][k] = getTotalFee(bills, v);                });            }        }        let summaryFields = [...feeFields, ...engineeringCostFields].map(data => data.v);        //进行单项工程级别的汇总        for(let tender of tenders){            summarizeToParent(IDMapping[tender.ParentID], IDMapping[tender.ID], summaryFields);        }        //进行建设项目级别的汇总        for(let eng of engineerings){            summarizeToParent(IDMapping[eng.ParentID], IDMapping[eng.ID], summaryFields);        }        //占造价比例、单方造价        const rateDecimal = -2;        const perCostDecimal = -2;        for(let tender of tenders){            let tenderInfo = IDMapping[tender.ID];            let engInfo = IDMapping[tender.ParentID];            tenderInfo.rate = engInfo.engineeringCost == 0 ? 0 : scMathUtil.roundTo(tenderInfo.engineeringCost * 100 / engInfo.engineeringCost, rateDecimal);            //单方造价            tenderInfo.perCost = tenderInfo.buildingArea.toString().trim() === '' || tenderInfo.buildingArea == 0                 ? tenderInfo.buildingArea.toString().trim()                 : scMathUtil.roundTo(tenderInfo.engineeringCost / tenderInfo.buildingArea, perCostDecimal);        }        for(let eng of engineerings){            let engInfo = IDMapping[eng.ID];            let projInfo = IDMapping[eng.ParentID];            engInfo.rate = !isDef(projInfo) || projInfo.engineeringCost == 0 ? 0 : scMathUtil.roundTo(engInfo.engineeringCost * 100 / projInfo.engineeringCost, rateDecimal);        }        //建设项目占造价比例及单方造价        for(let project of projects){            let projectInfo = IDMapping[project.ID];            projectInfo.rate = 100;            projectInfo.perCost = projectInfo.buildingArea.toString().trim() === '' || projectInfo.buildingArea == 0             ? projectInfo.buildingArea.toString().trim()             : scMathUtil.roundTo(projectInfo.engineeringCost / projectInfo.buildingArea, perCostDecimal);        }    }    return IDMapping;}//根据项目ID获取所属建设项目//@param {Number}projectID @return {Object}async function getConstructionProject(projectID){    function returnProject(project){        if(!project || project.projType === projectType.folder){            return null;        }        if(project.projType === projectType.project){            return project;        }    }    let project = await projectModel.findOne({ID: projectID, $or: notDeleted});    let returnV = returnProject(project);    if(returnV !== undefined){        return returnV;    }    let parent = await projectModel.findOne({ID: project.ParentID, $or: notDeleted});    returnV = returnProject(parent);    if(returnV !== undefined){        return returnV;    }    let grandParent = await projectModel.findOne({ID: parent.ParentID, $or: notDeleted});    returnV = returnProject(grandParent);    return returnV !== undefined ? returnV : null;}//获取单位工程完整目录结构//@param {Number}projectID @return {Arry}async function getFullPath(projectID) {    let fullPath = [];    let project = await projectModel.findOne({ID: projectID, $or: notDeleted}, '-_id ParentID name');    if(project){        fullPath.push(project.name);        await getParent(project.ParentID);    }    fullPath = fullPath.reverse();    return fullPath;    async function getParent(ParentID) {        if(ParentID != -1){            let parent = await projectModel.findOne({ID: ParentID, $or: notDeleted}, '-_id ParentID name');            if(parent){                fullPath.push(parent.name);                await getParent(parent.ParentID);            }        }    }}// 获取项目链上,自身、父项、爷爷项...的IDasync function getUpChainIDs(projectID) {    const rst = [];    while (projectID != -1) {        rst.push(projectID);        const project = await projectModel.findOne({ ID: projectID }, '-_id ParentID').lean();        projectID = project ? project.ParentID : -1;    }    return rst;}//获取projectIDs文件下所有子项目(不包括projectIDs本身)async function getPosterityProjects(projectIDs) {    let rst = [];    async function getProjects(parentIDs) {        if (parentIDs.length > 0) {            let newIDs = [];            let projs = await projectModel.find({ParentID: {$in: parentIDs}, $or: notDeleted}, '-_id').lean();            for (let proj of projs) {                // 兼容旧的错误数据,可能ParentID和ID相同                if (parentIDs.includes(proj.ID)) {                    continue;                }                rst.push(proj);                newIDs.push(proj.ID);            }            await getProjects(newIDs);        }    }    await getProjects(projectIDs);    return rst;}//根据项目获得分享信息(分享层级不一定在项目级)(以最新为准)async function getShareInfo(userID, projectID) {    // 接受到的分享项目    const upChainIDs = await getUpChainIDs(projectID);    const shareList = await getShareList({receiver: userID, projectID: {$in: upChainIDs}});    // 返回最新的分享    //return shareList.sort((a, b) => Date.parse(b.shareDate) - Date.parse(a.shareDate))[0];    return shareList.sort((a, b) => Date.parse(b.updateDate) - Date.parse(a.updateDate))[0];}//打开的单位工程是否是被分享的.async function isShare(userId, project){    //判断是否是打开分享的项目,属于分享文件的子项也算    while (project) {        for(let shareData of project.shareInfo){            if(shareData.userID === userId){                return true;            }        }        project = await projectModel.findOne({ID: project.ParentID}, '-_id shareInfo ID ParentID');    }    return false;}//用户是否第一次进入费用定额async function isFirst(userId, compilationId) {    let userData = await userModel.findOne({_id: mongoose.Types.ObjectId(userId)}, '-_id used_list');    let first = false;    if (userData) {        first = !_.find(userData.used_list, function (o) {            return o.compilationId === compilationId;        });;    }    return first;}async function importChongqingProject(data) {    let log_data = {        key:data.key,        userID:data.user_id,        status:"start",        create_time:+new Date()    };    await importLogsModel.create(log_data);    doImport(data.user_id,data.session.sessionCompilation._id,data.session.sessionCompilation.adProjects,data.key);     return "start importing";    async function doImport(user_id,compilationId,projectIDs,key) {        let doc = {status:"finish"};        try {           let r = await copyExample(user_id,compilationId,projectIDs);           if(r == false){               doc.errorMsg = "导入失败,请检查项目是否存在!";               doc.status = "error";           }        }catch (error){            console.log(error);            doc.errorMsg = "导入失败,请检查项目是否存在!";            doc.status = "error";        }finally {            await importLogsModel.update({key:key},doc);        }    }}//用户第一次进入费用定额的数据准备async function prepareInitialData(userId, compilation, example) {    let first = await isFirst(userId, compilation);    if (first) {        await updateUsedList(userId, compilation);        let prepareTask = [            copyCompleRationSection(userId, compilation),            copyCompleGljSection(userId, compilation)        ];        if (example && example.length > 0) {            prepareTask.push(copyExample(userId, compilation, example));        }        await Promise.all(prepareTask);    }}async function updateUsedList(userId, compilation) {    await userModel.update({_id: mongoose.Types.ObjectId(userId)}, {$push: {used_list: {compilationId: compilation}}});}//拷贝补充定额章节树async function copyCompleRationSection(userId, compilationId) {    await sectionTreeDao.copyDataFromTemplate(userId, compilationId);}//拷贝补充人材机分类树async function copyCompleGljSection(userId, compilationId) {    let templateData = await compleGljSectionTModel.find({compilationId: compilationId});    if (templateData.length > 0) {        let insertDatas = [],            uuidMapping = {};        //将ID替换成UUID        for (let temData of templateData) {            uuidMapping[temData.ID] = uuidV1();        }        for(let temData of templateData) {            let insertD = {                userId: userId,                compilationId: compilationId,                Name: temData.Name,                ID: uuidMapping[temData.ID],                ParentID: uuidMapping[temData.ParentID] || -1,                NextSiblingID: uuidMapping[temData.NextSiblingID] || -1,            };            insertDatas.push(insertD);        }        //插入数据        await compleGljSectionModel.insertMany(insertDatas);    }}async function changeFile(datas,userID,fileID,name,from,type){//from 费率或单价文件,type从单前建设项目还是从其它建设项目中复制    let projectIDs = [],newFile = {id:fileID,name:name};//计录从其它项目中复制的文件,选中多个的时候只需复制一次    let projectUpdateType = from == "feeRateFile"?"feeRate":"unitFile";    for(let d of datas){        let tem_file = from == "feeRateFile"? await feeRate_facade.changeFeeRateFile(d, newFile,type,userID):await glj_facade.changeUnitFile(d, newFile,type,userID);        if(type == 1 && tem_file){//从建设项目复制时,只有第一次需要复制,剩下的就相当于使用同个建设项目的情况了            let newID =  from == "feeRateFile"?tem_file.ID:tem_file.id;            newFile = {id:newID,name:tem_file.name};            type = 0;        }        projectIDs.push({ID:d.projectID})    }    await project_facade.markProjectsToChange(projectIDs,projectUpdateType)//项目标记为待刷新状态}//获取费用定额绑定的基本信息库数据async function getBasicInfo(compilationID, fileKind = null) {    let compilation = await compilationModel.findOne({_id: mongoose.Types.ObjectId(compilationID)});    if (!compilation) {        return null;    }    let billValuation = compilation.bill_valuation.find(function (data) {        return data.enable;    });    if (!billValuation) {        return null;    }    let engineerings = await engineeringModel.find({valuationID: billValuation.id});    let engineering = engineerings.find(function (data) {        return data.info_lib && data.info_lib.length > 0;    });    if (!engineering || !engineering.info_lib || !engineering.info_lib[0]) {        return null;    }    let infoLib = await basicInfoModel.findOne({ID: engineering.info_lib[0].id});    //提取文件类型中需要的数据 (投标项目可能不需要招标的一些信息)    if (fileKind && infoLib && infoLib.info && infoLib.info.length) {        let strMap = {            1: 'tender',    //投标            2: 'bid',       //招标            3: 'control'    //控制价        };        let needfulData = infoLib.info.filter(data => !data.fileKind || data.fileKind === strMap[fileKind]);        needfulData.forEach(nData => {            let needfulSub = nData.items.filter(sData => !sData.fileKind || sData.fileKind === strMap[fileKind]);            nData.items = needfulSub;        });        infoLib.info = needfulData;    }    return infoLib;}async function getProjectFeature(valuationID, engineeringName, feeName) {    let engineering = await engineeringModel.findOne({valuationID: valuationID, name: engineeringName, feeName: feeName});    if (!engineering || !engineering.feature_lib || !engineering.feature_lib[0]) {        return null;    }    let featureLib = await projectFeatureModel.findOne({ID: engineering.feature_lib[0].id});    return featureLib;}//根据单位工程,获取建设项目-单项工程-单位工程,不包含无单位工程的单项工程//@param {Number}tenderID(单位工程ID) {Number}granularity(颗粒度 1:建设项目 2:单项工程 3:单位工程)async function getProjectByGranularity(tenderID, granularity, summaryObj, userID, versionName) {    const GRANULARITY = {        PROJECT: 1,        ENGINEERING: 2,        TENDER: 3    };    let theTender = await projectModel.findOne({userID: userID, ID: tenderID}).lean(); //加上session的userID,防止tenderID被篡改过    if (!theTender) {        return null;    }    let theEngineering = await projectModel.findOne({ID: theTender.ParentID}).lean();    if (!theEngineering) {        return null;    }    let constructionProject = await projectModel.findOne({ID: theEngineering.ParentID}).lean();    if (!constructionProject) {        return null;    }    let engineerings;    if (granularity === GRANULARITY.PROJECT) {        engineerings = await projectModel.find({ParentID: constructionProject.ID, $or: notDeleted}).lean();        for (let eng of engineerings) {            eng.children = await projectModel.find({ParentID: eng.ID, $or: notDeleted}).lean();        }    } else {        engineerings = [theEngineering];        if (granularity === GRANULARITY.ENGINEERING) {            theEngineering.children = await projectModel.find({ParentID: theEngineering.ID, $or: notDeleted}).lean();        } else {            theEngineering.children = [theTender];        }    }    constructionProject.children = engineerings;    //获取汇总信息    const { feeFields, engineeringCostFields } = summaryObj;    constructionProject.summaryInfo = await getSummaryInfo([constructionProject.ID], feeFields, engineeringCostFields);    //获取编制软件信息: 软件公司;软件名;版本号;授权信息; base64    let product = await productModel.findOne({});    let company = product.company || '珠海纵横创新软件有限公司',        version = product.version || '';    constructionProject.softInfo = `${company};${versionName};${version};${userID}`;    return constructionProject;}//获取项目数据占位IDasync function getProjectPlaceholder(data) {    let rst = {};    //项目本身数据    if (data.projectCount) {        let projectCount = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.project, data.projectCount);        rst.project = projectCount.sequence_value - data.projectCount + 1;    }    //项目人材机数据    if (data.projectGLJCount) {        let projectGLJCount =  await counter.counterDAO.getIDAfterCountSync(counter.moduleName.glj_list, data.projectGLJCount);        rst.projectGLJ = projectGLJCount.sequence_value - data.projectGLJCount + 1;    }    //组成物数据    if (data.ratioCount) {        let ratioCount = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.mix_ratio, data.ratioCount);        rst.ratio = ratioCount.sequence_value - data.ratioCount + 1;    }    //单价文件数据    if (data.unitPriceFileCount) {        let unitPriceFileCount = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.unit_price_file, data.unitPriceFileCount);        rst.unitPriceFile = unitPriceFileCount.sequence_value - data.unitPriceFileCount + 1;    }    //单价数据    if (data.unitPriceCount) {        let unitPriceCount = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.unit_price, data.unitPriceCount);        rst.unitPrice = unitPriceCount.sequence_value - data.unitPriceCount + 1;    }    return rst;}/** 接口导入 项目详细数据都导入完成了,再生成项目数据(项目管理界面数据)* */async function importProject(importObj, userID, compilationID) {    let toInsertProjects = [importObj];  //待新增项目数据    await setupProject(importObj);    //设置项目ID及相关数据    for (let curEng of importObj.engs) {        await setupProject(curEng);        toInsertProjects.push(curEng);        for (let curTender of curEng.tenders) {            await setupProject(curTender.tender);            //插入单位工程的详细数据            await importTenderDetail(curTender);            toInsertProjects.push(curTender.tender);        }        delete curEng.tenders;    }    delete importObj.engs;    //项目内部数据设置、新增完毕后,插入项目本身的数据,更新前节点数据    let bulks = [];    //如果有前节点,更新前节点    if (importObj.preID !== -1) {        bulks.push({            updateOne: {filter: {ID: importObj.preID}, update: {$set: {NextSiblingID: importObj.ID}}}        });    }    for (let insertP of toInsertProjects) {        bulks.push({            insertOne: {document: insertP}        });    }    if (bulks.length > 0) {        await projectModel.bulkWrite(bulks);    }    let summaryInfo = await getSummaryInfo([importObj.ID]);    //设置汇总字段    for(let proj of toInsertProjects){        let summaryProj = summaryInfo[proj.ID];        if(summaryProj){            proj.engineeringCost = summaryProj.engineeringCost;            proj.subEngineering = summaryProj.subEngineering;            proj.measure = summaryProj.measure;            proj.safetyConstruction = summaryProj.safetyConstruction;            proj.other = summaryProj.other;            proj.charge = summaryProj.charge;            proj.tax = summaryProj.tax;            proj.rate = summaryProj.rate;            proj.buildingArea = summaryProj.buildingArea;            proj.perCost = summaryProj.perCost;        }    }    return toInsertProjects;    //给项目数据设置一些需要的数据    async function setupProject(data) {        data.userID = userID;        data.compilation = compilationID;        data.fileVer = G_FILE_VER;        data.createDateTime = new Date();        if (data.projType === 'Project') {            await setupConstruct(data);        } else if (data.projType === 'Tender') {            await setupTender(data);        }    }    //给建设项目设置一些数据    async function setupConstruct(data) {        //更新基本信息, 按照key匹配        let infoLib = await getBasicInfo(data.compilation, data.property.fileKind);        if (infoLib) {            //标准基本信息库的数据打平            let flatDatas = [];            infoLib.info.forEach(ifData => flatDatas.push(...ifData.items));            data.basicInformation.forEach(importI => {                flatDatas.forEach(item => {                    if (item.key === 'engineeringName') {                        item.value = data.name;                    } else if (item.key === 'fileKind') {                        item.value = {'1': '投标', '2': '招标'}[data.property.fileKind]                    } else if (item.key === 'taxModel') {                        item.value = {'1': '一般计税法', '2': '简易计税法'}[data.property.taxType]                    }                    if (item.key === importI.key) {                        item.value = importI.value;                    }                });            });        }        data.property.basicInformation = infoLib ? infoLib.info : [];    }    //给单位工程设置一些数据    async function setupTender(data) {        if (!data.property.decimal) {                 //小数位数 需要修改,所以深拷贝            data.property.decimal = JSON.parse(JSON.stringify(defaultDecimal));            // 定额工程量、人材机消耗量精度设为最大6            data.property.decimal.ration.quantity = 6;            data.property.decimal.glj.quantity = 6;        }        //清单工程量精度 需要修改,所以深拷贝        data.property.billsQuantityDecimal = JSON.parse(JSON.stringify(billsQuantityDecimal));        // 清单工程量精度默认为4        const billsQuantityDecimalValue = data.property.billsQuantityDecimalValue || 4;        data.property.billsQuantityDecimal.forEach(data => data.decimal = billsQuantityDecimalValue);        //呈现选项        data.property.displaySetting = displaySetting;        data.property.billsCalcMode = 0;        data.property.zanguCalcMode = 0;        //计算选项        data.property.calcOptions = calcOptions;        //安装增加费        if(data.property.isInstall === true || data.property.isInstall === 'true'){//判断是否安装工程            await installationFacade.copyInstallationFeeFromLib(data.ID, data.property.engineering_id);        }        //锁定清单        data.property.lockBills = true;        //工料机单价调整系数        data.property.tenderSetting = tenderSetting;        //工程特征相关更新 (配对更新+追加)        let featureLib = await getProjectFeature(data.property.valuation, data.property.engineeringName, data.property.feeStandardName);        if (featureLib) {            //把导入的数据设置到默认生成的数据中,按名称匹配 (导入项不确定,因此无法确定获取项的key)            // 只匹配第一层            const unmatched = [];            data.projectFeature.forEach(importF => {                let matchData = featureLib.feature.find(f => f.dispName === importF.dispName);                if (matchData) {                    matchData.value = importF.value;                    if (importF.items) {                        matchData.items = importF.items;                    }                } else {                    unmatched.push(importF);                }            });            featureLib.feature.push(...unmatched);            //设置工程专业的值为费用标准的值..            featureLib.feature.forEach(f => {                if (f.key === 'engineering') {                    f.value = data.property.feeStandardName;                }            });        }        data.property.projectFeature = featureLib ? featureLib.feature : [];        // 指标信息相关设置        await project_facade.setSEILibData(data.property);    }}//插入单位工程内部详细数据async function importTenderDetail(tenderData) {    //单价文件    let upFile = {        id: tenderData.tender.property.unitPriceFile.id,        name: tenderData.tender.name,        project_id: tenderData.tender.ID,        user_id: tenderData.tender.userID,        root_project_id: tenderData.tender.property.rootProjectID    };    await unitPriceFileModel.create(upFile);    //费率文件    let feeRateFileID = await feeRate_facade.newFeeRateFile(tenderData.tender.userID, tenderData.tender);    tenderData.tender.property.feeFile = feeRateFileID ? feeRateFileID : -1;    //人工系数文件    let lcFile = await labourCoeFacade.newProjectLabourCoe(tenderData.tender);    tenderData.tender.property.labourCoeFile = lcFile ? lcFile : null;    let cpFile = await calcProgramFacade.newProjectCalcProgramFile(tenderData.tender);    tenderData.tender.property.calcProgramFile = cpFile ? cpFile : null;    //列设置    let engineeringModel = new EngineeringLibModel();    let engineering = await engineeringModel.getEngineering(tenderData.tender.property.engineering_id);    let mainTreeCol = await mainColLibModel.findOne({ID: tenderData.tender.property.colLibID});    await projectSettingModel.create({projectID: tenderData.tender.ID, main_tree_col: mainTreeCol.main_tree_col, glj_col: engineering.glj_col});    //清单    if (tenderData.bills.length) {        await billsModel.insertMany(tenderData.bills);    }    // 工程量明细    if (tenderData.quantityDetails && tenderData.quantityDetails.length) {        await quantityDetailModel.insertMany(tenderData.quantityDetails);    }    //投标文件中,才会有下面这些数据    if (enterDetail(tenderData)) {        //匹配标准数据,更新一些标准数据        await setupStdData(tenderData);        let task = [];        //定额        if (tenderData.ration.length) {            task.push(rationModel.insertMany(tenderData.ration))        }        //定额人材机        if (tenderData.rationGLJ.length) {            task.push(rationGLJModel.insertMany(tenderData.rationGLJ));        }        //定额调整系数        if (tenderData.rationCoe.length) {            task.push(rationCoeModel.insertMany(tenderData.rationCoe));        }        //项目人材机        if (tenderData.projectGLJ.length) {            task.push(gljListModel.insertMany(tenderData.projectGLJ));        }        // 承包人材料        if (tenderData.contractorList.length) {           task.push(contractorListModel.insertMany(tenderData.contractorList));        }        // 评标材料表        if (tenderData.bidEvaluationList.length) {            task.push(bidListModel.insertMany(tenderData.bidEvaluationList));        }        // 暂估价材料表        if (tenderData.evaluationList.length) {            task.push(evaluateListModel.insertMany(tenderData.evaluationList));        }        //组成物        if (tenderData.mixRatio.length) {            task.push(mixRatioModel.insertMany(tenderData.mixRatio));        }        //单价文件        if (tenderData.unitPrice.length) {            task.push(unitPriceModel.insertMany(tenderData.unitPrice));        }        await Promise.all(task);    }    //继续处理定额等数据    function enterDetail(tenderData) {        return tenderData.ration.length ||            tenderData.rationGLJ.length ||            tenderData.projectGLJ.length ||            tenderData.mixRatio.length ||            tenderData.unitPrice.length;    }}//匹配标准数据,需要匹配标准定额,更新导入的定额人材机的定额消耗量等数据async function setupStdData(tenderData) {    //获取标准定额数据 (定额子目的编码查找)    let matchRationCodes = [...new Set(tenderData.ration.map(ration => ration.code))];    if (!matchRationCodes.length) {        return;    }    //限制在当前费用定额可用的定额库里查找    let stdRations = await stdRationItemModel.find({code: {$in: matchRationCodes}, rationRepId: {$in: tenderData.tender.rationLibIDs}},                                                    '-_id -rationAssList -rationCoeList -rationInstList');    //标准定额code - 映射    let stdRationCodeMap = {};    stdRations.forEach(stdRation => stdRationCodeMap[stdRation.code] = stdRation);    //获取标准人材机数据 (人材机汇总的编码查找)    let matchGLJCodes = [...new Set(tenderData.projectGLJ.map(pGLJ => pGLJ.original_code))];    if (!matchGLJCodes.length) {        return;    }    //限制在当前费用定额可用的人材机库里查找    let stdGLJs = await stdGljItemModel.find({code: {$in: matchGLJCodes}, repositoryId: {$in: tenderData.tender.gljLibIDs}},                                             '-_id -component -priceProperty');    //标准人材机code - 映射    let stdGLJCodeMap = {};    stdGLJs.forEach(stdGLJ => stdGLJCodeMap[stdGLJ.code] = stdGLJ);    //更新定额数据    tenderData.ration.forEach(r => {        let stdRation = stdRationCodeMap[r.code];        if (stdRation) {            r.caption = stdRation.caption;            r.from = 'std';            r.libID = stdRation.rationRepId;            r.stdID = stdRation.ID;            r.prefix = stdRation.rationRepId == tenderData.tender.defaultRationLib ? '' : '借';            r.content = stdRation.jobContent;            r.manageFeeRate = stdRation.manageFeeRate;            if (stdRation.feeType) {                r.programID = stdRation.feeType;            } else {                // 是标准定额,但是该标准定额取费专业为空,取定额取费专业(后台配置项)                r.programID = tenderData.tender.property.engineering;            }        } else {            r.from = 'cpt';            r.prefix = '补';        }    });    //更新定额人材机数据    tenderData.rationGLJ.forEach(rGLJ => {        rGLJ.from = 'cpt';        //匹配定额        let stdRation = stdRationCodeMap[rGLJ.rationCode];        if (stdRation) {            //根据导入数据的定额人材机编码匹配人材机            let stdGLJ = stdGLJCodeMap[rGLJ.original_code];            if (stdGLJ) {                rGLJ.from = 'std';                rGLJ.GLJID = stdGLJ.ID;                rGLJ.type = stdGLJ.gljType;                rGLJ.shortName = stdGLJ.shortName;                rGLJ.repositoryId = stdGLJ.repositoryId;                rGLJ.model = stdGLJ.model;                rGLJ.adjCoe = stdGLJ.adjCoe;                //找匹配到的标准定额中定额人材机的相应人材机,更新定额消耗                let stdRationGLJ = stdRation.rationGljList.find(data => data.gljId === stdGLJ.ID);                if (stdRationGLJ) {                    rGLJ.rationItemQuantity = stdRationGLJ.consumeAmt;                }            }        }    });    //更新人材机汇总数据    tenderData.projectGLJ.forEach(pGLJ => {        if (!pGLJ.specs) {            pGLJ.specs = null;        }        let stdGLJ = stdGLJCodeMap[pGLJ.original_code];        if (stdGLJ) {            pGLJ.glj_id = stdGLJ.ID;            pGLJ.model = stdGLJ.model;            if (pGLJ.type !== stdGLJ.gljType) { //更新组成物connect_key                let keyStr = [pGLJ.code || 'null', pGLJ.name || 'null', pGLJ.specs || 'null', pGLJ.unit || 'null', pGLJ.type].join('|-|');                let ratios = tenderData.mixRatio.filter(ratio =>                    ratio.connect_key === keyStr);                let newKeyStr = [pGLJ.code || 'null', pGLJ.name || 'null', pGLJ.specs || 'null', pGLJ.unit || 'null', stdGLJ.gljType || 'null'].join('|-|');                ratios.forEach(ratio => ratio.connect_key = newKeyStr);            }            pGLJ.type = stdGLJ.gljType;  //更新类型,标准的数据类型更准确,导入的类型数据有细分识别不了        }    });    //更新单价文件数据    tenderData.unitPrice.forEach(up => {        let stdGLJ = stdGLJCodeMap[up.original_code];        if (stdGLJ) {            up.glj_id = stdGLJ.ID;            up.type = stdGLJ.gljType;            up.short_name = stdGLJ.shortName;            if (stdGLJ.taxRate) {                up.taxRate = stdGLJ.taxRate;            }        }    });    //更新组成物数据    tenderData.mixRatio.forEach(ratio => {        let stdGLJ = stdGLJCodeMap[ratio.code];        if (stdGLJ) {            ratio.glj_id = stdGLJ.ID;            ratio.type = stdGLJ.gljType;        }    });}async function exportProject(userID,data){//导出建设项目    if(data.type == 'main'){        return await exportMainData(userID,data.projectID);    }else {        return await  exportTenderData(data);    }}async function exportMainData(userID,projectID) {    let result = {userID:userID,projects:[],type:'Project'};//type 备用属性,表示导出的类型,目前导出的都是整个建设项目    let tenderIDs = [];    let project = await projectModel.findOne({ID:projectID});    if(!project) throw  new Error("没有找到该建设项目:"+projectID);    result['compilationID'] = project.compilation;    result['from'] = "construction";    result.projects.push(project);    let subProjects = await  projectModel.find({"$or": [{'ParentID':projectID}, {"property.rootProjectID": projectID}]});    for(let s of subProjects){        if(!s.deleteInfo || !s.deleteInfo.deleted){            result.projects.push(s);            if(s.projType =="Tender") tenderIDs.push(s.ID);        }    }    let files = {unitFiles:await exportUnitFiles(projectID),feeRateFiles:await exportFeeRateFiles(projectID)};    result.files = files;    result = cipher.aesEncrypt(JSON.stringify(result));    result +="|----|" +JSON.stringify(tenderIDs);    return result;}async function exportUnitFiles(rootProjectID){    let unitFiles = [];    let files = await unitPriceFileModel.find({root_project_id:rootProjectID});    for(let f of  files){        if(f.deleteInfo && f.deleteInfo.deleted == true) continue;        let tem = {unitFile:f};        tem.unitPrices = await unitPriceModel.find({unit_price_file_id:f.id});        tem.mixRatios = await mixRatioModel.find({unit_price_file_id:f.id});        unitFiles.push(tem);    }    return unitFiles;}async function exportFeeRateFiles(rootProjectID) {    let feeRateFiles = [];    let files = await feeRateFileModel.find({rootProjectID:rootProjectID});    for(let f of files){        if(f.deleteInfo && f.deleteInfo.deleted == true) continue;        let tem = {feeRateFile:f};        tem.feeRate = await feeRateModel.findOne({ID:f.feeRateID});        feeRateFiles.push(tem);    }    return feeRateFiles;}async function exportTenderData(data){    let result = {};    let projectSetting =  await projectSettingModel.findOne({"projectID": data.projectID}, '-_id');    if(projectSetting) result['projSetting'] = projectSetting;    result.bills = await billsModel.find({"projectID": data.projectID});    result.rations = await rationModel.find({'$or': [{projectID: data.projectID, deleteInfo: null}, {projectID: data.projectID, 'deleteInfo.deleted': {$in: [null, false]}}]});    result.projectGLJs = await gljListModel.find({'project_id':data.projectID});    result.installationFees =  await installationFeeModel.find({projectID:data.projectID});    result.rationGLJs = await rationGLJModel.find({projectID:data.projectID});    result.rationCoes = await rationCoeModel.find({projectID:data.projectID});    result.quantityDetails = await quantityDetailModel.find({projectID:data.projectID});    result.rationInstallations = await rationInstallationModel.find({projectID:data.projectID});    result.rationTemplates = await rationTemplateModel.find({projectID:data.projectID});    result.calcProgramsFile = await calcProgramsModel.findOne({projectID:data.projectID});    result.labourCoes = await labourCoesModel.findOne({projectID:data.projectID});    result.evaluateList = await evaluateListModel.find({projectID:data.projectID}, '-_id').lean();    result.bidList = await bidListModel.find({projectID:data.projectID}, '-_id').lean();    result.contractorList = await contractorListModel.find({projectID:data.projectID}, '-_id').lean();    return cipher.aesEncrypt(JSON.stringify(result));}async function downLoadProjectFile(info) {    let result = {error:0};    let bucketManager2 = new qiniu.rs.BucketManager(mac, null);    let publicBucketDomain = qiniu_config.Domain;// "http://serverupdate.smartcost.com.cn";//这里不支持https    let deadline = parseInt(Date.now() / 1000) + 3600; // 1小时过期    let privateDownloadUrl = bucketManager2.privateDownloadUrl(publicBucketDomain, info.key, deadline);    console.log(privateDownloadUrl);    let data = {        key:info.key,        userID:info.session.sessionUser.id,        status:"start",        create_time:+new Date()    };    await importLogsModel.create(data);    doDownLoadAndImport(privateDownloadUrl,info);    return result;}async function doDownLoadAndImport(privateDownloadUrl,info) {    let stream = fs.createWriteStream(path.join(__dirname, "../../../tmp/"+info.key));//    request(privateDownloadUrl).pipe(stream).on("close", async function (err) {        console.log("文件[" + info.key + "]下载完毕");        let doc = {status:"finish"};        try {            let data = fs.readFileSync(stream.path,'utf-8');            let result = await importProjects(data,{session:info.session},info.updateData);            if(result.error == 1){                doc.errorMsg = result.msg;                doc.status = "error";            }        }catch (error){            console.log(error);            doc.errorMsg = "导入失败,请检查文件!";            doc.status = "error";        }finally {            await importLogsModel.update({key:info.key},doc);            fs.unlinkSync(stream.path);        }    });}async function importProcessChecking(data){    let result = {error:0};    let log = await importLogsModel.findOne({key:data.key});    if(log){        if(log.status == "finish"){            result.status = "complete";            await importLogsModel.remove({key:data.key});        }else if(log.status == "start"){            result.status = "processing";        }else if(log.status == "error"){            result.error = 1;            result.msg = log.errorMsg;            await importLogsModel.remove({key:data.key});        }    }else {        result.error = 1;        result.msg = `导入过程中发生错误!`;    }    return result;}// 从cdn服务器下载文件,key就是文件名async function downloadFileSync(key) {    const filePath = path.join(__dirname, '../../../tmp/', key);    const bucketManager2 = new qiniu.rs.BucketManager(mac, null);    const publicBucketDomain = qiniu_config.Domain; // "http://serverupdate.smartcost.com.cn";//这里不支持https    const deadline = parseInt(Date.now() / 1000) + 3600; // 1小时过期    const privateDownloadUrl = bucketManager2.privateDownloadUrl(publicBucketDomain, key, deadline);    const stream = fs.createWriteStream(filePath);    return new Promise((resolve, reject) => {        request(privateDownloadUrl)            .pipe(stream)            .on('close', () => {                // 读取文件返回字符串                const srcData = fs.readFileSync(stream.path,'utf-8');                resolve({                    path: stream.path,                    srcData: srcData                });            })            .on('error', reject);    });}// 导入接口async function importInterface(key, session) {    // 源文件内容文本    let downloadFilePath = '';    try {        const { path, srcData } = await downloadFileSync(key);        downloadFilePath = path;        if (!srcData) {            throw '无有效数据';        }        const userID = session.sessionUser.id;        const compilationID = session.sessionCompilation._id;        const importData = JSON.parse(srcData);        let  tenderCount = 0;        importData.engs.forEach(eng => {            eng.tenders.forEach(tender => {                tenderCount += 1;            });        });        if (await isTenderOverrun(tenderCount, session)) {            throw '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。';        }        const projectData = await importProject(importData, userID, compilationID);        return projectData;    } catch (err) {        throw err;    } finally {        fs.unlinkSync(downloadFilePath);    }}async function importProjects(data,req,updateData) {    let result = {error:0};    let stringArr = data.split("|----|");    let datas = [];    for(let s of stringArr){        datas.push(JSON.parse(cipher.aesDecrypt(s)));    }    let mainData = datas.length > 0 ?datas[0]:null;    if(mainData){        if(mainData.from && mainData.from != "construction"){            result.error = 1;            result.msg = "导入失败:您要导入的文件是由“纵横公路养护云版”导出,当前软件是“大司空云计价”,请选择正确的软件再进行操作!";        }else if(mainData.compilationID != req.session.sessionCompilation._id){            const fileCompilation = await compilationModel.findOne({_id: mongoose.Types.ObjectId(mainData.compilationID)}, 'name');            const fileCompilationName = fileCompilation ? fileCompilation.name : '未知的费用定额';            const curCompilationName = req.session.sessionCompilation.name;            result.error = 1;            result.msg = `导入失败:您要导入的文件是由“${fileCompilationName}”导出,当前软件是“${curCompilationName}”,请选择正确的费用定额再进行操作!`;        }else {            const tenders = mainData.projects.filter(item => item.projType === projectType.tender);            const tenderOverrun = await isTenderOverrun(tenders.length, req.session);            if (tenderOverrun) {                result.error = 1;                result.msg = `您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。`;            }            let [projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);            if(datas.length > 1 ){                for(let i = 1;i<datas.length;i++){                    await handleEachProject(datas[i],projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap)                }            }        }    }    return result;}async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap){    let bills = [],rations = [],projectGLJs = [],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[],evaluateList=[],bidList=[],contractorList=[];    let newProjectSetting =null,newCalcProgramsFile = null,newLabourCoe = null;    let billsIDMap = {},projectGLJIDMap={},rationIDMap = {};    let newProjectID = projectIDMap[data.projSetting.projectID];    //生成新的清单;    if(data.bills && data.bills.length > 0){        for(let b of data.bills){            billsIDMap[b.ID] = uuidV1();        }        for(let b of data.bills){            delete b._id;            b = getCopyBillDatas(b,newProjectID,billsIDMap);            bills.push(b);        }    }    //生成项目工料机数据    if(data.projectGLJs && data.projectGLJs.length > 0){        let gljCount = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.glj_list, data.projectGLJs.length);        for(let i = 0; i < data.projectGLJs.length; i++){            let d = data.projectGLJs[i];            delete d._id;            let newID = gljCount.sequence_value - (data.projectGLJs.length - 1) + i;            projectGLJIDMap[d.id] = newID;            d.project_id = newProjectID;            d.id = newID;            projectGLJs.push(d);        }    }    //生成新的定额    if(data.rations && data.rations.length > 0){        for(let r of data.rations){            rationIDMap[r.ID] = uuidV1();        }        for(let r of data.rations){            delete r._id;            r = getCopyRationData(r,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);            rations.push(r);        }    }    //生成定额下挂的定额工料机等等数据    if(data.rationGLJs && data.rationGLJs.length > 0) rationGLJs = setRationSubList(data.rationGLJs,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);    if(data.rationCoes && data.rationCoes.length > 0) rationCoes = setRationSubList(data.rationCoes,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);    if(data.quantityDetails && data.quantityDetails.length > 0) quantityDetails = setRationSubList(data.quantityDetails,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);    if(data.rationInstallations && data.rationInstallations.length > 0) rationInstallations = setRationSubList(data.rationInstallations,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);    if(data.rationTemplates && data.rationTemplates.length > 0) rationTemplates = setRationSubList(data.rationTemplates,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);    if(data.evaluateList && data.evaluateList.length > 0) evaluateList = setMaterialList(data.evaluateList,newProjectID,projectGLJIDMap);    if(data.bidList && data.bidList.length > 0) bidList = setMaterialList(data.bidList,newProjectID,projectGLJIDMap);    if(data.contractorList && data.contractorList.length > 0) contractorList = setMaterialList(data.contractorList,newProjectID,projectGLJIDMap);    //生成projectSetting 文件    if(data.projSetting){        data.projSetting.projectID =newProjectID;        newProjectSetting = data.projSetting;        delete newProjectSetting._id;    }    //生成计算程序文件,系数文件    if(data.calcProgramsFile && calcProgramFileIDMap[data.calcProgramsFile.ID]){        data.calcProgramsFile.ID = calcProgramFileIDMap[data.calcProgramsFile.ID];        data.calcProgramsFile.projectID = newProjectID;        newCalcProgramsFile =  data.calcProgramsFile;        delete newCalcProgramsFile._id;    }    if(data.labourCoes && labourCoeFileIDMap[data.labourCoes.ID]){        data.labourCoes.ID = labourCoeFileIDMap[data.labourCoes.ID];        data.labourCoes.projectID = newProjectID;        newLabourCoe = data.labourCoes;        delete newLabourCoe._id;    }    if(newProjectSetting) await projectSettingModel.create(newProjectSetting);    if(bills.length > 0) await insertMany(bills,billsModel);    if(rations.length > 0) await insertMany(rations,rationModel);    if(projectGLJs.length > 0) await insertMany(projectGLJs,gljListModel);    if(rationGLJs.length > 0) await insertMany(rationGLJs,rationGLJModel);    if(rationCoes.length > 0) await insertMany(rationCoes,rationCoeModel);    if(quantityDetails.length > 0) await insertMany(quantityDetails,quantityDetailModel);    if(rationInstallations.length > 0) await insertMany(rationInstallations,rationInstallationModel);    if(rationTemplates.length > 0) await insertMany(rationTemplates,rationTemplateModel);    if(evaluateList.length > 0) await insertMany(evaluateList,evaluateListModel);    if(bidList.length > 0) await insertMany(bidList,bidListModel);    if(contractorList.length > 0) await insertMany(contractorList,contractorListModel);    if(newCalcProgramsFile) await calcProgramsModel.create(newCalcProgramsFile);    if(newLabourCoe) await labourCoesModel.create(newLabourCoe);}function setMaterialList(datas,newProjectID,projectGLJIDMap){    let arrs = [];    for(let d of datas){       arrs.push(getNewMaterial(d,newProjectID,projectGLJIDMap))    }    return arrs}function setRationSubList(datas,newProjectID,billIDMap,rationIDMap,projectGLJIDMap) {    let arrs = [];    for(let d of datas){        delete d._id;        d = getCopyRationSubData(d,newProjectID,billIDMap,rationIDMap,projectGLJIDMap);        arrs.push(d);    }    return arrs;}async function handleMainProjectDatas(mainData,updateData,userID) {    let mainProjectID = -1;    let projectIDMap = {},feeRateFileIDMap={},unitPriceFileIDMap={},labourCoeFileIDMap={},calcProgramFileIDMap={};    let tasks = [];    projectIDMap[-1] = -1;//最后一个项目的nextID为-1的情况    //生成新的projectID    for(let p of mainData.projects){        let newProjectID = await getCounterID("projects");        projectIDMap[p.ID] = newProjectID;        if(p.projType == "Project") mainProjectID =  newProjectID;        if(p.projType == "Tender"){            if(p.property.calcProgramFile) calcProgramFileIDMap[p.property.calcProgramFile.ID] = uuidV1();//新的计算程序文件ID            if(p.property.labourCoeFile) labourCoeFileIDMap[p.property.labourCoeFile.ID] = uuidV1();//新的人工调整系数文件ID            if(p.property.feeFile) feeRateFileIDMap[p.property.feeFile.id] = uuidV1();//新的费率文件ID            if(p.property.unitPriceFile) unitPriceFileIDMap[p.property.unitPriceFile.id] = await getCounterID("unit_price_file");//新的单价文件ID        }    }    if(mainProjectID == -1) throw new Error("文件里面没包含建设项目信息!");    //处理项目信息  ----- 费率文件,单价文件ID信息要重新生成----to do    for(let p of mainData.projects){        delete p._id;        delete p.__v;        p.ID = projectIDMap[p.ID];        if(p.ID == mainProjectID){//对于建设项目,要父和下一节点ID要使用前端传入的位置ID            p.ParentID = updateData.self.ParentID;            p.NextSiblingID = updateData.self.NextSiblingID;            if(updateData.update){//树节构中的上一节点的下一节点设置为本建设项目ID;                tasks.push({updateOne:{filter : updateData.update.query, update : {NextSiblingID:mainProjectID}}});            }            //查看是否重名;            let temp = await projectModel.findOne({userID:userID,ParentID:p.ParentID,name:p.name});            if(temp) p.name = p.name + '(' + moment(Date.now()).tz("Asia/Shanghai").format('MM-DD HH:mm:ss') + '导入)';        }else {            p.ParentID = projectIDMap[p.ParentID];            p.NextSiblingID = projectIDMap[p.NextSiblingID];        }        p.userID =userID;        p.shareInfo=[];        if(p.projType == "Tender"){            if(p.property.calcProgramFile) p.property.calcProgramFile.ID = calcProgramFileIDMap[p.property.calcProgramFile.ID];            if(p.property.labourCoeFile) p.property.labourCoeFile.ID = labourCoeFileIDMap[p.property.labourCoeFile.ID];            if(p.property.feeFile) p.property.feeFile.id = feeRateFileIDMap[p.property.feeFile.id];            if(p.property.unitPriceFile) p.property.unitPriceFile.id = unitPriceFileIDMap[p.property.unitPriceFile.id];            p.property.rootProjectID = mainProjectID        }        tasks.push({insertOne: {document: p}})    }    //项目树节构数据生成:    await projectModel.bulkWrite(tasks);    //生成所有的费率文件    await importFeeRateFiles(mainData,projectIDMap,feeRateFileIDMap,userID);    //生成所有的单价文件    await importUnitPriceFiles(mainData,projectIDMap,unitPriceFileIDMap,userID);    return [projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap]}async function importUnitPriceFiles(mainData,projectIDMap,unitPriceFileIDMap,userID) {    if(!mainData.files.unitFiles) return;    let unitFiles = [],unitPrices =[],mixRatios=[],freights=[],originals=[];    for(let f of mainData.files.unitFiles){        let file = f.unitFile,prices = f.unitPrices,mixs = f.mixRatios,fres = f.freights,ors = f.originals;        //生成单价文件        delete file._id;        file.id = unitPriceFileIDMap[file.id]?unitPriceFileIDMap[file.id]:await getCounterID("unit_price_file");        file.project_id = projectIDMap[file.project_id];        file.root_project_id = projectIDMap[file.root_project_id];        file.user_id = userID;        unitFiles.push(file);        //生成子数据        if(prices) await  setSubList(prices,unitPrices,file.id,unitPriceModel);        if(mixs) await setSubList(mixs,mixRatios,file.id,mixRatioModel);    }    if(unitFiles.length > 0) await insertMany(unitFiles,unitPriceFileModel);    if(unitPrices.length > 0) await insertMany(unitPrices,unitPriceModel);    if(mixRatios.length > 0) await insertMany(mixRatios,mixRatioModel);    async function setSubList(oList,nList,fileID,model,UUID=false) {        for(let o of oList){            delete o._id;            o.unit_price_file_id = fileID;            UUID == true?o.ID = uuidV1():o.id = await getCounterID(model.modelName);            nList.push(o)        }    }}async function importFeeRateFiles(mainData,projectIDMap,feeRateFileIDMap,userID) {    let feeRateFiles = [], feeRates = [];    if(!mainData.files.feeRateFiles) return;    for(let f of mainData.files.feeRateFiles){        //生成费率记录        let rate = f.feeRate;        delete rate._id;        rate.ID = uuidV1();        feeRates.push(rate);        //生成费率文件记录        let file = f.feeRateFile;        delete file._id;        file.ID = feeRateFileIDMap[file.ID]?feeRateFileIDMap[file.ID]:uuidV1();        file.userID = userID;        file.rootProjectID = projectIDMap[file.rootProjectID];        file.feeRateID = rate.ID;        feeRateFiles.push(file);    }    if(feeRateFiles.length > 0) await insertMany(feeRateFiles,feeRateFileModel);    if(feeRates.length > 0) await insertMany(feeRates,feeRateModel);}// 初始化async function initOverHeightItems(engineeringID) {    const defaultData = [];    const engineering = await engineeringModel.findById(engineeringID);    const overHeightLibs = engineering.over_height_lib;    if (!overHeightLibs || !overHeightLibs.length) {        return defaultData;    }    const overHeightLibID = overHeightLibs[0].id;    const overHeightLib = await overHeightLibModel.findOne({ID: overHeightLibID});    return overHeightLib ? overHeightLib.list : defaultData;}function uploadToken() {    let options = {        scope: qiniu_config.Bucket,        deleteAfterDays: 1,        returnBody:            '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}'    };    let putPolicy = new qiniu.rs.PutPolicy(options);    let token = putPolicy.uploadToken(mac);    let result = {        uptoken: token,        domain: qiniu_config.Domain    }    return result}// 有些方法无法通过中间件就检查单位工程数量是否超限// 需要到具体的业务代码中进行判断// 这个方法就是具体业务代码中,需要检查单位工程数量是否超限用async function isTenderOverrun(tenderCount, session) {    const userID = session.sessionUser.id;    const compilation = session.sessionCompilation._id;    const compilationVersion = session.compilationVersion || '免费';    let systemSetting = session.systemSetting || (session.systemSetting = await systemSettingMiddleware.getSystemSetting());    // 这种情况只有在刚上线此功能时会出现,不考虑时间差    if (!systemSetting) {        return false;    }    const type = compilationVersion.includes('免费') ? 'normal' : 'professional';    const limit = systemSetting[type].project;    const curTenderCount = await projectModel.count({userID, compilation, projType: 'Tender', '$or':[{deleteInfo: null}, {'deleteInfo.completeDeleted': false}]});    return tenderCount + curTenderCount > limit;}async function getWelcomeInfo(compilationId,sessionUser) {    let setting = await welcomeModel.findOne({compilationId:compilationId});    let isShow = false;    let context = "";    if(setting){        if(setting.showType == 0) return [false,""];//关闭        context = setting.context;        if(setting.showType == 1){//每天一次            let dataString =moment(Date.now()).tz("Asia/Shanghai").format('YYYY-MM-DD');//今天的日期字符串            let userInfo =await userModel.findOne({_id: mongoose.Types.ObjectId(sessionUser.id)});            if (dataString != userInfo.welcomeShowTime){//今天没登录过                isShow = true;                await userModel.update({_id: mongoose.Types.ObjectId(sessionUser.id)},{welcomeShowTime:dataString})            }        }else if(setting.showType == 2){//每次登录显示            isShow = sessionUser.newLogin?true:false;            sessionUser.newLogin = false        }    }    return [isShow,context]}
 |