| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995 | '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');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 stageList = await ctx.service.stage.getValidStagesShort(tender.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,            };            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);            const 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;                    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;};
 |