| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010 | 'use strict';/** * Created by Tony on 2021/3/31. */const path = require('path');const uuidV1 = require('uuid').v1;const fs = require('fs');const MAX_ARCHIVE = 3;const tenderMenu = require('../../config/menu').tenderMenu;const measureType = require('../const/tender').measureType;const fsUtil = require('../public/js/fsUtil');const auditConst = require('../const/audit');const signConst = require('../const/sign');const shenpiConst = require('../const/shenpi');const accountGroup = require('../const/account_group').group;const sendToWormhole = require('stream-wormhole');const pushOperate = require('../const/spec_3f').pushOperate;const sourceTypeConst = require('../const/source_type');const rptArchiveConst = require('../const/rpt_archive');const advanceConst = require('../const/advance');module.exports = app => {    class ReportArchiveController extends app.BaseController {        /**         * 获取审批界面所需的 原报、审批人数据等         * @param ctx         * @return {Promise<void>}         * @private         */        async _getStageAuditViewData(ctx) {            if (!ctx.stage) return;            await ctx.service.stage.loadStageAuditViewData(ctx.stage);        }        async index(ctx) {            await this._getStageAuditViewData(ctx);            const tender = ctx.tender;            const stage = ctx.stage;            let stage_id = -1;            let stage_order = -1;            let stage_times = -1;            let stage_status = -1;            const treeNodes = await ctx.service.rptTreeNode.getNodesByProjectId([-1, tender.data.project_id]);            const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);            const allArchives = await ctx.service.rptArchive.getPrjStgArchiveByBz(ctx.session.sessionProject.id, null, tender.id);            // const stageList = await ctx.service.stage.getValidStagesShort(tender.id);            const stageList = await ctx.service.stage.getListByArchives(tender.id, ctx.helper._.map(allArchives, 'stage_id'));            const isAdmin = ctx.session.sessionUser.is_admin;            //            // 。。。            let archiveList = [];            let archiveEncryptionList = [];            // console.log('tender.data.project_id: ' + tender.data.project_id);            if (stage) {                // console.log('ctx.stage.id: ' + ctx.stage.id);                const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, ctx.stage.id);                const archiveEncryptions = await ctx.service.rptArchiveEncryption.getPrjStgArchiveEncryption(tender.data.project_id, ctx.stage.id);                stage_id = stage.id;                stage_order = stage.order;                stage_times = stage.times;                stage_status = stage.status;                if (archives.length > 0) {                    archiveList = JSON.parse(archives[0].content);                }                if (archiveEncryptions.length > 0) {                    archiveEncryptionList = JSON.parse(archiveEncryptions[0].content);                }            } else if (stageList.length > 0 && stageList[0].status === auditConst.stage.status.checked) {                // console.log('stageList[0].id: ' + stageList[0].id);                let archives = [];                for (let sidx = stageList.length - 1; sidx >= 0; sidx--) {                    if (stageList[sidx].status === 3) {                        archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, stageList[sidx].id);                        ctx.stage = stageList[sidx]; // 为了sub menu用                        break;                    }                }                ctx.stage = ctx.stage ? ctx.stage : stageList[stageList.length - 1];                // const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, stageList[stageList.length - 1].id);                const archiveEncryptions = await ctx.service.rptArchiveEncryption.getPrjStgArchiveEncryption(tender.data.project_id, ctx.stage.id);                // stage_id = stageList[0].id;                // stage_order = stageList[0].order;                // stage_times = stageList[0].times;                // stage_status = stageList[0].status;                if (archives && archives.length > 0) {                    archiveList = JSON.parse(archives[0].content);                }                if (archiveEncryptions && archiveEncryptions.length > 0) {                    archiveEncryptionList = JSON.parse(archiveEncryptions[0].content);                }            }            let rpt_tpl_items = '{ customize: [], common: [] }';            if (custTreeNodes.length > 0) {                rpt_tpl_items = custTreeNodes[0].rpt_tpl_items;            }            // 获取用户权限            const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);            // 获取所有项目参与者            const accountList = await ctx.service.projectAccount.getAllDataByCondition({                where: { project_id: ctx.session.sessionProject.id, enable: 1 },                columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],            });            const newAccountGroup = accountGroup.map((item, idx) => {                const groupList = accountList.filter(item => item.account_group === idx);                return { groupName: item, groupList };            });            const needFileMsg = await this.ctx.service.specMsg.reportNeedMsg(this.ctx.session.sessionProject.id, this.ctx.tender.id);            const renderData = {                tender: tender.data,                rpt_tpl_data: JSON.stringify(treeNodes),                cust_tpl_data: rpt_tpl_items,                project_id: tender.data.project_id,                tender_id: tender.id,                stg_id: stage_id,                stg_order: stage_order,                stg_times: stage_times,                stg_status: stage_status,                stage_list: stageList.length > 0 && stageList[0].status === auditConst.stage.status.checked ? JSON.stringify(stageList) : JSON.stringify([]),                tenderMenu,                measureType,                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.report.main),                stages: stageList,                auditConst: auditConst.stage,                archiveList,                archiveEncryptionList,                can_netcasign: false,                ossPath: signConst.path.oss,                shenpiConst,                preUrl: '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order,                authMobile: accountInfo.auth_mobile,                accountGroup: newAccountGroup,                accountList,                isAdmin,                needFileMsg,                auditType: auditConst.auditType,                advanceConst,            };            if (stage_id === -1) {                await this.layout('report/index_archive.ejs', renderData, 'report/archive_popup.ejs');            } else {                await this.layout('report/index_archive.ejs', renderData, 'report/stage_archive_modal.ejs');            }        }        async getReportArchive(ctx) {            const params = JSON.parse(ctx.request.body.params);            // ctx.body = await this._getReport(ctx, params);            let archives = [];            if ([-400, -500].includes(params.stgId)) {                archives = await ctx.service.rptArchive.getArchiveByBzId(params.prjId, params.stgId, params.bizId);            } else {                archives = await ctx.service.rptArchive.getPrjStgArchive(params.prjId, params.stgId);            }            const archiveEncryptions = await ctx.service.rptArchiveEncryption.getPrjStgArchiveEncryption(params.prjId, params.stgId);            let archiveList = [];            let archiveEncryptionList = [];            if (archives.length > 0) {                archiveList = JSON.parse(archives[0].content);            }            if (archiveEncryptions.length > 0) {                archiveEncryptionList = JSON.parse(archiveEncryptions[0].content);            }            const lastAuditor = await ctx.service.stageAudit.getLastestAuditor(params.stgId, params.stgTimes, auditConst.stage.status.checked);            ctx.body = {                data: archiveList,                encryptionData: archiveEncryptionList,                lastAuditor,            };        }        async _createNodes(ctx, source_type, pid) {            const treeNodes = await ctx.service.rptTreeNode.getNodesBySourceType([pid], source_type); // 这个查定制的            const commonTreeNodes = await ctx.service.rptTreeNode.getNodesByProjectId([-1]); // 这个查通用的            const nodeItems = JSON.parse(commonTreeNodes[0].items);            const commonArrs = [];            nodeItems.forEach(nodeItem => {                const srcType = nodeItem.source_type ? nodeItem.source_type : 1;                if (srcType === source_type) {                    commonArrs.push(nodeItem);                }            });            // 加一个dummy的通用报表            const dummyCommonRptNode = { id: 1, name: '通用报表', pid: -1, rpt_type: 0, items: JSON.stringify(commonArrs) };            treeNodes.push(dummyCommonRptNode);            const custCfg = await ctx.service.rptCustomizeCfg.getCustomizeCfgByUserId('Administrator');            return { treeNodes, commonArrs, custCfg };        }        async getReportArchive4bz(ctx) {            const params = JSON.parse(ctx.request.body.params);            const type = params.business_type;            const tenderId = params.tenderId;            const stgId = rptArchiveConst.getStageId(type);            const is_type_change = !params.bzId; // post值里不存在bzId代表切换总变更种类,存在代表切换变更列表值            const postBody = {};            if (is_type_change) {                let changes = [];                const allArchives = await ctx.service.rptArchive.getPrjStgArchiveByBz(ctx.session.sessionProject.id, stgId, tenderId);                switch (type) {                    case 'change':                        changes = await ctx.service.change.getListByArchives(tenderId, ctx.helper._.map(allArchives, 'business_id'));                        break;                    case 'change_project':                        changes = await ctx.service.changeProject.getListByArchives(tenderId, ctx.helper._.map(allArchives, 'business_id'));                        break;                    case 'change_apply':                        changes = await ctx.service.changeApply.getListByArchives(tenderId, ctx.helper._.map(allArchives, 'business_id'));                        break;                    case 'change_plan':                        changes = await ctx.service.changePlan.getListByArchives(tenderId, ctx.helper._.map(allArchives, 'business_id'));                        break;                    case 'advance':                        changes = await ctx.service.advance.getListByArchives(tenderId, ctx.helper._.map(allArchives, 'business_id'));                        break;                    case 'material':                        changes = await ctx.service.material.getListByArchives(tenderId, ctx.helper._.map(allArchives, 'business_id'));                        break;                    default: break;                }                postBody.changes = changes;            }            const bzId = !is_type_change ? params.bzId : (postBody.changes.length > 0 ? postBody.changes[0].cid || postBody.changes[0].id : null);            let archiveList = [];            const archives = await ctx.service.rptArchive.getArchiveByBzId(ctx.session.sessionProject.id, stgId, bzId);            const archiveEncryptions = await ctx.service.rptArchiveEncryption.getArchiveEncryptionByBzId(ctx.session.sessionProject.id, stgId, bzId);            if (archives.length > 0) {                archiveList = JSON.parse(archives[0].content);            }            // ctx.body = await this._getReport(ctx, params);            let archiveEncryptionList = [];            if (archives.length > 0) {                archiveList = JSON.parse(archives[0].content);            }            if (archiveEncryptions.length > 0) {                archiveEncryptionList = JSON.parse(archiveEncryptions[0].content);            }            const { treeNodes, custCfg } = await this._createNodes(ctx, sourceTypeConst.sourceType[type], ctx.session.sessionProject.id);            const cust_select_keys = JSON.stringify(['common', 'customize']); // 因其他地方也有可能保存用户报表的显示选择项,因当初设计问题,不好改数据库结构了,但可以调节内部json来满足需求            const rpt_tpl_items = { customize: [], common: [] };            if (treeNodes && treeNodes.length > 0) {                for (let tIdx = treeNodes.length - 1; tIdx >= 0; tIdx--) {                    treeNodes[tIdx].items = treeNodes[tIdx].items ? JSON.parse(treeNodes[tIdx].items) : [];                    if (treeNodes[tIdx].name === '通用报表') {                        const items = treeNodes[tIdx].items;                        for (let itemIdx = items.length - 1; itemIdx >= 0; itemIdx--) {                            rpt_tpl_items.common.push(items[itemIdx].name);                        }                    } else {                        rpt_tpl_items.customize.push(treeNodes[tIdx].name);                    }                }            }            postBody.data = archiveList;            postBody.encryptionData = archiveEncryptionList;            postBody.rpt_tpl_data = treeNodes;            postBody.stgId = stgId;            // postBody.cust_tpl_data = rpt_tpl_items;            // postBody.cust_select_keys = cust_select_keys;            // postBody.cust_cfg = JSON.stringify(custCfg);            ctx.body = postBody;        }        async _updateReportArchiveAdhocInfo(ctx, params) {            const userId = ctx.session.sessionUser.accountId;            const prjId = params.prjId;            const stgId = params.stgId;            const rptId = params.rptId;            const ttlPgs = params.ttlPgs;            const uuid = params.uuid;            const reportName = params.reportName;            const reportAreas = params.signatureAreas;            // 这里要更新zh_rpt_archive表的相关数据            const orgArchiveList = await ctx.service.rptArchive.getPrjStgArchive(prjId, stgId);            if (orgArchiveList.length > 0) {                const contentArr = JSON.parse(orgArchiveList[0].content);                for (const item of contentArr) {                    if (parseInt(item.rpt_id) === parseInt(rptId)) {                        for (const rptItem of item.items) {                            if (rptItem.uuid === uuid) {                                rptItem.uid = userId;                                rptItem.reportName = reportName;                                rptItem.ttl_pages = ttlPgs;                                rptItem.signature_area = reportAreas;                                break;                            }                        }                        break;                    }                }                const updatedRst = await ctx.service.rptArchive.updateArchive(orgArchiveList[0].id, prjId, stgId, contentArr);            } else {                // 正常情况下不可能的分支            }        }        async addReportArchiveEncryption(ctx) {            const params = JSON.parse(ctx.request.body.params);            const prjId = params.prjId;            const stgId = params.stgId;            const tdId = params.tenderId;            const bzId = params.businessId;            const rptId = params.rptId;            const ttlPgs = params.ttlPgs;            const uuid = params.uuid;            const removeUuid = params.removeUuid;            const childUuids = params.childUuids;            const splitArcPages = params.splitArcPages;            const reportName = params.reportName;            const userId = ctx.session.sessionUser.accountId;            const content = params.content;            const orgArchiveList = await ctx.service.rptArchiveEncryption.getArchiveEncryptionByBzId(prjId, stgId, bzId);            if (orgArchiveList.length > 0) {                const contentArr = JSON.parse(orgArchiveList[0].content);                let hasArchive = false;                for (const item of contentArr) {                    if (item.rpt_id === rptId) {                        // 考虑到报表模板的稳定性,只保留一项来记录位置就足够了,都不考虑用uuid了                        if (item.uuid === uuid) {                            // 最后打脸了,还真的要考虑不同的uuid,不早说,TNND                            item.encryption = content;                            item.total_page = ttlPgs;                            item.user_id = userId;                            item.report_name = reportName;                            item.childUuids = childUuids;                            item.splitArcPages = splitArcPages;                            hasArchive = true;                            break;                        }                    }                }                if (!hasArchive) {                    // 表示有新的要加, 有加有减                    for (let idx = contentArr.length - 1; idx >= 0; idx--) {                        if (contentArr[idx].uuid === removeUuid) {                            contentArr.splice(idx, 1);                            break;                        }                    }                    contentArr.push({ rpt_id: rptId, uuid, total_page: ttlPgs, encryption: content, user_id: userId, report_name: reportName, childUuids, splitArcPages });                } else {                    //                }                const updatedRst = await ctx.service.rptArchiveEncryption.updateArchiveEncryption(orgArchiveList[0].id, prjId, stgId, tdId, bzId, contentArr);                // console.log(updatedRst);                ctx.body = { err: 0, msg: '', data: { addedRst: contentArr } };            } else {                // 需要增加                const archiveArr = [];                archiveArr.push({ rpt_id: rptId, uuid, total_page: ttlPgs, encryption: content, user_id: userId, report_name: reportName, childUuids, splitArcPages });                const addedRst = await ctx.service.rptArchiveEncryption.createArchiveEncryption(prjId, stgId, tdId, bzId, archiveArr);                // console.log(addedRst);                ctx.body = { err: 0, msg: '', data: { addedRst: archiveArr } };            }        }        async _updateArchiveCommon(ctx, prjId, stgId, bzId, tdId, rptId, parentUuidName, childUuids) {            const updateDate = new Date();            const montStr = (updateDate.getMonth() + 1) < 10 ? ('0' + (updateDate.getMonth() + 1)) : (updateDate.getMonth() + 1);            const dateStr = (updateDate.getDate()) < 10 ? ('0' + updateDate.getDate()) : (updateDate.getDate());            let hrStr = '' + updateDate.getHours();            if (hrStr.length === 1) hrStr = '0' + hrStr;            let minStr = '' + updateDate.getMinutes();            if (minStr.length === 1) minStr = '0' + minStr;            let secStr = '' + updateDate.getSeconds();            if (secStr.length === 1) secStr = '0' + secStr;            const dtStr = `${updateDate.getFullYear()}-${montStr}-${dateStr} ${hrStr}:${minStr}:${secStr}`;            let rst = null;            const fileName = parentUuidName + '.PDF';            let removeUuid = ''; // 因这里把增删功能做在一起,有可能要删除一个旧的uuid,需要返回,给加密处理用            const orgArchiveList = await ctx.service.rptArchive.getArchiveByBzId(prjId, stgId, bzId);            if (orgArchiveList.length > 0) {                const contentArr = JSON.parse(orgArchiveList[0].content);                let hasArchive = false;                for (const item of contentArr) {                    if (item.rpt_id === rptId) {                        hasArchive = true;                        let updateRmIdx = -1;                        for (let idx = 0; idx < item.items.length; idx++) {                            if (parentUuidName === item.items[idx].uuid) {                                // 这里的判断是否是update archive逻辑(是否存在相同uuid的item)                                updateRmIdx = idx;                                break;                            }                        }                        if (updateRmIdx < 0 && item.items.length >= MAX_ARCHIVE) {                            updateRmIdx = 0;                            for (let idx = 1; idx < item.items.length; idx++) {                                if (item.items[updateRmIdx].updateDate_time > item.items[idx].updateDate_time) {                                    updateRmIdx = idx;                                }                            }                        }                        if (updateRmIdx >= 0) {                            // 删除oss文件                            removeUuid = item.items[updateRmIdx].uuid;                            await ctx.app.signPdfOss.delete(`archive/${item.items[updateRmIdx].uuid}.PDF`);                            // 以及删除子oss文件                            if (item.items[updateRmIdx].childUuids && item.items[updateRmIdx].childUuids.length > 0) {                                for (const childUuid of item.items[updateRmIdx].childUuids) {                                    let uuid = '';                                    if (typeof childUuid === 'string') {                                        uuid = childUuid;                                    } else {                                        uuid = childUuid.uuid;                                    }                                    await ctx.app.signPdfOss.delete(`archive/${uuid}.PDF`);                                }                            }                            item.items.splice(updateRmIdx, 1);                        }                        const newItem = { uuid: parentUuidName, updateDate_time: dtStr, childUuids };                        item.items.push(newItem);                        break;                    }                }                if (!hasArchive) {                    // 表示有新的模板需要添加                    contentArr.push({ rpt_id: rptId, items: [{ uuid: parentUuidName, updateDate_time: dtStr, childUuids }] });                }                const updatedRst = await ctx.service.rptArchive.updateArchive(orgArchiveList[0].id, prjId, stgId, tdId, bzId, contentArr);                // console.log(updatedRst);                rst = { err: 0, msg: parentUuidName, data: { uuid: parentUuidName, childUuids, removeUuid, fileName, updateDate, updatedRst: contentArr } };            } else {                // 需要增加                const archiveArr = [];                archiveArr.push({ rpt_id: rptId, items: [{ uuid: parentUuidName, updateDate_time: dtStr, childUuids }] });                const addedRst = await ctx.service.rptArchive.createArchive(prjId, stgId, tdId, bzId, archiveArr);                rst = { err: 0, msg: parentUuidName, data: { uuid: parentUuidName, childUuids, removeUuid, fileName, updateDate, updatedRst: archiveArr } };            }            return rst;        }        async addParentChildrenArchiveReports(ctx) {            // 接收多个子母PDF            let stream;            try {                const prjId = ctx.params.prjId;                const stgId = ctx.params.stgId;                const bzId = ctx.params.bzId;                const tdId = ctx.params.tdId;                const rptId = ctx.params.rptId;                // const childAmt = parseInt(ctx.params.childAmt); // 子分页数量                const childInfo = ctx.params.splitInfo.split(';'); // 这个参数带比较多的信息,包含分割指标的:1. 名称 2. ID 3. 内容                const splitArcPages = JSON.parse(childInfo[0]);                // console.log(splitArcPages);                const childAmt = splitArcPages.length;                const splitFieldObj = JSON.parse(childInfo[1]);                const ttlPages = parseInt(childInfo[2]); // 总页数                // console.log(splitFieldObj);                const parentUuid = uuidV1();                const childUuids = [];                // const newUuidName = uuidV1();                const parts = ctx.multipart({ autoFields: true });                stream = await parts();                let index = 0;                while (stream !== undefined) {                    /*                    // 测试保存文件                    await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app/public/upload/', stream.filename));                    await sendToWormhole(stream);                    //*/                    let fileName = parentUuid + '.PDF';                    if (index > 0) {                        const cUuid = uuidV1();                        const startPage = splitArcPages[index - 1];                        // const endPage = splitArcPages[index - 1];                        let endPage = ttlPages;                        if (index < splitArcPages.length) {                            endPage = splitArcPages[index] - 1;                        }                        childUuids.push({ uuid: cUuid, totalPages: (endPage - startPage + 1), startPage, endPage, splitKeyValue: splitFieldObj.splitPageValues[index - 1] });                        fileName = cUuid + '.PDF';                    }                    const oss_result = await ctx.app.signPdfOss.put('archive/' + fileName, stream);                    if (!(oss_result && oss_result.url && oss_result.res.status === 200)) {                        throw '上传文件失败';                    }                    await sendToWormhole(stream);                    ++index;                    if (index <= childAmt) {                        stream = await parts();                    } else {                        stream = undefined;                    }                    // stream = await parts();                }                const body = await this._updateArchiveCommon(ctx, prjId, stgId, bzId, tdId, rptId, parentUuid, childUuids);                ctx.body = body;            } catch (error) {                ctx.helper.log(error);                // 失败需要消耗掉stream 以防卡死                if (stream) {                    await sendToWormhole(stream);                }                ctx.body = this.ajaxErrorBody(error, '上传附件失败,请重试');            }        }        async addReportArchive(ctx) {            try {                const stream = await ctx.getFileStream();                const prjId = ctx.params.prjId;                const stgId = ctx.params.stgId;                const bzId = ctx.params.bzId;                const tdId = ctx.params.tdId;                const rptId = ctx.params.rptId;                const newUuidName = uuidV1();                const fileName = newUuidName + '.PDF';                const oss_result = await ctx.app.signPdfOss.put('archive/' + fileName, stream);                if (!(oss_result && oss_result.url && oss_result.res.status === 200)) {                    throw '上传文件失败';                }                const body = await this._updateArchiveCommon(ctx, prjId, stgId, bzId, tdId, rptId, newUuidName, []);                // console.log(body);                ctx.body = body;            } catch (err) {                this.log(err);                ctx.body = { err: 1, msg: err.toString(), data: null };            }        }        async updateReportArchiveEncryption(ctx) {            // 在add方法中已经处理            await this.addReportArchiveEncryption(ctx);        }        async updateReportArchive(ctx) {            try {                const stream = await ctx.getFileStream();                const prjId = ctx.params.prjId;                const stgId = ctx.params.stgId;                const bzId = ctx.params.bzId;                const tdId = ctx.params.tdId;                const rptId = ctx.params.rptId;                const orgUuidName = ctx.params.orgName;                const orgFileName = orgUuidName + '.PDF';                const newUuidName = uuidV1();                const fileName = newUuidName + '.PDF'; // 要用新的uuid!!!                console.log('updating fileName: ' + fileName);                // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', 'public/archive', fileName));                const oss_result = await ctx.app.signPdfOss.put('archive/' + fileName, stream);                if (!(oss_result && oss_result.url && oss_result.res.status === 200)) {                    throw '上传文件失败';                }                // 判断是否存在已签名文档,存在则删除文档并删除签名记录                const pdfMsg = await ctx.curl(signConst.path.oss + '/sign/' + orgUuidName + '.PDF');                if (pdfMsg && pdfMsg.status === 200) {                    const oss_reuslt = await ctx.app.signPdfOss.delete('archive/sign/' + orgFileName);                    if (oss_reuslt && oss_reuslt.res && oss_reuslt.res.status === 204) {                        const delSign_result = await ctx.service.netcasignLog.removeSign(orgUuidName);                    } else {                        throw '删除已签文档失败';                    }                }                const body = await this._updateArchiveCommon(ctx, prjId, stgId, bzId, tdId, rptId, newUuidName, []); // 只管用新的uuid,此方法中会自动删除最旧的那个记录及相关PDF文档                ctx.body = body;            } catch (err) {                this.log(err);                ctx.body = { err: 1, msg: err.toString(), data: null };            }        }        async _removeReportArchiveEncryption(ctx) {            let rst = null;            try {                const prjId = ctx.params.prjId;                const stgId = ctx.params.stgId;                const bzId = ctx.params.bzId;                const rptId = parseInt(ctx.params.rptId);                const uuid = ctx.params.orgName;                const orgArchiveList = await ctx.service.rptArchiveEncryption.getArchiveEncryptionByBzId(prjId, stgId, bzId);                if (orgArchiveList.length > 0) {                    const contentArr = JSON.parse(orgArchiveList[0].content);                    for (let idx = contentArr.length - 1; idx >= 0; idx--) {                        if (contentArr[idx].rpt_id === rptId && contentArr[idx].uuid === uuid) {                            contentArr.splice(idx, 1);                            break;                        }                    }                    rst = await ctx.service.rptArchiveEncryption.updateArchiveEncryption(orgArchiveList[0].id, prjId, stgId, orgArchiveList[0].tender_id, bzId, contentArr);                }            } catch (err) {                this.log(err);            }            return rst;        }        async removeReportArchiveEncryption(ctx) {            try {                const rst = await this._removeReportArchiveEncryption(ctx);                if (rst) {                    ctx.body = { err: 0, msg: '', data: { updatedRst: rst } };                } else {                    ctx.body = { err: 0, msg: '', data: { updatedRst: null } };                }            } catch (err) {                this.log(err);                ctx.body = { err: 1, msg: err.toString(), data: null };            }        }        async _removeReportArchive(ctx) {            let rst = null;            try {                const prjId = ctx.params.prjId;                const stgId = ctx.params.stgId;                const rptId = ctx.params.rptId;                const bzId = ctx.params.bzId;                const orgUuidName = ctx.params.orgName;                // const fileName = orgUuidName + '.PDF';                const orgArchiveList = await ctx.service.rptArchive.getArchiveByBzId(prjId, stgId, bzId);                if (orgArchiveList.length > 0) {                    const contentArr = JSON.parse(orgArchiveList[0].content);                    for (let idx = contentArr.length - 1; idx >= 0; idx--) {                        const item = contentArr[idx];                        if (item.rpt_id === rptId) {                            if (item.items && item.items.length > 0) {                                for (const subIdx in item.items) {                                    if (item.items[subIdx].uuid === orgUuidName) {                                        if (item.items[subIdx].childUuids && item.items[subIdx].childUuids.length > 0) {                                            // 如果有子分页,也得删除!                                            for (const childUuid of item.items[subIdx].childUuids) {                                                let childUuidName = '';                                                if (typeof childUuid === 'string') {                                                    childUuidName = childUuid;                                                } else {                                                    childUuidName = childUuid.uuid;                                                }                                                if (!childUuidName.includes('.PDF')) childUuidName = childUuidName + '.PDF';                                                await ctx.app.signPdfOss.delete(`archive/${childUuidName}`);                                            }                                        }                                        item.items.splice(subIdx, 1);                                        break;                                    }                                }                                if (item.items.length === 0) {                                    contentArr.splice(idx, 1);                                }                            }                            break;                        }                    }                    // contentArr 为空时,应该移除当前条                    rst = await ctx.service.rptArchive.updateArchive(orgArchiveList[0].id, prjId, stgId, orgArchiveList[0].tender_id, bzId, contentArr);                }            } catch (err) {                this.log(err);            }            return rst;        }        async removeReportArchive(ctx) {            try {                const orgUuidName = ctx.params.orgName;                const fileName = orgUuidName + '.PDF';                // console.log(ctx.params);                console.log('removing fileName: ' + fileName);                // const fullName = path.join(this.app.baseDir, 'app', 'public/archive', fileName);                const oss_sign_result = await ctx.app.signPdfOss.delete(`archive/sign/${fileName}`);                if (oss_sign_result && oss_sign_result.res && oss_sign_result.res.status === 204) {                    // console.log('删除归档的签名信息成功!');                    const oss_result = await ctx.app.signPdfOss.delete(`archive/${fileName}`);                    if (!(oss_result && oss_result.res.status === 204)) {                        throw '删除归档文件失败';                    }                } else {                    throw '删除归档签名文件失败';                }                // 还有加密签名信息                const archiveSignRemovedRst = await this._removeReportArchiveEncryption(ctx);                // console.log(archiveSignRemovedRst);                const archiveRemovedRst = await this._removeReportArchive(ctx);                if (archiveRemovedRst) {                    ctx.body = { err: 0, msg: orgUuidName, data: { fileName, updatedRst: archiveRemovedRst } };                } else {                    ctx.body = { err: 0, msg: orgUuidName, data: { fileName, updatedRst: null } };                }            } catch (err) {                this.log(err);                ctx.body = { err: 1, msg: err.toString(), data: null };            }        }        async addMultiReportArchive(ctx, params) {            // 暂时不支持        }        async getArchivedFileByUUID(ctx) {            // console.log('downloading : ' + ctx.params.uuid);            const uuid = ctx.params.uuid;            const rptName = ctx.params.rptName;            const suffix = '.PDF';            try {                const rptNameURI = encodeURI(rptName);                const filePath = this.app.baseDir + '/app/public/archive/';                // console.log('filePath: ' + filePath);                // await this.ctx.helper.recursiveMkdirSync(this.app.baseDir + '/app/public/download');                ctx.set({                    'Content-Type': 'application/vnd.openxmlformats',                    'Content-Disposition': 'attachment; filename="' + rptNameURI + suffix + "\"; filename*=utf-8''" + rptNameURI + suffix,                });                ctx.body = await fs.readFileSync(filePath + uuid + suffix);            } catch (e) {                console.log(e);            }        }        async pdfShow(ctx) {            // const renderData = {            //     can_netcasign: ctx.session.sessionProject.page_show.openNetCaSign === 1,            // };            await ctx.render('report/archive_pdf.ejs');        }        /*         *  网证通电子签名页,(和归档报表页高度相似)         */        async signReport(ctx) {            const tender = ctx.tender;            const stage = ctx.stage;            let stage_id = -1;            let stage_order = -1;            let stage_times = -1;            let stage_status = -1;            const treeNodes = await ctx.service.rptTreeNode.getNodesByProjectId([-1, tender.data.project_id]);            const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);            const stageList = await ctx.service.stage.getValidStagesShort(tender.id);            //            // 。。。            let archiveList = [];            let archiveEncryptionList = [];            // console.log('tender.data.project_id: ' + tender.data.project_id);            if (stage) {                // console.log('ctx.stage.id: ' + ctx.stage.id);                const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, ctx.stage.id);                const archiveEncryptions = await ctx.service.rptArchiveEncryption.getPrjStgArchiveEncryption(tender.data.project_id, ctx.stage.id);                stage_id = stage.id;                stage_order = stage.order;                stage_times = stage.times;                stage_status = stage.status;                if (archives.length > 0) {                    archiveList = JSON.parse(archives[0].content);                }                console.log('2:', archiveEncryptions, ctx.stage.id);                if (archiveEncryptions.length > 0) {                    archiveEncryptionList = JSON.parse(archiveEncryptions[0].content);                }            } else if (stageList.length > 0 && stageList[0].status === auditConst.stage.status.checked) {                // console.log('stageList[0].id: ' + stageList[0].id);                // const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, stageList[stageList.length - 1].id);                let archives = [];                for (let sidx = stageList.length - 1; sidx >= 0; sidx--) {                    if (stageList[sidx].status === 3) {                        archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, stageList[sidx].id);                        ctx.stage = stageList[sidx]; // 为了sub menu用                        break;                    }                }                ctx.stage = ctx.stage ? ctx.stage : stageList[stageList.length - 1];                const archiveEncryptions = await ctx.service.rptArchiveEncryption.getPrjStgArchiveEncryption(tender.data.project_id, ctx.stage.id);                // stage_id = stageList[0].id;                // stage_order = stageList[0].order;                // stage_times = stageList[0].times;                // stage_status = stageList[0].status;                if (archives && archives.length > 0) {                    archiveList = JSON.parse(archives[0].content);                }                if (archiveEncryptions && archiveEncryptions.length > 0) {                    archiveEncryptionList = JSON.parse(archiveEncryptions[0].content);                }            }            let rpt_tpl_items = '{ customize: [], common: [] }';            if (custTreeNodes.length > 0) {                rpt_tpl_items = custTreeNodes[0].rpt_tpl_items;            }            // 获取ukey绑定数据            const netcaSignData = await ctx.service.netcasign.getDataByCondition({ uid: ctx.session.sessionUser.accountId });            // 获取已签名数据            const signLogList = await ctx.service.netcasignLog.getLogList(ctx.tender.id);            const renderData = {                tender: tender.data,                rpt_tpl_data: JSON.stringify(treeNodes),                cust_tpl_data: rpt_tpl_items,                project_id: tender.data.project_id,                tender_id: tender.id,                stg_id: stage_id,                stg_order: stage_order,                stg_times: stage_times,                stg_status: stage_status,                stage_list: JSON.stringify(stageList),                tenderMenu,                measureType,                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.report.main),                stages: stageList,                auditConst: auditConst.stage,                archiveList,                archiveEncryptionList,                netcaSignData,                can_netcasign: ctx.session.sessionProject.page_show.openNetCaSign === 1,                ossPath: signConst.path.oss,                signLogList,            };            await this.layout('report/index_sign.ejs', renderData, 'report/index_sign_modal.ejs');        }        /**         * 网证通电子签名接口         *         * @param {object} ctx - egg全局变量         * @return {void}         */        async signPost(ctx) {            const response = {                err: 0,                msg: '',            };            try {                const data = JSON.parse(ctx.request.body.data);                let signData;                const netcaSignApi = signConst.path.api;                switch (data.type) {                    case 'getPdfBase64':                        const pdfResult = await ctx.app.signPdfOss.get(data.path);                        if (pdfResult.res.status !== 200) {                            throw '该文件不存在';                        }                        response.data = Buffer.from(pdfResult.content, 'binary').toString('base64');                        break;                    case 'pdfIsExist':                        const pdfMsg = await ctx.app.signPdfOss.head('archive/sign/' + data.uuid + '.PDF');                        response.data = pdfMsg.res.status === 200;                        break;                    // 获取摘要值                    case 'assemblyDigest':                        const postData = {                            requestJson: JSON.stringify(data.requestJson),                        };                        const result = await ctx.helper.sendMoreRequest(netcaSignApi + '/assemblyDigest', postData, 'POST');                        response.data = result;                        break;                    // 生成签名pdf                    case 'assemblyPdf':                        const postData2 = {                            requestJson: JSON.stringify(data.requestJson),                        };                        const result3 = await this.roundNetcaSign(ctx, postData2);                        // const result2 = await ctx.helper.sendMoreRequest(netcaSignApi + '/assemblyPdf', postData2, 'POST');                        // 上传到oss                        // console.log(result3);                        if (result3.code === 0) {                            // const result3 = await ctx.helper.sendMoreRequest(netcaSignApi + result2.data);                            const oss_result = await ctx.app.signPdfOss.put('archive/sign/' + data.requestJson.fileName + '.PDF', result3.data);                            if (oss_result && oss_result.res && oss_result.res.status === 200) {                                if (data.end) {                                    const versionId = oss_result.res.headers['x-oss-version-id'];                                    // 记录签名和保存                                    await ctx.service.netcasignLog.add(data.requestJson.fileName, data.role, ctx.session.sessionUser.accountId, versionId);                                    const signLogList = await ctx.service.netcasignLog.getLogList(ctx.tender.id);                                    response.data = signLogList;                                } else {                                    // next page                                }                            } else {                                throw '上传文件失败';                            }                        } else {                            throw result3.msg;                        }                        break;                    // 移除签名和已签移除pdf                    case 'removeSign':                        const oss_reuslt = await ctx.app.signPdfOss.delete('archive/sign/' + data.uuid + '.PDF');                        if (oss_reuslt && oss_reuslt.res && oss_reuslt.res.status === 204) {                            const delSign_result = await ctx.service.netcasignLog.removeSign(data.uuid);                            const signLogList = await ctx.service.netcasignLog.getLogList(ctx.tender.id);                            response.data = signLogList;                        } else {                            throw '删除文件失败';                        }                        break;                    default:throw '参数有误';                }            } catch (error) {                response.err = 1;                response.msg = error.toString();                const data = JSON.parse(ctx.request.body.data);                if (data && data.type === 'pdfIsExist') {                    response.err = 0;                    response.data = false;                }            }            ctx.body = response;        }        /**         * 网证通电子签名报表上传         *         * @param {object} ctx - egg全局变量         * @return {void}         */        async signFile(ctx) {            const stream = await ctx.getFileStream();            try {                const uuid = stream.fields.uuid;                const role = stream.fields.role;                const oss_result = await ctx.app.signPdfOss.put('archive/sign/' + uuid + '.PDF', stream);                if (!(oss_result && oss_result.url && oss_result.res.status === 200)) {                    throw '上传文件失败';                }                const versionId = oss_result.res.headers['x-oss-version-id'];                // 记录签名和保存                await ctx.service.netcasignLog.add(uuid, role, ctx.session.sessionUser.accountId, versionId);                const signLogList = await ctx.service.netcasignLog.getLogList(ctx.tender.id);                ctx.body = { err: 0, msg: '', data: signLogList };            } catch (err) {                // 必须将上传的文件流消费掉,要不然浏览器响应会卡死                await sendToWormhole(stream);                this.log(err);                ctx.body = { err: 1, msg: err.toString(), data: null };            }        }        async roundNetcaSign(ctx, postData2, round = 3) {            let response = {                code: 0,                data: '',            };            // 无法获取到result3,因为result2生成的pdf已损坏,请重复获取result2,直到获取成功或尝试3次失败后报错为止            try {                const netcaSignApi = signConst.path.api;                const result2 = await ctx.helper.sendMoreRequest(netcaSignApi + '/assemblyPdf', postData2, 'POST');                if (result2.code === 0) {                    const result3 = await ctx.curl(netcaSignApi + result2.data, {                        timeout: 300000, // 超时 5分钟中                    });                    if (result3) {                        response.data = result3.data;                    } else {                        if (round > 0) {                            round = round - 1;                            response = await this.roundNetcaSign(ctx, postData2, round);                        } else {                            throw 'pdf获取失败,网证通接口无法生成pdf';                        }                    }                }            } catch (error) {                response.code = 1;                response.msg = error;            }            return response;        }        async sendFileMsg(ctx) {            try {                if (this.ctx.session.sessionUser.accountId !== ctx.tender.data.user_id) throw '您无权操作';                const needFileMsg = await this.ctx.service.specMsg.tenderNeedMsg(this.ctx.session.sessionProject.id, this.ctx.tender.id, pushOperate.report.file);                if (!needFileMsg) throw '该标段暂不可进行该操作';                const waitingMsg = await this.ctx.service.specMsg.getDataByCondition({ tid: this.ctx.tender.id, timing: pushOperate.report.file, status: [0, 1] });                if (waitingMsg) throw '上一次归档完成,未执行完毕,请稍后再试';                const data = JSON.parse(ctx.request.body.data);                const stage = await this.ctx.service.stage.getDataById(data.sid);                await this.ctx.service.specMsg.addReportMsg(null, this.ctx.session.sessionProject.id, this.ctx.tender.data, stage, pushOperate.report.file);                ctx.body = { err: 0, msg: '提交成功,稍后将同步至档案系统', data: null };            } catch (err) {                this.ctx.log(err);                this.ctx.ajaxErrorBody(err, '操作失败');            }        }        async sendOtherFileMsg(ctx) {            try {                if (this.ctx.session.sessionUser.accountId !== ctx.tender.data.user_id) throw '您无权操作';                const data = JSON.parse(ctx.request.body.data);                const needFileMsg = await this.ctx.service.specMsg.tenderNeedMsg(this.ctx.session.sessionProject.id, this.ctx.tender.id, data.msgType);                if (!needFileMsg) throw '该标段暂不可进行该操作';                const waitingMsg = await this.ctx.service.specMsg.getDataByCondition({ tid: this.ctx.tender.id, timing: data.msgType, status: [0, 1] });                if (waitingMsg) throw '上一次归档完成,未执行完毕,请稍后再试';                await this.ctx.service.specMsg.addOtherReportMsg(null, this.ctx.session.sessionProject.id, this.ctx.tender.data, data.id, data.msgType);                ctx.body = { err: 0, msg: '提交成功,稍后将同步至档案系统', data: null };            } catch (err) {                this.ctx.log(err);                this.ctx.ajaxErrorBody(err, '操作失败');            }        }    }    return ReportArchiveController;};
 |