'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 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 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;
        }

        /**
         * 获取通用的renderData（用于layout, Menu, subMenu部分）
         * @param ctx
         * @returns {{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,
            };
            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'],
                });
                data.accountList = accountList;
            }
            data.tenderMenu.back.children[0].url = '/tender/' + ctx.tender.id + '/measure/stage';
            return data;
        }

        _stageReadOnly(stage) {
            return stage.status === auditConst.status.checked;
        }

        /**
         * 获取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 (this.ctx.stage.readOnly) {
                ledger.readOnly = true;
                pos.readOnly = true;
            }
            return [ledger, pos];
        }

        /**
         * 获取审批界面所需的 原报、审批人数据等
         * @param ctx
         * @returns {Promise<void>}
         * @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 (ctx.stage.times > 1) {
                for (let i = 1; i < ctx.stage.times; i++) {
                    ctx.stage.auditHistory.push(await ctx.service.stageAudit.getAuditors(ctx.stage.id, i));
                }
            }
            // 获取审批流程中左边列表
            ctx.stage.auditors2 = await ctx.service.stageAudit.getAuditGroupByList(ctx.stage.id, times);
            if (ctx.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) {
                ctx.stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times);
            }
        }

        /**
         * 期计量页面 (Get)
         * @param {Object} ctx - egg全局变量
         * @returns {Promise<void>}
         */
        async index(ctx) {
            try {
                await this._getStageAuditViewData(ctx);
                const renderData = await this._getDefaultRenderData(ctx);
                [renderData.ledgerSpread, renderData.posSpread] = this._getSpreadSetting();
                renderData.changeConst = changeConst;
                renderData.ledgerData = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
                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);
                }
                // 查询截止上期数据
                if (ctx.stage.order > 1) {
                    preStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage);
                    //renderData.preStageData = await ctx.service.stageBills.getEndStageData(ctx.tender.id, ctx.stage.order - 1);
                } else {
                    preStageData = [];
                }
                this.ctx.helper.assignRelaData(renderData.ledgerData, [
                    {data: curStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid',},
                    {data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid',}
                ]);
                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.index);
                renderData.whiteList = this.ctx.app.config.multipart.whitelist;
                // 获取附件列表
                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');
            }
        }

        /**
         * 获取期数据(截止上期 & 本期) (Ajax)
         * @param ctx
         * @returns {Promise<void>}
         */
        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.getPosData(condition);
                // 根据当前人，或指定对象查询数据
                console.time('cur');
                const curWhere = JSON.parse(ctx.request.body.data);
                if (ctx.stage.readOnly) {
                    curStageData = await ctx.service.stagePos.getAuditorStageData(ctx.tender.id,
                        ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder, curWhere);
                } else {
                    curStageData = await ctx.service.stagePos.getLastestStageData(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);
                    //responseData.data.preStageData = await ctx.service.stagePos.getEndStageData(ctx.tender.id, ctx.stage.order - 1);
                } else {
                    preStageData = [];
                }
                console.timeEnd('pre');
                console.time('assign');
                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
         * @returns {Promise<void>}
         */
        async updateStageData(ctx) {
            try {
                // 检查登录用户，是否可操作
                if (ctx.stage.readOnly) {
                    throw '该计量期当前您无权操作';
                }

                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) {
                    responseData.data.curStageData = await ctx.service.stageBills.updateStageData(data.bills);
                }
                await this.ctx.service.stage.updateCheckDetailFlag(ctx.stage.id, true);
                ctx.body = responseData;
            } catch (err) {
                this.log(err);
                ctx.body = {err: 1, msg: err.toString(), data: null};
            }
        }
        /**
         * 查询可用变更令 (Ajax-Post)
         * @param ctx
         * @returns {Promise<void>}
         */
        async searchValidChange(ctx) {
            try {
                // if (ctx.stage.readOnly) {
                //     throw '您无权调用变更令';
                // }

                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 = await ctx.service.change.getValidChanges(ctx.tender.id, bills);
                const useChanges = 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
         * @returns {Promise<void>}
         */
        async useChange(ctx) {
            try {
                if (ctx.stage.readOnly) {
                    throw '您无权使用变更令';
                }

                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);
                } else {
                    result = await ctx.service.stageChange.billsChange(data.target.bills, data.change);
                }
                await this.ctx.service.stage.updateCheckDetailFlag(ctx.stage.id, true);
                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
         * @returns {Promise<void>}
         */
        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
         * @returns {Promise<void>}
         */
        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
         * @returns {Promise<void>}
         */
        async buildDetailData (ctx) {
            try {
                // 检查登录用户，是否可操作
                if (ctx.stage.readOnly) {
                    throw '该计量期当前您无权操作';
                }

                const data = JSON.parse(ctx.request.body.data);
                await ctx.service.stage.buildDetailData(ctx.tender.id, ctx.stage.order, data);

                ctx.body = {err: 0, msg: '', data: null};
            } catch (err) {
                this.log(err);
                ctx.body = {err: 1, msg: err.toString(), data: null};
            }
        }

        /**
         * 加载数据 (Ajax)
         * @param ctx
         * @returns {Promise<void>}
         */
        async loadDetailRelaData(ctx) {
            try {
                const data = JSON.parse(ctx.request.body.data);
                // 加载台账数据
                if (data.loadType === 'ledger') {
                    const ledgerData = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
                    ctx.body = {err: 0, msg: '', data: ledgerData };
                } else if (data.loadType === 'all') {
                    const result = {};
                    result.ledger = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
                    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.getAuditorStageData(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'}
                        ]);
                        result.stageDetail = await ctx.service.stageDetail.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
                    } 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.getLastestStageData(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 ctx.service.stageDetail.getLastestStageData(ctx.tender.id, ctx.stage.id);
                    }
                    console.log(result.length);
                    ctx.body = { err: 0, msg: '', data: result };
                }
            } catch (err) {
                this.log(err);
                ctx.body = { err: 1, msg: err.toString(), data: null };
            }
        }

        /**
         * 中间计量，生成规则，高级设置 (Ajax)
         * @param ctx
         * @returns {Promise<void>}
         */
        async setAdvancedConfig(ctx) {
            try {
                // 检查登录用户，是否可操作
                if (ctx.stage.readOnly) {
                    throw '该计量期当前您无权操作';
                }

                const data = JSON.parse(ctx.request.body.data);
                await ctx.service.stage.update(data, { id: 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
         * @returns {Promise<void>}
         */
        async saveDetailData(ctx) {
            try {
                // 检查登录用户，是否可操作
                if (ctx.stage.readOnly) {
                    throw '该计量期当前您无权操作';
                }

                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);
                }
                ctx.body = responseData;
            } catch (err) {
                this.log(err);
                ctx.body = {err: 1, msg: err.toString(), data: null};
            }
        }

        /**
         * 中间计量，添加草图，上传图片 (Ajax)
         * @param ctx
         * @returns {Promise<void>}
         */
        async addCalcImage(ctx) {
            try {
                if (ctx.stage.readOnly) {
                    throw '该计量期当前您无权操作';
                }
                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
         * @returns {Promise<void>}
         */
        async mergeCalcImage(ctx) {
            try {
                // 检查登录用户，是否可操作
                if (ctx.stage.readOnly) {
                    throw '该计量期当前您无权操作';
                }
                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 + '.jpg');
                    const base64Data = data.img.replace(/^data:image\/\w+;base64,/, "");
                    const dataBuffer = new Buffer(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;
                    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);
                const responseData = {err: 0, msg: '', data: imData};
                ctx.body = responseData;
            } catch (err) {
                this.log(err);
                ctx.body = {err: 1, msg: err.toString(), data: null};
            }
        }

        /**
         * 中间计量 -- 完成中间计量(Post)
         * @param ctx
         * @returns {Promise<void>}
         */
        async doneDetail(ctx) {
            try {
                await ctx.service.stage.updateCheckDetailFlag(ctx.stage.id, false);
                ctx.stage.check_detail = false;
                ctx.redirect(ctx.request.header.referer);
            } catch (err) {
                ctx.redirect(ctx.request.header.referer);
            }
        }

        /**
         * 中间计量 -- 解锁编辑(Post)
         * @param ctx
         * @returns {Promise<void>}
         */
        async unlockDetail(ctx) {
            try {
                await ctx.service.stage.updateCheckDetailFlag(ctx.stage.id, true);
                ctx.stage.check_detail = true;
                ctx.redirect(ctx.request.header.referer);
            } catch (err) {
                ctx.redirect(ctx.request.header.referer);
            }
        }

        /**
         * 合同支付 (Get)
         * @param ctx
         * @returns {Promise<void>}
         */
        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;
                // if (dealPay && dealPay.length > 0) {
                //     renderData.dealPay = dealPay;
                // } else {
                //     await ctx.service.pay.addDefaultPayData(this.ctx.tender.id);
                //     await ctx.service.stagePay.addInitialStageData(ctx.stage);
                //     renderData.dealPay = await ctx.service.stagePay.getStagePays(ctx.stage);
                // }
                renderData.calcBase = await ctx.service.stage.getStagePayCalcBase();
                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.pay);
                renderData.whiteList = this.ctx.app.config.multipart.whitelist;

                // 用户有无权限上传和删除附件
                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);

                // 计算 本期金额
                const payCalculator = new PayCalculator(this.ctx, this.ctx.tender.info.decimal);
                await payCalculator.calculateAll(renderData.dealPay);
                await this.layout('stage/pay.ejs', renderData, 'stage/pay_modal.ejs');
            } catch (err) {
                console.log(err);
                this.log(err);
                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
            }
        }

        /**
         * 合同支付 - 编辑合同支付项 (Ajax)
         * @param ctx
         * @returns {Promise<void>}
         */
        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 {
                // 检查登录用户，是否可操作
                if (ctx.stage.readOnly) {
                    throw '该计量期当前您无权操作';
                }

                const data = JSON.parse(ctx.request.body.data);
                const responseData = { err: 0, msg: '', data: {}, };
                const payCalculator = new PayCalculator(this.ctx, this.ctx.tender.info.decimal);
                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.calculate(responseData.data);
                        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);
                        } 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);
                        break;
                }

                ctx.body = responseData;
            } catch(err) {
                this.log(err);
                ctx.body = {err: 1, msg: err.toString(), data: null};
            }
        }

        /**
         * 合同支付 - 章节明细 (Ajax-Post)
         * @param ctx
         * @returns {Promise<void>}
         */
        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
         * @returns {Promise<{}>}
         * @private
         */
        async _getChangeDetailData(tid, sid, cid) {
            const data = {};
            data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid);
            data.addUsedBills = await this.ctx.service.stageChange.getUsedData(tid, cid);
            data.curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, cid);
            return data;
        }
        /**
         * 变更令 (Get)
         * @param ctx
         * @returns {Promise<void>}
         */
        async change(ctx) {
            try {
                await this._getStageAuditViewData(ctx);
                const renderData = await this._getDefaultRenderData(ctx);
                renderData.ledger = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
                renderData.changes = await ctx.service.change.getChangeAndUsedInfo(ctx.tender.id);
                if (renderData.changes.length > 0) {
                    const change = renderData.changes[0];
                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid);
                }
                renderData.usedChangesId = await ctx.service.stageChange.getStageUsedChangeId(ctx.stage.id);
                renderData.changeConst = changeConst;
                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.change);
                await this.layout('stage/change.ejs', renderData);
            } catch (err) {
                this.log(err);
                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
            }
        }

        // 审批相关
        /**
         * 添加审批人
         * @param ctx
         * @returns {Promise<void>}
         */
        async addAudit(ctx) {
            try {
                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 '当前不允许添加审核人';
                }
                // 检查审核人是否已存在
                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
         * @returns {Promise<void>}
         */
        async deleteAudit(ctx) {
            try {
                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
         * @returns {Promise<void>}
         */
        async startAudit(ctx) {
            try {
                // 检查权限等
                if (!ctx.stage) {
                    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 '该期数据当前无法上报';
                }
                if (ctx.stage.check_detail) {
                    throw '上报前，需要完成中间计量。';
                }

                await ctx.service.stageAudit.start(ctx.stage.id, ctx.stage.times);

                ctx.redirect(ctx.request.header.referer);
            } catch (err) {
                this.log(err);
                ctx.session.postError = err.toString();
                ctx.redirect(ctx.request.header.referer);
            }
        }
        /**
         * 审批
         * @param ctx
         * @returns {Promise<void>}
         */
        async checkAudit(ctx) {
            try {
                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 = {
                    checkType: parseInt(ctx.request.body.checkType),
                    opinion: ctx.request.body.opinion,
                };
                if (!data.checkType || isNaN(data.checkType)) {
                    throw '提交数据错误';
                }
                if (data.checkType === auditConst.status.checked && ctx.stage.check_detail) {
                    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);
                console.log('success');

                ctx.redirect(ctx.request.header.referer);
            } catch (err) {
                console.log(err);
                this.log(err);
                ctx.session.postError = err.toString();
                ctx.redirect(ctx.request.header.referer);
            }
        }

        // 清单汇总相关
        _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
         * @returns {Promise<void>}
         */
        async gather(ctx) {
            try {
                await this._getStageAuditViewData(ctx);
                const renderData = await this._getDefaultRenderData(ctx);
                [renderData.gclSpread, renderData.leafXmjSpread] = this._getGatherSpreadSetting();
                renderData.ledger = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
                renderData.curLedgerData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.times, 0);

                renderData.pos = await ctx.service.pos.getPosData({tid: ctx.tender.id});
                // todo 根据当前人，或指定对象查询数据
                renderData.curPosData = await ctx.service.stagePos.getAuditorStageData(ctx.tender.id,
                    ctx.stage.id, ctx.stage.times, ctx.stage.curAuditor ? ctx.stage.curAuditor.order : 0);
                //renderData.gcl = await this.ctx.service.ledger.getAllLeafGclBills(this.ctx.tender.id);
                renderData.dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: {tender_id: this.ctx.tender.id} });

                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
         * @returns {Promise<void>}
         */
        async compare(ctx) {
            function getCompareSpreadSetting () {
                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 = ctx.helper.getNumberFormatter(ctx.tender.info.decimal.tp);
                const upFormatter = ctx.helper.getNumberFormatter(2);
                const ledger = JSON.parse(JSON.stringify(spreadConst.stageCompare.ledger));
                setColFormat(ledger.baseCols, 'unit_price', upFormatter);
                setColFormat(ledger.extraCols, 'total_price', tpFormatter);
                setColFormat(ledger.extraCols, 'tp', tpFormatter);

                const pos = JSON.parse(JSON.stringify(spreadConst.stageCompare.pos));
                return [ledger, pos];
            }
            try {
                await this._getStageAuditViewData(ctx);
                const renderData = await this._getDefaultRenderData(ctx);
                [renderData.ledgerSpread, renderData.posSpread] = getCompareSpreadSetting();
                renderData.ledger = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
                renderData.orgStageLedger = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.times, 0);
                renderData.pos = await ctx.service.pos.getPosData({tid: ctx.tender.id});
                renderData.orgStagePos = await ctx.service.stagePos.getAuditorStageData(ctx.tender.id,
                    ctx.stage.id, ctx.stage.times, ctx.stage.curAuditor ? ctx.stage.curAuditor.order : 0);
                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 = [];
                for (const order of data.auditors) {
                    const data = { order: order, bills: [], pos: [] };
                    data.bills = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.times, order);
                    data.pos = await ctx.service.stagePos.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.times, order);
                    result.push(data);
                }
                ctx.body = {err: 0, msg: '', data: result};
            } catch (err) {
                this.log(err);
                ctx.body = {err: 1, msg: err.toString(), data: null};
            }
        }

        /**
         * 报表
         * @param ctx
         * @returns {Promise<void>}
         */
        async report(ctx) {
            try {
                const renderData = await this._getDefaultRenderData(ctx);
                await this.layout('stage/report.ejs', renderData, 'stage/report_modal.ejs');
            } catch (err) {
                this.log(err);
                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
            }
        }

        /**
         * 上传附件
         * @param {Object} ctx - egg全局变量
         * @return {void}
         */
        async uploadFile(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            let stream;
            try {
                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 create_time = Date.parse(new Date()) / 1000;
                    const fileInfo = path.parse(stream.filename);
                    const fileName = 'stage' + create_time + '_' + index + fileInfo.ext;

                    // 保存文件
                    await ctx.helper.saveStreamFile(stream, path.join(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: path.join(dirName, fileName),
                    };
                    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);
                    this.setMessage(err.toString(), this.messageType.ERROR);
                }
            }
        }

        /**
         * 删除附件
         * @param {Object} ctx - egg全局变量
         * @return {void}
         */
        async deleteFile(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: '',
            };
            try {
                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 {
                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;
        }

        /**
         * 合同支付上传附件
         * @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 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 {
                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;
        }
    }

    return StageController;
};
