'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 payConst = require('../const/deal_pay.js'); const externalDataConst = require('../const/external_data.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'); 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, }, }; 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; } /** * 获取SpreadSetting * @private */ _getSpreadSetting() { const _ = this.app._; function removeFieldCols(setting, cols) { _.remove(setting.cols, function(c) { return cols.indexOf(c.field) > -1; }); } function setColFormat(cols, field, formatter) { const filterCols = cols.filter(function(c) { return c.field.search(field) !== -1; }); for (const col of filterCols) { col.formatter = formatter; } } // const tpFormatter = this.ctx.helper.getNumberFormatter(this.ctx.tender.info.decimal.tp); // const upFormatter = this.ctx.helper.getNumberFormatter(2); const tender = this.ctx.tender, stage = this.ctx.stage; const stageSetting = tender.data.measure_type === measureType.tz.value ? spreadConst.stageTz : (tender.info.display.ledger.clQty ? spreadConst.stageCl : spreadConst.stageNoCl); const ledger = JSON.parse(JSON.stringify(stageSetting.ledger)); // setColFormat(ledger.cols, 'unit_price', upFormatter); // setColFormat(ledger.cols, 'total_price', tpFormatter); // setColFormat(ledger.cols, 'tp', tpFormatter); if (!tender.info.display.ledger.dgnQty) { removeFieldCols(ledger, spreadConst.filterCols.stageDgnCols); } const pos = JSON.parse(JSON.stringify(stageSetting.pos)); if (!tender.info.display.stage.realComplete) { removeFieldCols(pos, spreadConst.filterCols.realCompleteCols); } if (!this.ctx.session.sessionProject.gxby) { removeFieldCols(ledger, spreadConst.filterCols.thirdPartyCols.gxby); removeFieldCols(pos, spreadConst.filterCols.thirdPartyCols.gxby); } if (!this.ctx.session.sessionProject.dagl) { removeFieldCols(ledger, spreadConst.filterCols.thirdPartyCols.dagl); removeFieldCols(pos, spreadConst.filterCols.thirdPartyCols.dagl); } if (this.ctx.stage.readOnly || this.ctx.stage.revising) { ledger.readOnly = true; pos.readOnly = true; } return [ledger, pos]; } /** * 获取审批界面所需的 原报、审批人数据等 * @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 = await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, 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); } } _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); [renderData.ledgerSpread, renderData.posSpread] = this._getSpreadSetting(); 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; 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; } async _getStageLedgerData(ctx) { const ledgerData = await ctx.service.ledger.getData(ctx.tender.id); const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id); for (const d of dgnData) { const l = ctx.app._.find(ledgerData, { id: d.id }); ctx.app._.assignIn(l, d); } let curStageData, preStageData; // 当前操作人查看最新数据,其他人查看历史数据 if (ctx.stage.readOnly) { curStageData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder); } else { curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, ctx.stage.id); const surplus = this._checkUniqData(curStageData, 'lid'); if (surplus.length > 0) { await ctx.service.stageBills.deleteById(surplus); } } // 查询截止上期数据 if (ctx.stage.order > 1) { preStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1); // renderData.preStageData = await ctx.service.stageBills.getEndStageData(ctx.tender.id, ctx.stage.order - 1); } else { preStageData = []; } this.ctx.helper.assignRelaData(ledgerData, [ { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' }, { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_', relaId: 'lid' }, ]); return ledgerData; } async _getStagePosData(ctx) { let curStageData, preStageData; const posData = await ctx.service.pos.getPosDataWithAddStageOrder({ tid: ctx.tender.id }); // 根据当前人,或指定对象查询数据 // console.time('cur'); 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); } } // console.timeEnd('cur'); // 查询截止上期数据 // console.time('pre'); if (ctx.stage.order > 1) { preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1); // responseData.data.preStageData = await ctx.service.stagePos.getEndStageData(ctx.tender.id, ctx.stage.order - 1); } else { preStageData = []; } // console.timeEnd('pre'); // console.time('assign'); // console.log('cur: ' + curStageData.length); // console.log('pre: ' + preStageData.length); this.ctx.helper.assignRelaData(posData, [ { data: curStageData, fields: ['contract_qty', 'contract_expr', 'qc_qty', 'postil'], prefix: '', relaId: 'pid' }, { data: preStageData, fields: ['contract_qty', 'qc_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: {} }; for (const f of filter) { switch (f) { case 'ledger': responseData.data.ledgerData = await this._getStageLedgerData(ctx); break; case 'pos': responseData.data.posData = await this._getStagePosData(ctx); break; case 'detail': responseData.data.detailData = await this._getStageDetailData(ctx); break; case 'change': responseData.data.changeData = await this._getStageChangeData(ctx); break; case 'dealBills': responseData.data.dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: this.ctx.tender.id }, }); break; } } ctx.body = responseData; } catch (err) { this.log(err); ctx.body = { err: 1, msg: err.toString(), data: null }; } } async check(ctx) { try { const ledgerData = await this._getStageLedgerData(ctx); const posData = await this._getStagePosData(ctx); const qtyData = ctx.helper.checkBillsWithPos(ledgerData, posData, ['contract_qty', 'qc_qty']); qtyData.error.forEach(x => { x.errorType = 'qty'; }); const tpData = ctx.helper.checkBillsTp(ledgerData, [ {qty: 'contract_qty', tp: 'contract_tp'}, {qty: 'qc_qty', tp: 'qc_tp'} ], this.ctx.tender.info.decimal); tpData.error.forEach(x => { x.errorType = 'tp'; }); ctx.body = { err: 0, msg: '', data: { error: [...qtyData.error, ...tpData.error], source: { bills: [...qtyData.source.bills, ...tpData.source.bills], pos: [...qtyData.source.pos, ...tpData.source.pos], }, }}; } 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); // 根据当前人,或指定对象查询数据 // console.time('cur'); 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); } // console.timeEnd('cur'); // 查询截止上期数据 // console.time('pre'); if (ctx.stage.order > 1) { preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1); // responseData.data.preStageData = await ctx.service.stagePos.getEndStageData(ctx.tender.id, ctx.stage.order - 1); } else { preStageData = []; } // console.timeEnd('pre'); // console.time('assign'); // console.log('cur: ' + curStageData.length); // console.log('pre: ' + preStageData.length); this.ctx.helper.assignRelaData(responseData.data, [ { data: curStageData, fields: ['contract_qty', 'qc_qty', 'postil'], prefix: '', relaId: 'pid' }, { data: preStageData, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid' }, ]); // console.timeEnd('assign'); 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) { 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 changes = ctx.stage.readOnly ? await ctx.service.change.getAuditValidChanges(ctx.tender.id, bills, null, ctx.stage.curTimes, ctx.stage.curOrder) : await ctx.service.change.getValidChanges(ctx.tender.id, bills); 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.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.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 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); 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.getAuditorStageData(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.getLastestStageData(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') { 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.updateType; delete data.img; 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 _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); const dealPay = await ctx.service.stagePay.getStagePays(ctx.stage); // 附件不取下载地址 for (const index in dealPay) { if (dealPay[index].attachment !== null) { const attachments = JSON.parse(dealPay[index].attachment); for (const att_index in attachments) { delete attachments[att_index].filepath; attachments[att_index].username = (await ctx.service.projectAccount.getAccountInfoById(attachments[att_index].uid)).name; } dealPay[index].attachment = attachments; } } renderData.dealPay = dealPay; 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.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.stage.user_id) || (ctx.stage.status === auditConst.status.checkNoPre && ctx.session.sessionUser.accountId === ctx.stage.curAuditor.aid) || (ctx.stage.status === auditConst.status.checking && ctx.stage.curAuditor && ctx.stage.curAuditor.aid === ctx.session.sessionUser.accountId); if (!ctx.stage.readOnly) { // 计算 本期金额 const payCalculator = new PayCalculator(ctx, ctx.stage, ctx.tender.info); await payCalculator.calculateAll(renderData.dealPay); await this._updateStageCache(ctx, payCalculator); // renderData.calcBase = payCalculator.bases; } 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); assignStageData(cd, tp); break; case 11: assignStageData(cd, await ctx.service.stageBills.getSumTotalPriceGcl(ctx.stage)); 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)); 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) { const data = {}; data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid); data.bills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } }); data.addUsedBills = 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.change.getChangeAndUsedInfo(ctx.tender.id); if (data.changes.length > 0) { const change = data.changes[0]; change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid); } ctx.body = { err: 0, msg: '', data }; } 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 result = await ctx.service.stageAudit.addAuditor(ctx.stage.id, id, ctx.stage.times); if (!result) { throw '添加审核人失败'; } const audit = await ctx.service.stageAudit.getAuditor(ctx.stage.id, id, ctx.stage.times); ctx.body = { err: 0, msg: '', data: audit }; } 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 ctx.service.stageAudit.start(ctx.stage.id, ctx.stage.times); ctx.body = { err: 0, msg: '', data: [] }; } catch (err) { this.log(err); 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); // console.log(cacheCode); 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, }; } } // 清单汇总相关 _getGatherSpreadSetting() { const _ = this.app._; function removeFieldCols(setting, cols) { _.remove(setting.cols, function(c) { return cols.indexOf(c.field) > -1; }); } function setColFormat(cols, field, formatter) { const filterCols = cols.filter(function(c) { return c.field.search(field) !== -1; }); for (const col of filterCols) { col.formatter = formatter; } } // const tpFormatter = this.ctx.helper.getNumberFormatter(this.ctx.tender.info.decimal.tp); // const upFormatter = this.ctx.helper.getNumberFormatter(2); const gcl = JSON.parse(JSON.stringify(spreadConst.stageGather.gcl)); // setColFormat(gcl.cols, 'unit_price', upFormatter); // setColFormat(gcl.cols, 'total_price', tpFormatter); // setColFormat(gcl.cols, 'tp', tpFormatter); const leafXmj = JSON.parse(JSON.stringify(spreadConst.stageGather.leafXmj)); const tender = this.ctx.tender; if (tender.data.measure_type === measureType.tz.value) { removeFieldCols(gcl, spreadConst.filterCols.tzWithoutCols); } return [gcl, leafXmj]; } /** * 清单汇总 页面 (Get) * @param ctx * @return {Promise} */ async gather(ctx) { try { await this._getStageAuditViewData(ctx); const renderData = await this._getDefaultRenderData(ctx); [renderData.gclSpread, renderData.leafXmjSpread] = this._getGatherSpreadSetting(); 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.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 = await ctx.service.ledger.getData(ctx.tender.id); result.main.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id }); } for (const order of data.roles) { const data = { order, bills: [], pos: [] }; data.bills = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, order); data.pos = await ctx.service.stagePos.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, 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); 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 { 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; 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)); // console.log(await fs.existsSync(path.resolve(this.app.baseDir, 'app', filepath))); // 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, }; 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); } else { throw '不存在该文件'; } } catch (err) { this.log(err); console.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/, ''); } 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 !== undefined && fileInfo !== '') { // 先删除文件 await fs.unlinkSync(path.join(this.app.baseDir, 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 fileName = 'stage' + create_time + 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)); // 保存数据到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) { try { const fileIds = JSON.parse(ctx.request.query.fileIds); 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 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, }); ctx.body = fs.createReadStream(path.join(this.app.baseDir, zipPath)); // ctx.body = fs.readFileSync(path.resolve(this.app.baseDir, zipPath)); if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) { fs.unlinkSync(path.resolve(this.app.baseDir, zipPath)); } } catch (error) { this.log(error); } } /** * 合同支付上传附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async payUploadFile(ctx) { const responseData = { err: 0, msg: '', data: [], }; let stream; try { this._checkStageCanModify(ctx); 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 fileName = 'pay' + create_time + '_' + index + 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 sendToWormhole(stream); // 插入到stage_pay对应的附件列表中 const attData = { filename: fileInfo.name, fileext: fileInfo.ext, filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size, filepath: path.join(dirName, fileName), uid: ctx.session.sessionUser.accountId, in_time: moment(create_time * 1000).format('YYYY-MM-DD'), }; const result = await ctx.service.stagePay.saveAtt(parts.field.pay_id, attData); if (!result) { throw '导入数据库保存失败'; } delete attData.filepath; attData.username = ctx.session.sessionUser.name; 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 payDownloadFile(ctx) { const id = ctx.params.pid; const index = ctx.params.index; if (id && index) { try { const payInfo = await ctx.service.stagePay.getDataById(id); if (payInfo !== undefined && payInfo.attachment !== null) { const fileInfo = JSON.parse(payInfo.attachment)[index]; 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); } else { throw '不存在该文件'; } } catch (err) { this.log(err); this.setMessage(err.toString(), this.messageType.ERROR); } } } /** * 合同支付删除附件 * @param {Object} ctx - egg全局变量 * @return {void} */ async payDeleteFile(ctx) { const responseData = { err: 0, msg: '', data: '', }; try { this._checkStageCanModify(ctx); const data = JSON.parse(ctx.request.body.data); const payInfo = await ctx.service.stagePay.getDataById(data.id); if (payInfo !== undefined) { const fileInfo = JSON.parse(payInfo.attachment)[data.index]; // 先删除文件 await fs.unlinkSync(path.join(this.app.baseDir, fileInfo.filepath)); // 再删除数据库 const attachment = JSON.parse(payInfo.attachment); attachment.splice(data.index, 1); const result = await ctx.service.stagePay.deleteAtt(data.id, attachment); 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 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); } } } return StageController; };