'use strict'; /** * * * @author Mai * @date 2018/6/20 * @version */ const moment = require('moment'); const auditConst = require('../const/audit').stage; const changeAudit = require('../const/audit').flow; const spreadConst = require('../const/spread'); const tenderConst = require('../const/tender'); const shenpiConst = require('../const/shenpi'); const payConst = require('../const/deal_pay.js'); const changeConst = require('../const/change'); const measureType = tenderConst.measureType; const path = require('path'); const PayCalculator = require('../lib/pay_calc'); const accountGroup = require('../const/account_group').group; const sendToWormhole = require('stream-wormhole'); const billsPosConvert = require('../lib/bills_pos_convert'); const fs = require('fs'); const stdConst = require('../const/standard'); const LzString = require('lz-string'); const spreadSetting = require('../lib/spread_setting'); const funSetConst = require('../const/fun_set'); module.exports = app => { class StageController extends app.BaseController { /** * 构造函数 * * @param {Object} ctx - egg全局变量 * @return {void} */ constructor(ctx) { super(ctx); ctx.showProject = true; ctx.showTender = true; ctx.showTitle = true; // ctx.reUploadPermission = false; } /** * 获取通用的renderData(用于layout, Menu, subMenu部分) * @param ctx * @return {{tender, tenderMenu, auditConst}} * @private */ async _getDefaultRenderData(ctx) { const data = { tender: ctx.tender.data, tenderMenu: JSON.parse(JSON.stringify(this.menu.stageMenu)), auditConst, measureType, preUrl: '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order, stage: ctx.stage, thirdParty: { gxby: ctx.session.sessionProject.gxby_status, dagl: ctx.session.sessionProject.dagl_status, }, shenpiConst, }; if ((ctx.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.stage.user_id) { // data.accountGroup = accountGroup; // 获取所有项目参与者 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'], }); data.accountList = accountList; data.accountGroup = accountGroup.map((item, idx) => { const groupList = accountList.filter(item => item.account_group === idx); return { groupName: item, groupList }; }); } data.tenderMenu.back.children[0].url = '/tender/' + ctx.tender.id + '/measure/stage'; // 是否已验证手机短信 const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId); data.authMobile = pa.auth_mobile; return data; } /** * 获取审批界面所需的 原报、审批人数据等 * @param ctx * @return {Promise} * @private */ async _getStageAuditViewData(ctx) { const times = ctx.stage.status === auditConst.status.checkNo ? ctx.stage.times - 1 : ctx.stage.times; ctx.stage.user = await ctx.service.projectAccount.getAccountInfoById(ctx.stage.user_id); ctx.stage.auditHistory = []; if (times >= 1) { for (let i = 1; i <= times; i++) { ctx.stage.auditHistory.push(await ctx.service.stageAudit.getAuditors(ctx.stage.id, i)); } } // 获取审批流程中左边列表 ctx.stage.auditors2 = ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id !== ctx.session.sessionUser.accountId ? await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, times) : await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, ctx.stage.times); const { status } = ctx.stage; if (status === auditConst.status.uncheck || status === auditConst.status.checkNo) { ctx.stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times); } await this._checkStageStart(ctx); } async _checkStageStart(ctx) { // 上报日期获取,及上报权限开关 if (ctx.session.sessionProject.page_show.openStageStart) { const stageStart = ctx.session.sessionProject.funSet.stage_start; const thisYear = moment().year(); // const thisYear = 2021; const thisMonth = moment().month(); // const thisMonth = 11; const thisDate = new Date(); let startEndDay = stageStart.end_day + '号'; let startDay = moment([thisYear, thisMonth, stageStart.start_day]).isValid() ? [thisYear, thisMonth, stageStart.start_day] : [thisYear, thisMonth + 1, 1]; const maxDay = moment(thisDate).format('YYYY-MM-DD') >= moment(startDay).format('YYYY-MM-DD'); startEndDay = maxDay && stageStart.end_month === funSetConst.endMonth[1].val ? funSetConst.endMonth[stageStart.end_month - 1].name + startEndDay : startEndDay; if (moment(thisDate).format('YYYY-MM-DD') < moment(startDay).format('YYYY-MM-DD') && stageStart.end_month === funSetConst.endMonth[1].val) { startDay = thisMonth !== 0 && moment([thisYear, thisMonth - 1, stageStart.start_day]).isValid() ? [thisYear, thisMonth - 1, stageStart.start_day] : (thisMonth !== 0 ? [thisYear, thisMonth - 1, 1] : [thisYear - 1, 11, stageStart.start_day]); } ctx.stage.startEndDay = startEndDay; // 上报截止时间 let endDay = [thisYear, thisMonth, stageStart.end_day]; if (stageStart.end_month === funSetConst.endMonth[0].val && !moment(endDay).isValid()) { endDay = thisMonth === 11 ? [thisYear + 1, 0, 1] : [thisYear, thisMonth + 1, 1]; endDay = moment(endDay).subtract(1, 'days'); } else if (stageStart.end_month === funSetConst.endMonth[1].val && maxDay) { if (!moment(endDay).isValid()) { endDay = [thisYear, thisMonth + 1, 1]; endDay = moment(endDay).subtract(1, 'days'); } else { endDay = thisMonth === 11 ? [thisYear + 1, 0, stageStart.end_day] : [thisYear, thisMonth + 1, stageStart.end_day]; } } if (!moment(endDay).isValid()) { endDay = [thisYear, thisMonth + 1, 1]; endDay = moment(endDay).subtract(1, 'days'); } // console.log(moment(thisDate).format('YYYY-MM-DD'), moment(startDay).format('YYYY-MM-DD'), moment(endDay).format('YYYY-MM-DD')); const startPermission = moment(moment(thisDate).format('YYYY-MM-DD')).isBetween(moment(startDay).format('YYYY-MM-DD'), moment(endDay).format('YYYY-MM-DD'), null, '[]'); ctx.stage.startPermission = startPermission; } } _checkStageCanModify(ctx) { // 检查登录用户,是否可操作 if (ctx.stage.readOnly) { throw '该计量期当前您无权操作'; } if (ctx.stage.revising) { throw '台账修订中,请勿修改提交期数据'; } } /** * 期计量页面 (Get) * @param {Object} ctx - egg全局变量 * @return {Promise} */ async index(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id); renderData.minusNoValue = projectFunInfo.minusNoValue && ctx.tender.info.fun_rela.stage_change.minusNoValue; [renderData.ledgerSpread, renderData.posSpread] = await spreadSetting.getStageSpreadSetting(ctx, ctx.tender.id, this.ctx.stage.readOnly || this.ctx.stage.revising, {minusNoValue: renderData.minusNoValue}); renderData.changeConst = changeConst; renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.index); renderData.whiteList = this.ctx.app.config.multipart.whitelist; renderData.imType = tenderConst.imType; renderData.curAuditor = ctx.stage.curAuditor; renderData.auditConst = auditConst; // 获取附件列表 const attData = await ctx.service.stageAtt.getDataByTenderIdAndStageId(ctx.tender.id, ctx.params.order); for (const index in attData) { attData[index].in_time = moment(attData[index].in_time * 1000).format('YYYY-MM-DD'); } renderData.attData = attData; renderData.coopwd = ((ctx.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.stage.user_id) || ((ctx.stage.status === auditConst.status.checking || ctx.stage.status === auditConst.status.checkNoPre) && ctx.stage.curAuditor && ctx.stage.curAuditor.aid === ctx.session.sessionUser.accountId); // 获取收方单列表 const sfData = await ctx.service.stageShoufang.getAllDataByCondition({ where: { sid: ctx.stage.id }, orders: [['id', 'desc']] }); for (const sf of sfData) { sf.qrcode = ctx.app.config.fujianOssPath + sf.qrcode; sf.filenum = await ctx.service.stageShoufangAtt.count({ sfid: sf.id }); } renderData.sfData = sfData; // 收方单附件删除权限 renderData.sfAttDelPower = ctx.session.sessionUser.accountId === ctx.stage.user_id || ctx.helper._.findIndex(ctx.stage.auditors2, { aid: ctx.session.sessionUser.accountId }) !== -1; renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver; renderData.hintMinusCb = projectFunInfo.banMinusChangeBills; renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id); await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage'); } } _checkUniqData(data, field) { const groupData = this.ctx.helper._.groupBy(data, field); const surplus = []; for (const gd in groupData) { if (groupData[gd].length > 1) { const first = groupData[gd][0]; for (let i = 1, iLen = groupData[gd].length; i < iLen; i++) { const check = groupData[gd][i]; if (this.ctx.helper._.isMatch({ field: check[field], sid: check.sid, order: check.order, times: check.times }, { field: first[field], sid: first.sid, order: first.order, times: first.times })) { surplus.push(groupData[gd][i].id); } } } } return surplus; } _getLedgerColumn(sjsRela) { const tender = this.ctx.tender; this.ledgerColumn = [ 'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name', 'unit', 'unit_price', 'quantity', 'total_price', 'memo', 'drawing_code', 'node_type']; if (tender.data.measure_type === measureType.gcl.value) this.ledgerColumn.push('deal_qty', 'deal_tp'); if (tender.info.display.ledger.dgnQty) this.ledgerColumn.push('dgn_qty1', 'dgn_qty2'); this.ledgerExtraColumn = ['is_tp']; if (this.ctx.session.sessionProject.gxby) this.ledgerExtraColumn.push('gxby_status', 'gxby_url', 'gxby_limit'); if (this.ctx.session.sessionProject.dagl) this.ledgerExtraColumn.push('dagl_status', 'dagl_url', 'dagl_limit'); this.posColumn = ['id', 'tid', 'lid', 'name', 'position', 'porder', 'quantity', 'add_stage_order', 'drawing_code']; if (tender.info.display.stage.realComplete) this.posColumn.push('real_qty'); this.posExtraColumn = []; if (this.ctx.session.sessionProject.gxby) this.posExtraColumn.push('gxby_status', 'gxby_url', 'gxby_limit'); if (this.ctx.session.sessionProject.dagl) this.posExtraColumn.push('dagl_status', 'dagl_url', 'dagl_limit'); if (!sjsRela) return; if (sjsRela) { for (const field of sjsRela.ledgerCol) { if (field.show) { this.ledgerColumn.push(field.field); this.posColumn.push(field.field); } } } } async _getStageLedgerData(ctx) { const ledgerData = ctx.stage.ledgerHis ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.bills_file) : await ctx.service.ledger.getAllDataByCondition({ columns: this.ledgerColumn, where: { tender_id: ctx.tender.id } }); const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id); const extraData = await ctx.service.ledgerExtra.getData(ctx.tender.id, this.ledgerExtraColumn); const pcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: ctx.stage.id } }); const importData = await ctx.service.stageImportChange.getImportLid(ctx.stage.id); let curStageData; // 当前操作人查看最新数据,其他人查看历史数据 if (ctx.stage.readOnly) { curStageData = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder); } else { curStageData = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, ctx.stage.id); const surplus = this._checkUniqData(curStageData, 'lid'); if (surplus.length > 0) { await ctx.service.stageBills.deleteById(surplus); } } // 查询截止上期数据 const preStageData = ctx.stage.order > 1 ? await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : []; this.ctx.helper.assignRelaData(ledgerData, [ { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id' }, { data: extraData, fields: this.ledgerExtraColumn, prefix: '', relaId: 'id' }, { data: importData, fields: ['is_import'], prefix: '', relaId: 'lid' }, { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'lid' }, { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used'], prefix: 'pre_', relaId: 'lid' }, { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' }, ]); return ledgerData; } async _getStagePosData(ctx) { let curStageData; const posData = ctx.stage.ledgerHis ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.pos_file) : await ctx.service.pos.getAllDataByCondition({ columns: this.posColumn, where: { tid: ctx.tender.id } }); const extraData = await ctx.service.posExtra.getData(ctx.tender.id, this.posExtraColumn); // 根据当前人,或指定对象查询数据 if (ctx.stage.readOnly) { curStageData = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder); } else { curStageData = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, ctx.stage.id); const surplus = this._checkUniqData(curStageData, 'pid'); if (surplus.length > 0) { await ctx.service.stagePos.deleteById(surplus); } } // 查询截止上期数据 const preStageData = ctx.stage.order > 1 ? await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : []; this.ctx.helper.assignRelaData(posData, [ { data: extraData, fields: this.posExtraColumn, prefix: '', relaId: 'id'}, { data: curStageData, fields: ['contract_qty', 'contract_expr', 'qc_qty', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'pid' }, { data: preStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'pre_', relaId: 'pid' }, ]); return posData; } async _getStageDetailData(ctx) { if (ctx.stage.readOnly) { return await ctx.service.stageDetail.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder); } return await ctx.service.stageDetail.getLastestStageData(ctx.tender.id, ctx.stage.id); } async _getStageChangeData(ctx) { if (ctx.stage.readOnly) { return await ctx.service.stageChange.getAuditorAllStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder); } return await ctx.service.stageChange.getLastestAllStageData(ctx.tender.id, ctx.stage.id); } async getStageData(ctx) { try { const data = JSON.parse(ctx.request.body.data); const filter = data.filter.split(';'); const responseData = { err: 0, msg: '', data: {}, hpack: [] }; const hpack = true; const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo); this._getLedgerColumn(sjsRela); for (const f of filter) { switch (f) { case 'ledger': if (hpack) { responseData.hpack.push('ledgerData'); responseData.data.ledgerData = this.ctx.helper.hpackArr(await this._getStageLedgerData(ctx)); } else { responseData.data.ledgerData = await this._getStageLedgerData(ctx); } break; case 'pos': if (hpack) { responseData.hpack.push('posData'); responseData.data.posData = this.ctx.helper.hpackArr(await this._getStagePosData(ctx)); } else { responseData.data.posData = await this._getStagePosData(ctx); } break; case 'detail': responseData.data.detailData = await this._getStageDetailData(ctx); responseData.data.detailAtt = await this.ctx.service.stageDetailAtt.getStageData(ctx.stage.id, ctx.stage.im_type); break; case 'change': responseData.data.changeData = await this._getStageChangeData(ctx); break; case 'changeBills': responseData.data.changeBills = await this.ctx.service.changeAuditList.checkedChangeBills(ctx.stage.tid); break; case 'import_change': responseData.data.import_change = await this.ctx.service.stageImportChange.getStageImportData(this.ctx.stage); break; case 'dealBills': responseData.data.dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: this.ctx.tender.id }, }); break; case 'tag': responseData.data.tags = await ctx.service.ledgerTag.getDatas(ctx.tender.id, ctx.stage.id); break; case 'cooperation': const uid = ctx.stage.curAuditor ? ctx.stage.curAuditor.aid : (ctx.stage.status === auditConst.status.uncheck || (ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id === ctx.session.sessionUser.accountId) ? ctx.stage.user_id : (ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id !== ctx.session.sessionUser.accountId) ? ctx.session.sessionUser.accountId : null); responseData.data.cooperation = uid !== null ? await ctx.service.ledgerCooperation.getValidData( ctx.tender.id, uid) : []; const stageTimes = (ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id !== ctx.session.sessionUser.accountId) ? ctx.stage.times - 1 : ctx.stage.times; responseData.data.cooperationConfirm = uid !== null ? await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, stageTimes, uid) : []; responseData.data.locked = await ctx.service.cooperationConfirm.getLockedId(ctx.tender, ctx.stage); break; case 'spec': const spec = {zlj: JSON.parse(JSON.stringify(stdConst.zlj)), jrg: stdConst.jrg}; spec.zlj.deal_bills_tp = ctx.tender.info.deal_param.zanLiePrice; responseData.data.spec = spec; break; } } ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } async check(ctx) { try { const helper = this.ctx.helper; this._getLedgerColumn(); const ledgerData = await this._getStageLedgerData(ctx); ledgerData.forEach(x => { x.end_qc_minus_qty = helper.add(x.pre_qc_minus_qty, x.qc_minus_qty); x.final_1_qty = helper.add(x.quantity, x.end_qc_minus_qty); x.deal_final_1_qty = helper.add(x.deal_qty, x.end_qc_minus_qty); }); const posData = await this._getStagePosData(ctx); posData.forEach(x => { x.end_qc_minus_qty = helper.add(x.pre_qc_minus_qty, x.qc_minus_qty); x.final_1_qty = helper.add(x.quantity, x.end_qc_minus_qty); }); const projRela = await this.ctx.service.project.getFunRela(this.ctx.session.sessionProject.id); const checkDataModel = require('../lib/ledger').checkData; const checkData = new checkDataModel(ctx, measureType); checkData.loadData(ledgerData, posData); await this.ctx.service.s2bProj.refreshSessionS2b(); checkData.check3fLimit(ctx.tender.data); projRela.banOver && ctx.tender.info.ledger_check.over && checkData.checkOverRange(['contract_qty', 'qc_qty']); checkData.checkBillsTp([ { qty: 'contract_qty', tp: 'contract_tp' }, { qty: 'qc_qty', tp: 'qc_tp' }, ], this.ctx.tender.info.decimal, x => { return x.is_tp; }); checkData.checkBillsQty(['contract_qty', 'qc_qty']); if (projRela.banMinusChangeBills) { const change = await this.ctx.service.change.getAllChangeHasMinus(ctx.tender.id); if (change.length > 0) { const cid = change.map(x => { return x.cid; }); const changeBills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } }); const endStageChange = await this.ctx.service.stageChange.getFinalUsedData(ctx.tender.id, cid); checkData.checkMinusChangeBills(change, changeBills, endStageChange); } } ctx.body = { err: 0, msg: '', data: checkData.checkResult }; } catch (err) { this.log(err); ctx.body = this.ajaxErrorBody(err, '检查数据错误'); } } async _checkMinusChangeBills(ctx) { const checkDataModel = require('../lib/ledger').checkData; const checkData = new checkDataModel(ctx, measureType); const change = await this.ctx.service.change.getAllChangeHasMinus(ctx.tender.id); if (change.length > 0) { const cid = change.map(x => { return x.cid; }); const changeBills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } }); const endStageChange = await this.ctx.service.stageChange.getFinalUsedData(ctx.tender.id, cid); checkData.checkMinusChangeBills(change, changeBills, endStageChange); } return checkData.checkResult.error; } async stageCheck(ctx) { try { if (!ctx.query.type) throw '参数错误'; const type = ctx.query.type.split(','); const result = {}; for (const t of type) { switch (t) { case 'minus_cb': result.minus_cb = await this._checkMinusChangeBills(ctx); break; } } ctx.body = { err: 0, msg: '', data: result }; } catch (err) { this.log(err); ctx.body = this.ajaxErrorBody(err, '检查数据错误'); } } /** * 获取期数据(截止上期 & 本期) (Ajax) * @param ctx * @return {Promise} */ async getStagePosData(ctx) { try { const condition = JSON.parse(ctx.request.body.data) || {}; condition.tid = ctx.tender.id; const responseData = { err: 0, msg: '', data: {} }; let curStageData, preStageData; responseData.data = await ctx.service.pos.getPosDataWithAddStageOrder(condition); // 根据当前人,或指定对象查询数据 const curWhere = JSON.parse(ctx.request.body.data); if (ctx.stage.readOnly) { curStageData = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder, curWhere); } else { curStageData = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, ctx.stage.id, curWhere); } // 查询截止上期数据 if (ctx.stage.order > 1) { preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1); } else { preStageData = []; } this.ctx.helper.assignRelaData(responseData.data, [ { data: curStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty', 'postil'], prefix: '', relaId: 'pid' }, { data: preStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'pre_', relaId: 'pid' }, ]); ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 保存数据 (Ajax) * @param ctx * @return {Promise} */ async updateStageData(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); const responseData = { err: 0, msg: '', data: {} }; if (data.pos) { responseData.data = await ctx.service.stagePos.updateStageData(data.pos); } else if (data.bills) { if (data.bills.main) { const updateDatas = data.bills.main instanceof Array ? data.bills.main : [data.bills.main]; const result = await ctx.app.mysql.updateRows(ctx.service.ledger.tableName, updateDatas); responseData.data.bills = await ctx.service.ledger.getDataByIds(this.ctx.helper._.map(updateDatas, 'id')); } if (data.bills.dgn) { ctx.helper.checkDgnQtyPrecision(data.bills.dgn); responseData.data.dgn = await ctx.service.stageBillsDgn.saveDgnData(data.bills.dgn); } if (data.bills.stage) { responseData.data.curStageData = await ctx.service.stageBills.updateStageData(data.bills.stage); } if (data.bills.calcType) { responseData.data = await ctx.service.stageBills.updateStageBillsCalcType(data.bills.calcType); } } await ctx.service.stage.updateCheckCalcFlag(ctx.stage, true); await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 查询可用变更令 (Ajax-Post) * @param ctx * @return {Promise} */ async searchValidChange(ctx) { try { const data = JSON.parse(ctx.request.body.data); if (!data.bills && !data.pos) { throw '数据错误'; } const bills = data.bills ? data.bills : await ctx.service.ledger.getDataById(data.pos.lid); const pos = data.pos; const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id); const changes = await ctx.service.change.getValidChanges(ctx.tender, ctx.stage, data, projectFunInfo.minusNoValue && ctx.tender.info.fun_rela.stage_change.minusNoValue); const useChanges = ctx.stage.readOnly ? await ctx.service.stageChange.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder, bills.id, pos ? pos.id : -1) : await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, bills.id, pos ? pos.id : -1); ctx.body = { err: 0, msg: '', data: { changes, useChanges } }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 调用变更令 (Ajax-Post) * @param ctx * @return {Promise} */ async useChange(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); if (!data.target || (!data.target.bills && !data.target.pos) || !data.change) { throw '调用变更令数据错误'; } let result; if (data.target.pos) { result = await ctx.service.stageChange.posChange(data.target.pos, data.target.minus, data.change); result.change = { target: { lid: data.target.pos.lid, pid: data.target.pos.id } }; result.change.data = await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, data.target.pos.lid, data.target.pos.id); } else { result = await ctx.service.stageChange.billsChange(data.target.bills, data.target.minus, data.change); result.change = { target: { lid: data.target.bills.id, pid: '-1' } }; result.change.data = await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, data.target.bills.id, '-1'); } await ctx.service.stage.updateCheckCalcFlag(ctx.stage, true); await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = { err: 0, msg: '', data: result }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 调用变更令 (Ajax-Post) * @param ctx * @return {Promise} */ async autoUseChange(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); if (!data.autoType) throw '参数错误'; let result; switch (data.autoType) { case 'bills': if (!data.cid || !data.cbid) throw '参数错误'; result = await ctx.service.stageChange.autoUseChangeBills(this.ctx.tender, this.ctx.stage, data.bills); result.change = { target: [] }; for (const b of data.bills) { result.change.push({ lid: b.lid, pid: b.pid }); } result.change.data = await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, data.target.pos.lid, data.target.pos.id); break; case 'all': result = await ctx.service.stageChange.autoUseAllChange(this.ctx.tender, this.ctx.stage); break; default: throw '参数错误'; } await ctx.service.stage.updateCheckCalcFlag(ctx.stage, true); await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = { err: 0, msg: '', data: result }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 查询变更令 明细数据(包括附件、变更清单、累计使用情况、本期使用情况) (Ajax-Post) * @param ctx * @return {Promise} */ async changeDetail(ctx) { try { const data = JSON.parse(ctx.request.body.data); if (!data.cid) { throw '查询数据错误'; } const detailData = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, data.cid, data.is_import); ctx.body = { err: 0, msg: '', data: detailData }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 中间计量 (Get) * @param ctx * @return {Promise} */ async detail(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.detail); renderData.imType = tenderConst.imType; await this.layout('stage/detail.ejs', renderData, 'stage/detail_modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order); } } /** * 设置中间计量生成规则,并生成中间计量数据 (Ajax) * @param ctx * @return {Promise} */ async buildDetailData(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); await ctx.service.stage.buildDetailData(ctx.tender.id, ctx.stage.order, data); await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = { err: 0, msg: '', data: null }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 加载数据 (Ajax) * @param ctx * @return {Promise} */ async loadDetailRelaData(ctx) { try { const data = JSON.parse(ctx.request.body.data); // 加载台账数据 if (data.loadType === 'ledger') { const ledgerData = await ctx.service.ledger.getData(ctx.tender.id); ctx.body = { err: 0, msg: '', data: ledgerData }; } else if (data.loadType === 'all') { const result = {}; result.ledger = await ctx.service.ledger.getData(ctx.tender.id); result.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id }); if (ctx.stage.readOnly) { const curStage = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder); this.ctx.helper.assignRelaData(result.ledger, [ { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' }, ]); const curPosStage = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder); this.ctx.helper.assignRelaData(result.pos, [ { data: curPosStage, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid' }, ]); } else { const curStage = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, ctx.stage.id); this.ctx.helper.assignRelaData(result.ledger, [ { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' }, ]); const curPosStage = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, ctx.stage.id); this.ctx.helper.assignRelaData(result.pos, [ { data: curPosStage, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid' }, ]); } result.stageDetail = await this._getStageDetailData(ctx); result.changeData = await this._getStageChangeData(ctx); ctx.body = { err: 0, msg: '', data: result }; } } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 中间计量,生成规则,高级设置 (Ajax) * @param ctx * @return {Promise} */ async setAdvancedConfig(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); await ctx.service.stage.update(data, { id: ctx.stage.id }); await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = { err: 0, msg: '', data: this.ctx.stage }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 中间计量,编辑中间计量数据(Ajax) * @param ctx * @return {Promise} */ async saveDetailData(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); const responseData = { err: 0, msg: '', data: {} }; if (data instanceof Array) { responseData.data = await ctx.service.stageDetail.saveDetailDatas(data); } else { responseData.data = await ctx.service.stageDetail.saveDetailData(data); } await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 中间计量,添加草图,上传图片 (Ajax) * @param ctx * @return {Promise} */ async addCalcImage(ctx) { try { this._checkStageCanModify(ctx); const stream = await ctx.getFileStream(); const create_time = Date.parse(new Date()) / 1000; const fileInfo = path.parse(stream.filename); const fileName = path.join('public/upload', this.ctx.tender.id.toString(), 'im', 'calcImg_' + create_time + fileInfo.ext); await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', fileName)); ctx.body = { err: 0, msg: '', data: fileName }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 中间计量,设置草图 (Ajax) * @param ctx * @return {Promise} */ async mergeCalcImage(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); if (data.updateType === 'update') { if (data.img) { const create_time = Date.parse(new Date()) / 1000; const fileName = path.join('public/upload', this.ctx.tender.id.toString(), 'im', 'calcImg_' + create_time + '.png'); const base64Data = data.img.replace(/^data:image\/\w+;base64,/, ''); const dataBuffer = new Buffer.from(base64Data, 'base64'); await this.ctx.helper.saveBufferFile(dataBuffer, path.join(this.app.baseDir, 'app', fileName)); data.calc_img = fileName; data.calc_img_org = JSON.stringify(data.imgInfo); delete data.img; } delete data.updateType; delete data.imgInfo; } else if (data.updateType === 'clear') { data.calc_img = null; data.calc_img_org = null; data.calc_img_remark = null; delete data.updateType; } await this.ctx.service.stageDetail.saveDetailData(data); const imData = await ctx.service.stageDetail.getLastestImStageData(this.ctx.tender.id, this.ctx.stage.id, data.lid, data.uuid); await ctx.service.stage.updateCacheTime(ctx.stage.id); const responseData = { err: 0, msg: '', data: imData }; ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } async uploadImFile(ctx) { let stream; try { const parts = ctx.multipart({ autoFields: true }); let index = 0; const create_time = Date.parse(new Date()) / 1000; stream = await parts(); const uploadFiles = []; while (stream !== undefined) { if (!stream.filename) throw '未发现上传文件!'; const fileInfo = path.parse(stream.filename); // const dirName = path.join('app', 'public', 'upload', this.ctx.tender.id.toString(), 'im'); // const fileName = `${ctx.session.sessionUser.accountId}_${create_time}_${index}${fileInfo.ext}`; const filepath = `app/public/upload/${this.ctx.tender.id}/im/${ctx.session.sessionUser.accountId}_${create_time}_${index}${fileInfo.ext}`; // 保存文件 // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName)); await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream); await sendToWormhole(stream); // 插入到stage_pay对应的附件列表中 uploadFiles.push({ file_id: this.ctx.app.uuid.v4(), filename: fileInfo.name, fileext: fileInfo.ext, filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size, filepath, uid: ctx.session.sessionUser.accountId, in_time: moment(create_time * 1000).format('YYYY-MM-DD'), renew: ctx.stage.status === auditConst.status.checked, }); ++index; if (Array.isArray(parts.field.size) && index < parts.field.size.length) { stream = await parts(); } else { stream = undefined; } } const baseInfo = JSON.parse(parts.field.base); const result = await ctx.service.stageDetailAtt.addFiles(this.ctx.stage, baseInfo, uploadFiles); ctx.body = { err: 0, mgs: '', data: result }; } catch (err) { // 失败需要消耗掉stream 以防卡死 if (stream) await sendToWormhole(stream); ctx.log(err); ctx.ajaxErrorBody(err, '上传附件失败'); } } async deleteImFile(ctx) { try { const data = JSON.parse(ctx.request.body.data); if (!data.uuid || !data.file_id) throw '数据错误'; const result = await ctx.service.stageDetailAtt.delFiles(data.uuid, data.file_id); ctx.body = { err: 0, msg: '', data: result }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '删除附件失败'); } } async downloadImFile(ctx) { try { const uuid = ctx.query.im_id; const file_id = ctx.query.file_id; if (!uuid || !file_id) throw '数据错误'; const fileInfo = await ctx.service.stageDetailAtt.getFiles(uuid, file_id); if (!fileInfo) throw '文件不存在'; // const fileName = path.join(this.app.baseDir, fileInfo.filepath); // 解决中文无法下载问题 const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase(); let disposition = ''; if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) { disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext); } else if (userAgent.indexOf('firefox') >= 0) { disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"'; } else { /* safari等其他非主流浏览器只能自求多福了 */ disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary'); } ctx.response.set({ 'Content-Type': 'application/octet-stream', 'Content-Disposition': disposition, 'Content-Length': fileInfo.filesize, }); // ctx.body = await fs.createReadStream(fileName); ctx.body = await ctx.helper.ossFileGet(fileInfo.filepath); } catch (err) { ctx.log(err); ctx.postError(err, '下载文件失败'); } } async _updateStageCache(ctx, payCalculator) { await ctx.service.stage.update({ check_calc: false, contract_tp: payCalculator.cur.contract_tp, qc_tp: payCalculator.cur.qc_tp, yf_tp: payCalculator.yf.tp, sf_tp: payCalculator.sf.tp, }, { id: ctx.stage.id }); } /** * 合同支付 (Get) * @param ctx * @return {Promise} */ async pay(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); renderData.dealPay = await ctx.service.stagePay.getStagePays(ctx.stage); const payAtt = await ctx.service.payAtt.getStageData({ sid: ctx.stage.id }); // 附件不取下载地址 for (const dp of renderData.dealPay) { dp.attachment = payAtt.filter(x => { return x.pid === dp.pid }); } renderData.calcBase = await ctx.service.stage.getStagePayCalcBase(ctx.stage, ctx.tender.info); renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.pay); renderData.whiteList = this.ctx.app.config.multipart.whitelist; if (ctx.stage.order > 1) { renderData.pre = await ctx.service.stageBillsFinal.getSumTotalPrice(ctx.stage.tid, ctx.stage.order - 1); renderData.pre.gather_tp = ctx.helper.add(renderData.pre.contract_tp, renderData.pre.qc_tp); } else { renderData.pre = { contract_tp: null, qc_tp: null, gather_tp: null }; } // 用户有无权限上传和删除附件 renderData.uploadPermission = !ctx.tender.isTourist || ctx.tender.touristPermission.file; if (!ctx.stage.readOnly || ctx.tender.isTourist) { // 计算 本期金额 const payCalculator = new PayCalculator(ctx, ctx.stage, ctx.tender.info); await payCalculator.calculateAll(renderData.dealPay); await this._updateStageCache(ctx, payCalculator); } await this.layout('stage/pay.ejs', renderData, 'stage/pay_modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage'); } } /** * 合同支付 - 编辑合同支付项 (Ajax) * @param ctx * @return {Promise} */ async savePayData(ctx) { function checkCalcField(data) { return data.sexpr !== undefined || data.sprice !== undefined || data.rexpr !== undefined || data.rprice !== undefined || data.minus !== undefined || data.is_yf !== undefined || data.dl_type !== undefined; } try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); const responseData = { err: 0, msg: '', data: {} }; const payCalculator = new PayCalculator(ctx, ctx.stage, ctx.tender.info); switch (data.type) { case 'add': responseData.data = await ctx.service.pay.add(); responseData.data = await ctx.service.stagePay.getStagePay(ctx.stage, responseData.data.pid); break; case 'del': await ctx.service.pay.del(data.id); responseData.data = await ctx.service.stagePay.getStagePays(ctx.stage); await payCalculator.calculateAll(responseData.data); await this._updateStageCache(ctx, payCalculator); break; case 'changeOrder': responseData.data = await ctx.service.pay.changeOrder(data.id1, data.id2); break; case 'info': responseData.data = await ctx.service.pay.save(data.updateData); const bReCalc = data.updateData instanceof Array ? checkCalcField(data.updateData[0]) : checkCalcField(data.updateData); if (bReCalc) { responseData.data = await ctx.service.stagePay.getStagePays(ctx.stage); await payCalculator.calculateAll(responseData.data); await this._updateStageCache(ctx, payCalculator); } else { if (data.updateData instanceof Array) { responseData.data = await ctx.service.stagePay.getStagePay(ctx.stage, this.app._.map(responseData.data, 'id')); } else { responseData.data = await ctx.service.stagePay.getStagePay(ctx.stage, responseData.data.id); } } break; case 'stage': await ctx.service.stagePay.save(data.updateData); responseData.data = await ctx.service.stagePay.getStagePays(ctx.stage); await payCalculator.calculateAll(responseData.data); await this._updateStageCache(ctx, payCalculator); break; } ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 合同支付 - 章节明细 (Ajax-Post) * @param ctx * @return {Promise} */ async chapterDetail(ctx) { const _ = this.app._; const fields = ['contract_tp', 'qc_tp', 'gather_tp', 'end_gather_tp']; function assignStageData(chapter, curStage, preStage) { chapter.contract_tp = curStage.contract_tp; chapter.qc_tp = curStage.qc_tp; chapter.gather_tp = ctx.helper.add(curStage.contract_tp, curStage.qc_tp); if (preStage) { chapter.pre_contract_tp = preStage.contract_tp; chapter.pre_qc_tp = preStage.qc_tp; chapter.pre_gather_tp = ctx.helper.add(preStage.contract_tp, preStage.qc_tp); chapter.end_contract_tp = ctx.helper.add(curStage.contract_tp, preStage.contract_tp); chapter.end_qc_tp = ctx.helper.add(curStage.qc_tp, preStage.qc_tp); chapter.end_gather_tp = ctx.helper.add(chapter.gather_tp, chapter.pre_gather_tp); } else { chapter.end_contract_tp = curStage.contract_tp; chapter.end_qc_tp = curStage.qc_tp; chapter.end_gather_tp = chapter.gather_tp; } } try { const chapterDetail = JSON.parse(JSON.stringify(payConst.chapterDetail)); const calcDetail = _.sortBy(chapterDetail, ['cType']); for (const cd of calcDetail) { switch (cd.cType) { case 1: const tp = await ctx.service.stageBills.getSumTotalPriceGcl(ctx.stage, cd.filter); const preTp = await ctx.service.stageBillsFinal.getSumTotalPriceGcl(ctx.stage.tid, ctx.stage.order - 1, cd.filter); assignStageData(cd, tp, preTp); break; case 11: assignStageData(cd, await ctx.service.stageBills.getSumTotalPriceGcl(ctx.stage), await ctx.service.stageBillsFinal.getSumTotalPriceGcl(ctx.stage.tid, ctx.stage.order - 1)); break; case 21: const sum = _.find(calcDetail, { cType: 11 }); const chapters = _.filter(calcDetail, { cType: 1 }); for (const f of fields) { cd[f] = ctx.helper.sub(sum[f], this.ctx.helper.sum(_.map(chapters, f)));// _.sumBy(chapters, f)); } break; case 31: assignStageData(cd, await ctx.service.stageBills.getSumTotalPriceNotGcl(ctx.stage), await ctx.service.stageBillsFinal.getSumTotalPriceNotGcl(ctx.stage.tid, ctx.stage.order - 1)); break; case 41: const sum1 = _.find(calcDetail, { cType: 11 }); const sum2 = _.find(calcDetail, { cType: 31 }); for (const f of fields) { cd[f] = ctx.helper.add(sum1.value, sum2.value); } break; } } ctx.body = { err: 0, msg: '', data: { detail: chapterDetail, decimal: ctx.tender.info.decimal } }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } // 变更令相关 /** * 查询变更令 明细数据(包括附件、变更清单、累计使用情况、本期使用情况) * @param tid * @param sid * @param cid * @return {Promise<{}>} * @private */ async _getChangeDetailData(tid, sid, cid, isImport) { const data = {}; data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid); data.bills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } }); if (isImport) { data.endUsedBills = await this.ctx.service.stageImportChange.getChangeEndUsedData(tid, cid); data.curUsedBills = await this.ctx.service.stageImportChange.getChangeUsedData(sid, cid); } else { data.endUsedBills = await this.ctx.service.stageChange.getUsedData(tid, cid); data.curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, cid); } return data; } /** * 获取变更数据 Post(Ajax) * @param ctx * @return {Promise} */ async getChangeData(ctx) { try { const data = {}; data.tenderInfo = ctx.tender.info; data.ledger = await ctx.service.ledger.getData(ctx.tender.id); data.usedChangesId = await ctx.service.stageChange.getStageUsedChangeId(ctx.stage.id); data.changes = await ctx.service.stageChange.getChangeWithUsedInfo(ctx.stage); if (data.changes.length > 0) { const change = data.changes[0]; change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, false); } data.import_changes = await ctx.service.stageImportChange.getChangeWithUsedInfo(ctx.stage); if (data.import_changes.length > 0) { const change = data.import_changes[0]; change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, true); } data.used_import_cid = await ctx.service.stageImportChange.getStageUsedChangeId(ctx.stage.id); ctx.body = { err: 0, msg: '', data }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } async getImportChangeData(ctx) { try { const data = JSON.parse(ctx.request.body.data); if (!data.import_lid) throw '查询数据错误'; const result = {}; result.import_changes = await ctx.service.stageImportChange.getChangeWithUsedInfo(ctx.stage, data.import_lid); if (result.import_changes.length > 0) { const change = result.import_changes[0]; change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, true); } ctx.body = { err: 0, msg: '', data: result }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 变更令 (Get) * @param ctx * @return {Promise} */ async change(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); renderData.changeConst = changeConst; renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.change); await this.layout('stage/change.ejs', renderData, 'stage/audit_modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage'); } } // 审批相关 /** * 添加审批人 * @param ctx * @return {Promise} */ async addAudit(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); const id = this.app._.toInteger(data.auditorId); if (isNaN(id) || id <= 0) { throw '参数错误'; } // 检查权限等 if (ctx.stage.user_id !== ctx.session.sessionUser.accountId) { throw '您无权添加审核人'; } if (ctx.stage.status === auditConst.status.checking || ctx.stage.status === auditConst.status.checked) { throw '当前不允许添加审核人'; } ctx.stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times); // 检查审核人是否已存在 const exist = this.app._.find(ctx.stage.auditorList, { aid: id }); if (exist) { throw '该审核人已存在,请勿重复添加'; } const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition({ tid: ctx.tender.id, sp_type: shenpiConst.sp_type.stage, sp_status: shenpiConst.sp_status.gdzs }); const is_gdzs = shenpiInfo && ctx.tender.info.shenpi.stage === shenpiConst.sp_status.gdzs ? 1 : 0; const result = await ctx.service.stageAudit.addAuditor(ctx.stage.id, id, ctx.stage.times, is_gdzs); if (!result) { throw '添加审核人失败'; } const auditors = await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, ctx.stage.times); ctx.body = { err: 0, msg: '', data: auditors }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 移除审批人 * @param ctx * @return {Promise} */ async deleteAudit(ctx) { try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); const id = data.auditorId instanceof Number ? data.auditorId : this.app._.toNumber(data.auditorId); if (isNaN(id) || id <= 0) { throw '参数错误'; } const result = await ctx.service.stageAudit.deleteAuditor(ctx.stage.id, id, ctx.stage.times); if (!result) { throw '移除审核人失败'; } const auditors = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times); ctx.body = { err: 0, msg: '', data: auditors }; } catch (err) { ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 上报 * @param ctx * @return {Promise} */ async startAudit(ctx) { try { this._checkStageCanModify(ctx); if (ctx.stage.user_id !== ctx.session.sessionUser.accountId) { throw '您无权上报该期数据'; } if (ctx.stage.status === auditConst.status.checking || ctx.stage.status === auditConst.status.checked) { throw '该期数据当前无法上报'; } await this._checkStageStart(ctx); if (ctx.session.sessionProject.page_show.openStageStart && !ctx.stage.startPermission) { throw '非指定日期范围内无法上报'; } await ctx.service.stageAudit.start(ctx.stage.id, ctx.stage.times); ctx.body = { err: 0, msg: '', data: [] }; } catch (err) { this.log(err); ctx.session.postError = err.toString(); // ctx.redirect(ctx.request.header.referer); ctx.body = this.ajaxErrorBody(err, '上报失败'); } } /** * 审批 * @param ctx * @return {Promise} */ async checkAudit(ctx) { try { this._checkStageCanModify(ctx); if (!this.ctx.stage || (this.ctx.stage.status !== auditConst.status.checking && this.ctx.stage.status !== auditConst.status.checkNoPre)) { throw '当前期数据有误'; } if (!this.ctx.stage.curAuditor || this.ctx.stage.curAuditor.aid !== ctx.session.sessionUser.accountId) { throw '您无权进行该操作'; } const data = JSON.parse(ctx.request.body.data); data.checkType = parseInt(data.checkType); // const data = { // checkType: parseInt(ctx.request.body.checkType), // opinion: ctx.request.body.opinion, // }; if (!data.checkType || isNaN(data.checkType)) { throw '提交数据错误'; } if (data.checkType === auditConst.status.checkNo) { if (!data.checkType || isNaN(data.checkType)) { throw '提交数据错误'; } } await ctx.service.stageAudit.check(ctx.stage.id, data, ctx.stage.times); ctx.body = { err: 0, msg: '', data: [] }; } catch (err) { this.log(err); ctx.body = this.ajaxErrorBody(err, '提交失败'); ctx.session.postError = err.toString(); } } /** * 重新审批 * @param ctx * @return {Promise} */ async checkAuditAgain(ctx) { try { if (ctx.stage.revising) { throw '台账修订中,请勿修改提交期数据'; } if (ctx.query.confirm !== undefined && ctx.query.confirm !== '确认设置终审审批') { throw '请输入正确的文本信息'; } if (ctx.session.sessionUser.loginStatus === 0) { const code = ctx.query.code; const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId); if (!pa.auth_mobile) { throw '未绑定手机号'; } const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId; const cacheCode = await app.redis.get(cacheKey); if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) { throw '验证码不正确!'; } } if ((ctx.stage.auditors[ctx.stage.auditors.length - 1].aid === ctx.session.sessionUser.accountId || (ctx.query.confirm === '确认设置终审审批' && ctx.session.sessionUser.is_admin)) && ctx.stage.status === auditConst.status.checked && ctx.stage.order === ctx.stage.highOrder) { await ctx.service.stageAudit.checkAgain(ctx.stage.id, ctx.stage.times); // ctx.redirect(ctx.request.header.referer); ctx.body = { err: 0, url: ctx.request.header.referer, msg: '', }; } else { throw '您无权进行该操作'; } } catch (err) { this.log(err); // ctx.session.postError = err.toString(); // ctx.redirect(ctx.request.header.referer); ctx.body = { err: 1, // url: ctx.request.header.referer, msg: err, }; } } /** * 清单汇总 页面 (Get) * @param ctx * @return {Promise} */ async gather(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); [renderData.gclSpread, renderData.leafXmjSpread] = await spreadSetting.getStageGatherSpreadSetting(ctx); const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id); renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver; renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.gather); await this.layout('stage/gather.ejs', renderData, 'stage/gather_modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order); } } /** * 审核比较 页面 (Get) * @param ctx * @return {Promise} */ async compare(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); if (ctx.stage.status === auditConst.status.checkNo && !ctx.stage.readOnly) { renderData.compareAuditors = ctx.stage.auditHistory[ctx.stage.curTimes - 2]; } else if (ctx.stage.times !== ctx.stage.curTimes) { renderData.compareAuditors = ctx.stage.auditHistory[ctx.stage.curTimes - 1]; } else { renderData.compareAuditors = ctx.stage.auditors; } renderData.ledgerSpread = JSON.parse(JSON.stringify(spreadConst.stageCompare.ledger)); renderData.posSpread = JSON.parse(JSON.stringify(spreadConst.stageCompare.pos)); renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.compare); await this.layout('stage/compare.ejs', renderData, 'stage/compare_modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order); } } async compareAuditor(ctx) { try { const data = JSON.parse(ctx.request.body.data); const result = { main: null, roles: [], }; if (data.main) { result.main = {}; result.main.ledger = ctx.stage.ledgerHis ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.bills_file) : await ctx.service.ledger.getData(ctx.tender.id); const bpcData = await ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: ctx.stage.id } }); if (bpcData.length > 0) { this.ctx.helper.assignRelaData(result.main.ledger, [ { data: bpcData, fields: ['pc_tp', 'org_price'], prefix: '', relaId: 'lid' }, ]); } result.main.pos = ctx.stage.ledgerHis ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.pos_file) : await ctx.service.pos.getPosData({ tid: ctx.tender.id }); } for (const order of data.roles) { const data = { order, bills: [], pos: [] }; const compareTimes = ctx.stage.status === auditConst.status.checkNo && !ctx.stage.readOnly ? ( order === 0 ? ctx.stage.curTimes : ctx.stage.curTimes - 1) : ctx.stage.curTimes; data.bills = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, compareTimes, order); data.pos = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id, ctx.stage.id, compareTimes, order); result.roles.push(data); } ctx.body = { err: 0, msg: '', data: result }; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } async bwtz(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.bwtz); const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo); renderData.ex_memo1 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo1'; }); renderData.ex_memo2 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo2'; }); renderData.ex_memo3 = sjsRela.ledgerCol.find(x => { return x.field === 'ex_memo3'; }); await this.layout('stage/bwtz.ejs', renderData, 'stage/audit_modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order); } } async loadBwtz(ctx) { try { this._getLedgerColumn(); const data = ctx.request.body.data ? JSON.parse(ctx.request.body.data) : {}; const ledgerData = await this._getStageLedgerData(ctx); const posData = await this._getStagePosData(ctx); const changeData = await this._getStageChangeData(ctx); const convert = new billsPosConvert(ctx); convert.loadData(ledgerData, posData, changeData); const result = convert.convert(data.filter); // const wbsCodeHis = await ctx.service.externalData.getExValue(ctx.tender.id, -1, // externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode) || []; // const [result, needUpdate] = convert.convertByWbsCode(wbsCodeHis); // if (needUpdate) { // await ctx.service.externalData.saveExValue(ctx.tender.id, -1, // externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode, wbsCodeHis); // } ctx.body = { err: 0, msg: '', data: result }; } catch (err) { this.log(err); ctx.body = this.ajaxErrorBody(err, '加载部位台账数据错误'); } } /** * 检查当前期当前用户是否在审核列表中,如果是的话允许再次上传附件 * @param {Object} ctx 上下文 */ _checkStageCanModifyRe(ctx) { // 检查登录用户,是否可操作 // if (ctx.stage.readOnly) { // if (ctx.stage.status === auditConst.status.checked) { // // 当前期状态为完成,且提交人是审核列表中的则可再次上传 // if (ctx.stage.user_id === ctx.session.sessionUser.accountId || ctx.stage.auditors.findIndex(auditor => auditor.aid === ctx.session.sessionUser.accountId) !== -1) { // // 再次上传的图片要给个标识,方便给前端进行编辑操作 // // ctx.reUploadPermission = true; // return; // } // throw '该计量期当前您无权操作'; // } else { // throw '该计量期当前您无权操作'; // } // } if (ctx.stage.revising) { throw '台账修订中,请勿修改提交期数据'; } } /** * 上传附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async uploadFile(ctx) { const responseData = { err: 0, msg: '', data: [], }; let stream; try { this._checkStageCanModifyRe(ctx); const parts = ctx.multipart({ autoFields: true }); const files = []; let index = 0; const extra_upload = ctx.stage.status === auditConst.status.checked; while ((stream = await parts()) !== undefined) { // 判断用户是否选择上传文件 if (!stream.filename) { throw '请选择上传的文件!'; } // const dirName = 'app/public/upload/stage/' + moment().format('YYYYMMDD'); // 判断文件夹是否存在,不存在则直接创建文件夹 // if (!fs.existsSync(path.join(this.app.baseDir, dirName))) { // await fs.mkdirSync(path.join(this.app.baseDir, dirName)); // } const fileInfo = path.parse(stream.filename); const create_time = Date.parse(new Date()) / 1000; const filepath = `app/public/upload/${this.ctx.tender.id}/stage/fujian_${create_time + index.toString() + fileInfo.ext}`; // await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, filepath)); await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream); // const fileInfo = path.parse(stream.filename); // const fileName = 'stage' + create_time + '_' + index + fileInfo.ext; // 保存文件 // await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, dirName, fileName)); if (stream) { await sendToWormhole(stream); } // 保存数据到att表 const fileData = { tid: ctx.params.id, sid: ctx.params.order, in_time: create_time, filename: fileInfo.name, fileext: fileInfo.ext, filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size, filepath, extra_upload, }; // if (ctx.reUploadPermission) { // fileData.re_upload = 1; // } const result = await ctx.service.stageAtt.save(parts.field, fileData, ctx.session.sessionUser.accountId); if (!result) { throw '导入数据库保存失败'; } const attData = await ctx.service.stageAtt.getDataByFid(result.insertId); attData.in_time = moment(create_time * 1000).format('YYYY-MM-DD'); files.length !== 0 ? files.unshift(attData) : files.push(attData); ++index; } responseData.data = files; } catch (err) { this.log(err); // 失败需要消耗掉stream 以防卡死 if (stream) { await sendToWormhole(stream); } this.setMessage(err.toString(), this.messageType.ERROR); responseData.err = 1; responseData.msg = err.toString(); } ctx.body = responseData; } /** * 下载附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async downloadFile(ctx) { const id = ctx.params.fid; if (id) { try { const fileInfo = await ctx.service.stageAtt.getDataById(id); if (fileInfo !== undefined && fileInfo !== '') { // const fileName = path.join(this.app.baseDir, fileInfo.filepath); // 解决中文无法下载问题 const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase(); let disposition = ''; if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) { disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext); } else if (userAgent.indexOf('firefox') >= 0) { disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"'; } else { /* safari等其他非主流浏览器只能自求多福了 */ disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary'); } ctx.response.set({ 'Content-Type': 'application/octet-stream', 'Content-Disposition': disposition, 'Content-Length': fileInfo.filesize, }); // ctx.body = await fs.createReadStream(fileName); ctx.body = await ctx.helper.ossFileGet(fileInfo.filepath); } else { throw '不存在该文件'; } } catch (err) { this.log(err); this.setMessage(err.toString(), this.messageType.ERROR); } } } /** * 查看附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async checkFile(ctx) { const responseData = { err: 0, msg: '' }; const { id = '' } = JSON.parse(ctx.request.body.data); if (id) { try { const fileInfo = await ctx.service.stageAtt.getDataById(id); if (fileInfo && Object.keys(fileInfo).length) { let filepath = fileInfo.filepath; if (!ctx.helper.canPreview(fileInfo.fileext)) { filepath = `/tender/${ctx.tender.id}/measure/stage/${ctx.params.order}/download/file/${fileInfo.id}`; } else { // filepath = filepath.replace(/^app|\/app/, ''); filepath = ctx.app.config.fujianOssPath + filepath; } fileInfo.filepath && (responseData.data = { filepath }); } } catch (error) { this.log(error); this.setMessage(error.toString(), this.messageType.ERROR); responseData.err = 1; responseData.msg = error.toString(); } } ctx.body = responseData; } /** * 删除附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async deleteFile(ctx) { const responseData = { err: 0, msg: '', data: '', }; try { this._checkStageCanModifyRe(ctx); const data = JSON.parse(ctx.request.body.data); const fileInfo = await ctx.service.stageAtt.getDataById(data.id); if (!fileInfo || !Object.keys(fileInfo).length) { throw '该文件不存在'; } if (!fileInfo.extra_upload && ctx.stage.status === auditConst.status.checked) { throw '无权限删除'; } if (fileInfo !== undefined && fileInfo !== '') { // 先删除文件 // await fs.unlinkSync(path.join(this.app.baseDir, fileInfo.filepath)); await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + fileInfo.filepath); // 再删除数据库 await ctx.service.stageAtt.deleteById(data.id); responseData.data = ''; } else { throw '不存在该文件'; } // if (data.tid === undefined || data.uci === undefined || data.uc === undefined || data.ac === undefined) { // throw '参数有误'; // } // const [addCompany, selectCompany] = await ctx.service.changeCompany.setCompanyList(data); // responseData.data = { add: addCompany, select: selectCompany }; } catch (err) { responseData.err = 1; responseData.msg = err; } ctx.body = responseData; } /** * 保存附件(或替换) * @param {Object} ctx - egg全局变量 * @return {void} */ async saveFile(ctx) { const responseData = { err: 0, msg: '', data: [], }; let stream; try { this._checkStageCanModifyRe(ctx); stream = await ctx.getFileStream({ requireFile: false }); let fileData = {}; if (stream.filename !== undefined) { const create_time = Date.parse(new Date()) / 1000; const fileInfo = path.parse(stream.filename); // const dirName = 'app/public/upload/stage/' + moment().format('YYYYMMDD'); const dirName = `app/public/upload/${this.ctx.tender.id}/stage`; const fileName = 'fujian_' + create_time + fileInfo.ext; // 保存文件 // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName)); await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + path.join(dirName, fileName), stream); // 保存数据到att表 fileData = { filesize: stream.fields.size, filepath: path.join(dirName, fileName), }; } const result = await ctx.service.stageAtt.updateByID(stream.fields, fileData); if (!result) { throw '导入数据库保存失败'; } const attData = await ctx.service.stageAtt.getDataByFid(stream.fields.id); attData.in_time = moment(attData.in_time * 1000).format('YYYY-MM-DD'); responseData.data = attData; } catch (err) { this.log(err); // 失败需要消耗掉stream 以防卡死 if (stream) { await sendToWormhole(stream); } this.setMessage(err.toString(), this.messageType.ERROR); responseData.err = 1; responseData.msg = err.toString(); } ctx.body = responseData; } /** * 批量下载 - 压缩成zip文件返回 * @param {Object} ctx - 全局上下文 */ async downloadZip(ctx) { const zipFilename = `${ctx.tender.data.name}-计量台账-${ctx.params.order}-附件.zip`; const time = Date.now(); const zipPath = `app/public/upload/${ctx.tender.id}/stage/fu_jian_zip${time}.zip`; const responseData = { err: 0, msg: '', }; try { const { fileIds = [] } = JSON.parse(ctx.request.body.data); // const fileIds = JSON.parse(ctx.request.query.fileIds); const size = await ctx.service.stageAtt.compressedFile(fileIds, zipPath); // 解决中文无法下载问题 const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase(); let disposition = ''; if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) { disposition = 'attachment; filename=' + encodeURIComponent(zipFilename); } else if (userAgent.indexOf('firefox') >= 0) { disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(zipFilename) + '"'; } else { /* safari等其他非主流浏览器只能自求多福了 */ disposition = 'attachment; filename=' + new Buffer(zipFilename).toString('binary'); } ctx.response.set({ 'Content-Type': 'application/octet-stream', 'Content-Disposition': disposition, 'Content-Length': size, }); const readStream = fs.createReadStream(path.join(this.app.baseDir, zipPath)); ctx.body = readStream; readStream.on('close', () => { if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) { fs.unlinkSync(path.resolve(this.app.baseDir, zipPath)); } }); // fs的错误不能被try catch捕捉 readStream.on('error', err => { this.log(err); if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) { fs.unlinkSync(path.resolve(this.app.baseDir, zipPath)); } responseData.err = 1; responseData.msg = err.toString(); ctx.body = responseData; }); } catch (err) { this.log(err); if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) { fs.unlinkSync(path.resolve(this.app.baseDir, zipPath)); } this.setMessage(err.toString(), this.messageType.ERROR); responseData.err = 1; responseData.msg = err.toString(); ctx.body = responseData; } } /** * 合同支付上传附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async payUploadFile(ctx) { const responseData = { err: 0, msg: '', data: [], }; let stream; try { const parts = ctx.multipart({ autoFields: true }); const files = []; let index = 0; const create_time = Date.parse(new Date()) / 1000; while ((stream = await parts()) !== undefined) { // 判断用户是否选择上传文件 if (!stream.filename) { throw '请选择上传的文件!'; } const fileInfo = path.parse(stream.filename); // const dirName = 'app/public/upload/pay/' + moment().format('YYYYMMDD'); // const dirName = 'app/public/upload/' + this.ctx.tender.id + '/pay'; // const dirName = `app/public/upload/${this.ctx.tender.id}/pay`; // const fileName = 'pay' + create_time + '_' + index + fileInfo.ext; const filepath = `app/public/upload/${this.ctx.tender.id}/pay/fujian_${create_time + index.toString() + fileInfo.ext}`; // 判断文件夹是否存在,不存在则直接创建文件夹 // if (!fs.existsSync(path.join(this.app.baseDir, dirName))) { // await fs.mkdirSync(path.join(this.app.baseDir, dirName)); // } // 保存文件 // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName)); await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream); await sendToWormhole(stream); // 插入到stage_pay对应的附件列表中 const attData = { tid: this.ctx.tender.id, sid: this.ctx.stage.id, uid: ctx.session.sessionUser.accountId, pid: parts.field.pay_id, filename: fileInfo.name, fileext: fileInfo.ext, filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size, filepath, in_time: moment(create_time * 1000).format('YYYY-MM-DD'), renew: this.ctx.stage.status === auditConst.status.checked, }; attData.username = ctx.session.sessionUser.name; files.length !== 0 ? files.unshift(attData) : files.push(attData); ++index; } responseData.data = await ctx.service.payAtt.addFiles(files); } catch (err) { this.log(err); // 失败需要消耗掉stream 以防卡死 if (stream) { await sendToWormhole(stream); } this.setMessage(err.toString(), this.messageType.ERROR); responseData.err = 1; responseData.msg = err.toString(); } ctx.body = responseData; } /** * 合同支付下载附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async payDownloadFile(ctx) { const id = ctx.params.fid; try { if (!id) throw '数据错误'; const fileInfo = await ctx.service.payAtt.getDataById(id); if (!fileInfo) throw '不存在该文件'; // const fileName = path.join(this.app.baseDir, fileInfo.filepath); // 解决中文无法下载问题 const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase(); let disposition = ''; if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) { disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext); } else if (userAgent.indexOf('firefox') >= 0) { disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"'; } else { /* safari等其他非主流浏览器只能自求多福了 */ disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary'); } ctx.response.set({ 'Content-Type': 'application/octet-stream', 'Content-Disposition': disposition, 'Content-Length': fileInfo.filesize, }); // ctx.body = await fs.createReadStream(fileName); ctx.body = await ctx.helper.ossFileGet(fileInfo.filepath); } catch (err) { this.log(err); this.setMessage(err.toString(), this.messageType.ERROR); } } /** * 合同支付删除附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async payDeleteFile(ctx) { try { const data = JSON.parse(ctx.request.body.data); const fileInfo = await ctx.service.payAtt.getDataById(data.id); if (!fileInfo) throw '不存在该文件'; await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + fileInfo.filepath); const result = await ctx.service.payAtt.delFiles(data.id); // if (fs.existsSync(path.join(ctx.app.baseDir, fileInfo.filepath))) { // await fs.unlinkSync(path.join(ctx.app.baseDir, fileInfo.filepath)); // } ctx.body = { err: 0, msg: '', data: result }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '删除文件错误'); } } /** * 期审批管理 * @param {Object} ctx - egg全局变量 * @return {void} */ async manager(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); // 获取已完成期审批列表 const lastStage = await ctx.service.stage.getLastestStage(ctx.tender.id, true); const auditList = []; if (lastStage) { renderData.lastStageUser = await ctx.service.projectAccount.getDataById(lastStage.user_id); const times = lastStage.status !== auditConst.status.checkNo ? lastStage.times : lastStage.times - 1; for (let i = times; i > 0; i--) { const timeAuditList = await ctx.service.stageAudit.getAuditors(lastStage.id, i, 'desc'); auditList.push(timeAuditList); } } renderData.lastStage = lastStage; renderData.lastAuditList = auditList; renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.manager); await this.layout('stage/manager.ejs', renderData, 'stage/manager_modal.ejs'); } catch (err) { this.log(err); ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage'); } } /** * 删除本次审批 - 期审批管理页面 * @param ctx * @return {Promise} */ async managerAuditDelete(ctx) { try { if (ctx.stage.revising) { throw '台账修订中,请勿修改提交期数据'; } if (ctx.request.body.confirm !== undefined && ctx.request.body.confirm !== '确认删除本次审批') { throw '请输入正确的文本信息'; } if (ctx.request.body.confirm === '确认删除本次审批' && ctx.session.sessionUser.is_admin && ctx.stage.order === ctx.stage.highOrder && ctx.stage.times !== 1) { await ctx.service.stageAudit.timesDelete(); ctx.redirect(ctx.request.header.referer); } else { throw '您无权进行该操作'; } } catch (err) { this.log(err); ctx.session.postError = err.toString(); ctx.redirect(ctx.request.header.referer); } } /** * 删除本次审批 - 期审批管理页面 * @param ctx * @return {Promise} */ async saveCooperationData(ctx) { try { const data = JSON.parse(ctx.request.body.data); const responseData = { err: 0, msg: '', data: {} }; switch (data.type) { case 'save-confirm': await ctx.service.cooperationConfirm.save(data.postData); responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId); break; case 'del-confirm': await ctx.service.cooperationConfirm.del(data.postData); responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId); break; default: throw '参数有误'; } ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 收方单操作 * @param ctx * @return {Promise} */ async saveShoufang(ctx) { try { if (ctx.session.sessionUser.accountId !== ctx.stage.user_id) { throw '没有权限操作收方单'; } const data = JSON.parse(ctx.request.body.data); const responseData = { err: 0, msg: '', data: {} }; switch (data.type) { case 'add': // 判断收方单是否已存在 const sfData = await ctx.service.stageShoufang.getDataByCondition({ sid: ctx.stage.id, lid: data.postData.lid, pid: data.postData.pid }); if (sfData) { throw '该收方单已存在,不必重复生成'; } const result = await ctx.service.stageShoufang.add(data.postData); // responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId); const info = await ctx.service.stageShoufang.getDataById(result.insertId); info.qrcode = ctx.app.config.fujianOssPath + info.qrcode; responseData.data = info; break; case 'del': // 判断收方单是否已存在 const sfData2 = await ctx.service.stageShoufang.getDataById(data.postData.sfid); if (!sfData2) { throw '该收方单已删除,请刷新'; } await ctx.service.stageShoufang.del(data.postData.sfid); // responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId); break; default: throw '参数有误'; } ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } /** * 收方单附件 * @param ctx * @return {Promise} */ async shoufangAtt(ctx) { try { const data = JSON.parse(ctx.request.body.data); const responseData = { err: 0, msg: '', data: {} }; const files = await ctx.service.stageShoufangAtt.getListBySfid(data.sfid); responseData.data = files; ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } async stashList(ctx) { try { if (!ctx.session.sessionUser.is_admin) throw '您无权查看'; const data = await ctx.service.stageStash.getValidStash(ctx.tender.id); data.forEach(x => { x.recover = ctx.stage.status === auditConst.status.uncheck; }); ctx.body = { err: 0, msg: '', data }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '加载暂存计量数据失败'); } } async addStash(ctx) { try { if (!ctx.session.sessionUser.is_admin) throw '您无权操作'; const result = await ctx.service.stageStash.addStash(ctx.stage, ''); const stash = await ctx.service.stageStash.getDataById(result); stash.recover = ctx.stage.status === auditConst.status.uncheck; ctx.body = { err: 0, msg: '', data: stash }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '保存当前计量数据失败'); } } async recoverStash(ctx) { try { if (!ctx.session.sessionUser.is_admin) throw '您无权操作'; if (!ctx.stage.status === auditConst.status.uncheck) throw '仅新增期且未上报时,可从暂存计量中导入数据'; const data = JSON.parse(ctx.request.body.data); await ctx.service.stageStash.recover(ctx.stage, data.id); await ctx.service.stage.updateCheckCalcFlag(ctx.stage, true); await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = { err: 0, msg: '', data: null }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '保存当前计量数据失败'); } } async delStash(ctx) { try { if (!ctx.session.sessionUser.is_admin) throw '您无权操作'; const data = JSON.parse(ctx.request.body.data); await ctx.service.stageStash.delStash(data.id); ctx.body = { err: 0, msg: '', data: null }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '删除数据失败'); } } async importStageSheet(ctx) { try { if (!ctx.stage.status === auditConst.status.uncheck) throw '仅新增期且未上报时,可从暂存计量中导入数据'; const compressData = ctx.request.body.data; const data = JSON.parse(LzString.decompressFromUTF16(compressData)); await ctx.service.stageStash.loadExcelSheet(ctx.stage, data.sheet); await ctx.service.stage.updateCheckCalcFlag(ctx.stage, true); await ctx.service.stage.updateCacheTime(ctx.stage.id); ctx.body = { err: 0, msg: '', data: null }; } catch (err) { ctx.log(err); ctx.ajaxErrorBody(err, '导入计量台账数据错误'); } } } return StageController; };