| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411 | 'use strict';/** * * * @author Mai * @date 2018/8/14 * @version */const auditConst = require('../const/audit').changeApply;const auditType = require('../const/audit').auditType;const pushType = require('../const/audit').pushType;const pushOperate = require('../const/spec_3f').pushOperate;const shenpiConst = require('../const/shenpi');const smsTypeConst = require('../const/sms_type');const SMS = require('../lib/sms');const SmsAliConst = require('../const/sms_alitemplate');const wxConst = require('../const/wechat_template');module.exports = app => {    class ChangeApplyAudit extends app.BaseService {        /**         * 构造函数         *         * @param {Object} ctx - egg全局变量         * @return {void}         */        constructor(ctx) {            super(ctx);            this.tableName = 'change_apply_audit';        }        /**         * 获取 审核列表信息         *         * @param {Number} caId - 变更立项id         * @param {Number} times - 第几次审批         * @return {Promise<*>}         */        async getAuditors(caId, times = 1, order_sort = 'asc', noYB = false) {            // const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' +            //     'FROM ?? AS la, ?? AS pa, (SELECT t1.`aid`,(@i:=@i+1) as `sort` FROM (SELECT t.`aid`, t.`order` FROM (select `aid`, `order` from ?? WHERE `caid` = ? AND `times` = ? ORDER BY `order` LIMIT 200) t GROUP BY t.`aid` ORDER BY t.`order`) t1, (select @i:=0) as it) as g ' +            //     'WHERE la.`caid` = ? and la.`times` = ? and la.`aid` = pa.`id` and g.`aid` = la.`aid` order by la.`order`';            // const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.tableName, caId, times, caId, times];            // const result = await this.db.query(sql, sqlParam);            // const sql2 = 'SELECT COUNT(a.`aid`) as num FROM (SELECT `aid` FROM ?? WHERE `caid` = ? AND `times` = ? GROUP BY `aid`) as a';            // const sqlParam2 = [this.tableName, caId, times];            // const count = await this.db.queryOne(sql2, sqlParam2);            // for (const i in result) {            //     result[i].max_sort = count.num;            // }            const ybSql = noYB ? ' AND la.audit_order != 0' : '';            const sql = 'SELECT la.id, la.aid, la.times, la.order, la.status, la.opinion, la.begin_time, la.end_time, la.audit_type, la.audit_order,' +                '    pa.name, pa.company, pa.role, pa.mobile, pa.telephone' +                `  FROM ${this.tableName} la LEFT JOIN ${this.ctx.service.projectAccount.tableName} pa ON la.aid = pa.id` +                '  WHERE la.caid = ? AND la.times = ?' + ybSql +                '  ORDER BY la.order ' + order_sort;            const sqlParam = [caId, times];            const result = await this.db.query(sql, sqlParam);            const max_sort = this._.max(result.map(x => { return x.audit_order; }));            for (const i in result) {                result[i].max_sort = max_sort;            }            return result;        }        /**         * 获取 当前审核人         *         * @param {Number} caId - 变更立项id         * @param {Number} times - 第几次审批         * @return {Promise<*>}         */        async getCurAuditor(caId, times = 1) {            const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +                '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +                '  WHERE la.`caid` = ? and la.`status` = ? and la.`times` = ?';            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, caId, auditConst.status.checking, times];            return await this.db.queryOne(sql, sqlParam);        }        async getCurAuditors(caId, times = 1) {            const sql =                'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, la.audit_type, la.audit_order ' +                '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +                '  WHERE la.`caid` = ? and la.`status` = ? and la.`times` = ?';            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, caId, auditConst.status.checking, times];            return await this.db.query(sql, sqlParam);        }        /**         * 获取审核人流程列表         *         * @param auditorId         * @return {Promise<*>}         */        async getAuditGroupByList(changeId, times, transaction = false) {            const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`caid`, la.`aid`, la.`order`, la.`status`, la.audit_type, la.audit_order ' +                '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +                '  WHERE la.`caid` = ? and la.`times` = ? and la.`status` != ? and la.`status` != ? GROUP BY la.`aid` ORDER BY la.`order`';            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times, auditConst.status.revise, auditConst.status.cancelRevise];            return transaction !== false ? await transaction.query(sql, sqlParam) : await this.db.query(sql, sqlParam);        }        /**         * 移除审核人         *         * @param {Number} materialId - 材料调差期id         * @param {Number} status - 期状态         * @param {Number} status - 期次数         * @return {Promise<boolean>}         */        async getAuditorByStatus(caId, status, times = 1) {            let auditor = null;            let sql = '';            let sqlParam = '';            switch (status) {                case auditConst.status.checking :                case auditConst.status.checked :                case auditConst.status.revise :                case auditConst.status.cancelRevise :                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`caid`, la.`aid`, la.`order` ' +                        '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +                        '  WHERE la.`caid` = ? and la.`status` = ? ' +                        '  ORDER BY la.`times` desc, la.`order` desc';                    sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, caId, status];                    auditor = await this.db.queryOne(sql, sqlParam);                    break;                case auditConst.status.checkNo :                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`caid`, la.`aid`, la.`order` ' +                        '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +                        '  WHERE la.`caid` = ? and la.`status` = ? and la.`times` = ?' +                        '  ORDER BY la.`times` desc, la.`order` desc';                    sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, caId, auditConst.status.checkNo, parseInt(times) - 1];                    auditor = await this.db.queryOne(sql, sqlParam);                    break;                case auditConst.status.uncheck :                    break;                default:break;            }            return auditor;        }        async getAuditorsByStatus(caId, status, times = 1) {            let auditor = [];            let sql = '';            let sqlParam = '';            let cur;            switch (status) {                case auditConst.status.checking :                case auditConst.status.checked :                case auditConst.status.revise :                case auditConst.status.cancelRevise :                    cur = await this.db.queryOne('SELECT * From ?? where caid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, caId, times, status]);                    if (!cur) return [];                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`caid`, la.`order`, la.status, la.audit_order, la.audit_type ' +                        '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +                        '  WHERE la.`caid` = ? and la.`order` = ? and times = ?';                    sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, caId, cur.order, times];                    auditor = await this.db.query(sql, sqlParam);                    break;                case auditConst.status.checkNo :                    cur = await this.db.queryOne('SELECT * From ?? where caid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, caId, parseInt(times) - 1, status]);                    if (!cur) return [];                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`caid`, la.`order`, la.status, la.audit_order, la.audit_type ' +                        '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +                        '  WHERE la.`caid` = ? and la.`order` = ? and la.`times` = ?';                    sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, caId, cur.order, parseInt(times) - 1];                    auditor = await this.db.query(sql, sqlParam);                    break;                case auditConst.status.uncheck:                default:                    break;            }            return auditor;        }        async getLastAudit(caid, times, transaction = null) {            const sql = 'SELECT * FROM ?? WHERE `caid` = ? AND `times` = ? ORDER BY `order` DESC';            const sqlParam = [this.tableName, caid, times];            return transaction ? await transaction.queryOne(sql, sqlParam) : await this.db.queryOne(sql, sqlParam);        }        /**         * 获取审核人流程列表(包括原报)         * @param {Number} materialId 调差id         * @param {Number} times 审核次数         * @return {Promise<Array>} 查询结果集(包括原报)         */        async getAuditorsWithOwner(caId, times = 1) {            const result = await this.getAuditGroupByList(caId, times);            const sql =                'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As caid, 0 As `order`' +                '  FROM ' +                this.ctx.service.changeApply.tableName +                ' As s' +                '  LEFT JOIN ' +                this.ctx.service.projectAccount.tableName +                ' As pa' +                '  ON s.uid = pa.id' +                '  WHERE s.id = ?';            const sqlParam = [times, caId, caId];            const user = await this.db.queryOne(sql, sqlParam);            result.unshift(user);            return result;        }        /**         * 新增审核人         *         * @param {Number} caId - 立项书id         * @param {Number} auditorId - 审核人id         * @param {Number} times - 第几次审批         * @return {Promise<number>}         */        async addAuditor(caId, auditorId, times = 1, is_gdzs = 0) {            const transaction = await this.db.beginTransaction();            let flag = false;            try {                let [newOrder, newAuditOrder] = await this.getNewOrder(caId, times);                // 判断是否存在固定终审,存在则newOrder - 1并使终审order+1                newOrder = is_gdzs === 1 ? newOrder - 1 : newOrder;                newAuditOrder = is_gdzs === 1 ? newAuditOrder - 1 : newAuditOrder;                if (is_gdzs) await this._syncOrderByDelete(transaction, caId, newOrder, times, '+');                const data = {                    tid: this.ctx.tender.id,                    caid: caId,                    aid: auditorId,                    times,                    order: newOrder,                    status: auditConst.status.uncheck,                    audit_order: newAuditOrder,                };                const result = await transaction.insert(this.tableName, data);                await transaction.commit();                flag = result.effectRows = 1;            } catch (err) {                await transaction.rollback();                throw err;            }            return flag;        }        /**         * 获取 最新审核顺序         *         * @param {Number} caId - 立项书id         * @param {Number} times - 第几次审批         * @return {Promise<number>}         */        async getNewOrder(caId, times = 1) {            const sql = 'SELECT Max(`order`) As max_order, Max(audit_order) As max_audit_order FROM ?? Where `caid` = ? and `times` = ?';            const sqlParam = [this.tableName, caId, times];            const result = await this.db.queryOne(sql, sqlParam);            return result && result.max_order ? [result.max_order + 1, result.max_audit_order + 1] : [1, 1];        }        /**         * 移除审核人         *         * @param {Number} caId - 变更立项书id         * @param {Number} auditorId - 审核人id         * @param {Number} times - 第几次审批         * @return {Promise<boolean>}         */        async deleteAuditor(caId, auditorId, times = 1) {            const transaction = await this.db.beginTransaction();            try {                const condition = { caid: caId, aid: auditorId, times };                const auditor = await this.getDataByCondition(condition);                if (!auditor) {                    throw '该审核人不存在';                }                // 移除整个流程的人                await transaction.delete(this.tableName, { caid: caId, order: auditor.order, times });                await this._syncOrderByDelete(transaction, caId, auditor.order, times);                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }            return true;        }        /**         * 移除审核人时,同步其后审核人order         * @param transaction - 事务         * @param {Number} caId - 变更立项书id         * @param {Number} auditorId - 审核人id         * @param {Number} times - 第几次审批         * @return {Promise<*>}         * @private         */        async _syncOrderByDelete(transaction, caId, order, times, selfOperate = '-') {            this.initSqlBuilder();            this.sqlBuilder.setAndWhere('caid', {                value: caId,                operate: '=',            });            this.sqlBuilder.setAndWhere('order', {                value: order,                operate: '>=',            });            this.sqlBuilder.setAndWhere('times', {                value: times,                operate: '=',            });            this.sqlBuilder.setUpdateData('order', {                value: 1,                selfOperate,            });            this.sqlBuilder.setUpdateData('audit_order', {                value: 1,                selfOperate,            });            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');            const data = await transaction.query(sql, sqlParam);            return data;        }        /**         * 开始审批         * @param {Number} caId - 立项书id         * @param {Number} times - 第几次审批         * @return {Promise<boolean>}         */        async start(caId, times = 1) {            const audits = await this.getAllDataByCondition({ where: { caid: caId, times, order: 1 } });            if (audits.length === 0) {                if (this.ctx.tender.info.shenpi.change === shenpiConst.sp_status.gdspl) {                    throw '请联系管理员添加审批人';                } else {                    throw '请先选择审批人,再上报数据';                }            }            const transaction = await this.db.beginTransaction();            try {                const begin_time = new Date();                const updateData = audits.map(x => { return { id: x.id, status: auditConst.status.checking, begin_time }; });                await transaction.updateRows(this.tableName, updateData);                await transaction.update(this.ctx.service.changeApply.tableName, {                    id: caId, status: auditConst.status.checking,                });                // 微信模板通知                const shenpiUrl = await this.ctx.helper.urlToShort(                    this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/apply/' + caId + '/information#shenpi'                );                // 微信模板通知                const wechatData = {                    type: 'apply',                    wap_url: shenpiUrl,                    status: wxConst.status.check,                    tips: wxConst.tips.check,                    code: this.ctx.session.sessionProject.code,                    c_name: this.ctx.change.name,                };                await this.ctx.helper.sendWechat(this._.map(audits, 'aid'), smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);                for (const audit of audits) {                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.BG, {                        pid: this.ctx.session.sessionProject.id,                        tid: this.ctx.tender.id,                        uid: audit.aid,                        sp_type: 'change',                        sp_id: audit.id,                        table_name: this.tableName,                        template: wxConst.template.change,                        wx_data: wechatData,                    });                }                await transaction.delete(this.ctx.service.changeApplyHistory.tableName, { caid: caId });                // todo 更新标段tender状态 ?                //  检查三方特殊推送                await this.ctx.service.specMsg.addChangeApplyMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.change, pushOperate.change_apply.flow);                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }            return true;        }        /**         * 获取审核人需要审核的期列表         *         * @param auditorId         * @return {Promise<*>}         */        async getAuditChangeApply(auditorId) {            const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`caid`,' +                '    m.`status` As `mstatus`, m.`code` As `mcode`,' +                '    t.`name`, t.`project_id`, t.`type`, t.`user_id` ' +                '  FROM ?? AS ma LEFT JOIN ?? AS m ON  ma.`caid` = m.`id` LEFT JOIN ?? As t ON ma.`tid` = t.`id`' +                '  WHERE ((ma.`aid` = ? and ma.`status` = ?) OR (m.`uid` = ? and ma.`status` = ? and m.`status` = ? and ma.`times` = (m.`times`-1)))' +                '  ORDER BY ma.`begin_time` DESC';            const sqlParam = [this.tableName, this.ctx.service.changeApply.tableName, this.ctx.service.tender.tableName, auditorId, auditConst.status.checking, auditorId, auditConst.status.checkNo, auditConst.status.checkNo];            const result = await this.db.query(sql, sqlParam);            // 过滤result中存在重复sid的值, 保留最新的一条            const filterResult = [];            const caidArr = [];            for (const r of result) {                if (caidArr.indexOf(r.caid) === -1) {                    filterResult.push(r);                    caidArr.push(r.caid);                }            }            return filterResult;        }        /**         * 获取审核人审核的次数         *         * @param auditorId         * @return {Promise<*>}         */        async getCountByChecked(auditorId) {            return await this.db.count(this.tableName, { aid: auditorId, status: [auditConst.status.checked, auditConst.status.checkNo] });        }        /**         * 获取最近一次审批结束时间         *         * @param auditorId         * @return {Promise<*>}         */        async getLastEndTimeByChecked(auditorId) {            const sql = 'SELECT `end_time` FROM ?? WHERE `aid` = ? ' +                'AND `status` in (' + this.ctx.helper.getInArrStrSqlFilter([auditConst.status.checked, auditConst.status.checkNo]) + ') ORDER BY `end_time` DESC';            const sqlParam = [this.tableName, auditorId];            const result = await this.db.queryOne(sql, sqlParam);            return result ? result.end_time : null;        }        /**         * 用于添加推送所需的content内容         * @param {Number} pid 项目id         * @param {Number} tid 台账id         * @param {Number} caId 立项书id         * @param {Number} uid 审批人id         */        async getNoticeContent(pid, tid, caId, uid, opinion = '') {            const noticeSql = 'SELECT * FROM (SELECT ' +                '  t.`id` As `tid`, ma.`caid`, m.`code` as `c_code`, t.`name`, pa.`name` As `su_name`, pa.role As `su_role`' +                '  FROM (SELECT * FROM ?? WHERE `id` = ? ) As t' +                '  LEFT JOIN ?? As m On t.`id` = m.`tid` AND m.`id` = ?' +                '  LEFT JOIN ?? As ma ON m.`id` = ma.`caid`' +                '  LEFT JOIN ?? As pa ON pa.`id` = ?' +                '  WHERE  t.`project_id` = ? ) as new_t GROUP BY new_t.`tid`';            const noticeSqlParam = [this.ctx.service.tender.tableName, tid, this.ctx.service.changeApply.tableName, caId, this.tableName, this.ctx.service.projectAccount.tableName, uid, pid];            const content = await this.db.query(noticeSql, noticeSqlParam);            if (content.length) {                content[0].opinion = opinion;            }            return content.length ? JSON.stringify(content[0]) : '';        }        /**         * 审批         * @param {Number} caId - 立项书id         * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果         * @param {Number} times - 第几次审批         * @return {Promise<void>}         */        async check(caId, checkData, times = 1) {            if (checkData.checkType !== auditConst.status.checked && checkData.checkType !== auditConst.status.checkNo) {                throw '提交数据错误';            }            const pid = this.ctx.session.sessionProject.id;            switch (checkData.checkType) {                case auditConst.status.checked:                    await this._checked(pid, caId, checkData, times);                    break;                case auditConst.status.checkNo:                    await this._checkNo(pid, caId, checkData, times);                    break;                default:                    throw '无效审批操作';            }        }        async _checked(pid, caId, checkData, times) {            const accountId = this.ctx.session.sessionUser.accountId;            const time = new Date();            // 整理当前流程审核人状态更新            const audits = await this.getAllDataByCondition({ where: { caid: caId, times, status: auditConst.status.checking } });            if (audits.length === 0) throw '审核数据错误';            const selfAudit = audits.find(x => { return x.aid === accountId; });            if (!selfAudit) throw '当前标段您无权审批';            // const flowAudits = await this.getAllDataByCondition({ where: { caid: caId, times, order: selfAudit.order } });            const nextAudits = await this.getAllDataByCondition({ where: { caid: caId, times, order: selfAudit.order + 1 } });            const transaction = await this.db.beginTransaction();            try {                await transaction.update(this.tableName, {                    id: selfAudit.id,                    status: checkData.checkType,                    opinion: checkData.opinion,                    end_time: time,                });                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, selfAudit.id);                // 获取推送必要信息                const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, caId, selfAudit.aid, checkData.opinion);                // 添加推送                const records = [];                const auditors = await this.getAuditorsWithOwner(caId, times);                auditors.forEach(audit => {                    records.push({ pid, type: pushType.changeApply, uid: audit.aid, status: auditConst.status.checked, content: noticeContent });                });                await transaction.insert('zh_notice', records);                if (audits.length === 1 || selfAudit.audit_type !== auditType.key.and) {                    // 或签更新他人审批状态                    if (selfAudit.audit_type === auditType.key.or) {                        const updateOther = [];                        for (const audit of audits) {                            if (audit.aid === selfAudit.aid) continue;                            updateOther.push({                                id: audit.id,                                status: auditConst.status.checkSkip,                                opinion: '',                                end_time: time,                            });                            await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);                        }                        if (updateOther.length > 0) transaction.updateRows(this.tableName, updateOther);                    }                    // 无下一审核人表示,审核结束                    if (nextAudits.length > 0) {                        // 流程至下一审批人                        const updateData = nextAudits.map(x => { return { id: x.id, status: auditConst.status.checking, begin_time: time }; });                        await transaction.updateRows(this.tableName, updateData);                        // 同步 期信息                        await transaction.update(this.ctx.service.changeApply.tableName, {                            id: caId, status: auditConst.status.checking,                        });                        // 微信模板通知                        const shenpiUrl = await this.ctx.helper.urlToShort(                            this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/apply/' + caId + '/information#shenpi'                        );                        // 微信模板通知                        const wechatData = {                            type: 'apply',                            wap_url: shenpiUrl,                            status: wxConst.status.check,                            tips: wxConst.tips.check,                            code: this.ctx.session.sessionProject.code,                            c_name: this.ctx.change.name,                        };                        await this.ctx.helper.sendWechat(this._.map(nextAudits, 'aid'), smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);                        for (const nextAudit of nextAudits) {                            await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.BG, {                                pid: this.ctx.session.sessionProject.id,                                tid: this.ctx.tender.id,                                uid: nextAudit.aid,                                sp_type: 'change',                                sp_id: nextAudit.id,                                table_name: this.tableName,                                template: wxConst.template.change,                                wx_data: wechatData,                            });                        }                        //  检查三方特殊推送                        await this.ctx.service.specMsg.addChangeApplyMsg(transaction, pid, this.ctx.change, pushOperate.change_apply.flow);                    } else {                        // 本期结束                        // 生成截止本期数据 final数据                        // 同步 期信息                        await transaction.update(this.ctx.service.changeApply.tableName, {                            id: caId, status: checkData.checkType,                            notice_code: checkData.notice_code,                            notice_uid: checkData.notice_uid,                            decimal: JSON.stringify(this.ctx.change.decimal),                        });                        // 微信模板通知                        const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), this.ctx.change.uid));                        // 微信模板通知                        const shenpiUrl = await this.ctx.helper.urlToShort(                            this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/apply/' + caId + '/information#shenpi'                        );                        const wechatData = {                            type: 'apply',                            wap_url: shenpiUrl,                            status: wxConst.status.success,                            tips: wxConst.tips.success,                            code: this.ctx.session.sessionProject.code,                            c_name: this.ctx.change.name,                        };                        await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);                        //  检查三方特殊推送                        await this.ctx.service.specMsg.addChangeApplyMsg(transaction, pid, this.ctx.change, pushOperate.change_apply.flow);                        await this.ctx.service.specMsg.addChangeApplyMsg(transaction, pid, this.ctx.change, pushOperate.change_apply.checked);                    }                } else {                    // 同步 期信息                    await transaction.update(this.ctx.service.changeApply.tableName, {                        id: caId,                        status: auditConst.status.checking,                    });                }                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        async _checkNo(pid, caId, checkData, times) {            const accountId = this.ctx.session.sessionUser.accountId;            const time = new Date();            const changeData = await this.ctx.service.changeApply.getDataById(caId);            // 整理当前流程审核人状态更新            const audits = await this.getAllDataByCondition({ where: { caid: caId, times, status: auditConst.status.checking } });            if (!audits) throw '审核数据错误';            const selfAudit = audits.find(x => { return x.aid === accountId; });            if (!selfAudit) throw '当前标段您无权审批';            const auditors = await this.getUniqAuditor(caId, times); // 全部参与的审批人            const newAuditors = auditors.map(x => {                return {                    aid: x.aid, tid: selfAudit.tid, caid: selfAudit.caid,                    times: times + 1, order: x.audit_order, status: auditConst.status.uncheck,                    audit_type: x.audit_type, audit_order: x.audit_order,                };            });            const transaction = await this.db.beginTransaction();            try {                const updateData = [];                audits.forEach(x => {                    updateData.push({                        id: x.id,                        status: x.aid === selfAudit.aid ? checkData.checkType : auditConst.status.checkSkip,                        opinion: x.aid === selfAudit.aid ? checkData.opinion : '',                        end_time: x.aid === selfAudit.aid ? time : null,                    });                });                await transaction.updateRows(this.tableName, updateData);                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));                // 添加到消息推送表                const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, caId, selfAudit.aid, checkData.opinion);                const records = [{ pid, type: pushType.changeApply, uid: this.ctx.change.uid, status: auditConst.status.checkNo, content: noticeContent }];                auditors.forEach(audit => {                    records.push({ pid, type: pushType.changeApply, uid: audit.aid, status: auditConst.status.checkNo, content: noticeContent });                });                await transaction.insert(this.ctx.service.noticePush.tableName, records);                // 同步期信息                await transaction.update(this.ctx.service.changeApply.tableName, {                    id: caId, status: checkData.checkType,                    times: times + 1,                });                // 拷贝新一次审核流程列表                await transaction.insert(this.tableName, newAuditors);                // 微信模板通知                const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), this.ctx.change.uid));                // 微信模板通知                const shenpiUrl = await this.ctx.helper.urlToShort(                    this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/apply/' + caId + '/information#shenpi'                );                const wechatData = {                    type: 'apply',                    wap_url: shenpiUrl,                    status: wxConst.status.back,                    tips: wxConst.tips.back,                    code: this.ctx.session.sessionProject.code,                    c_name: this.ctx.change.name,                };                await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);                // 生成内容保存表至zh_change_project_history中,用于撤回                // 回退spamount值数据                const changeList = await this.ctx.service.changeApplyList.getAllDataByCondition({ where: { caid: caId } });                await this.ctx.service.changeApplyHistory.saveHistory(transaction, changeData, changeList);                //  检查三方特殊推送                await this.ctx.service.specMsg.addChangeApplyMsg(transaction, pid, this.ctx.change, pushOperate.change_apply.flow);                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        /**         * 复制上一期的审批人列表给最新一期         *         * @param transaction - 新增一期的事务         * @param {Object} preMaterial - 上一期         * @param {Object} newaMaterial - 最新一期         * @return {Promise<*>}         */        async copyPreChangeApplyAuditors(transaction, preChange, newChange) {            const auditList = await this.getUniqAuditor(preChange.id, preChange.times); // 全部参与的审批人            const newAuditors = [];            for (const x of auditList) {                if (x.audit_order !== 0) {                    newAuditors.push({                        tid: preChange.tid, caid: newChange.id, aid: x.aid,                        times: 1, order: x.audit_order, status: auditConst.status.uncheck,                        audit_type: x.audit_type, audit_order: x.audit_order,                    });                }            }            const result = newAuditors.length > 0 ? await transaction.insert(this.tableName, newAuditors) : null;            return result ? result.affectedRows === newAuditors.length : true;        }        async getAllAuditors(tenderId) {            const sql = 'SELECT ma.aid, ma.tid FROM ' + this.tableName + ' ma' +                '  LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On ma.tid = t.id' +                '  WHERE t.id = ?' +                '  GROUP BY  ma.aid';            const sqlParam = [tenderId];            return this.db.query(sql, sqlParam);        }        /**         * 审批撤回         * @param {Number} stageId - 标段id         * @param {Number} times - 第几次审批         * @return {Promise<void>}         */        async checkCancel(change) {            // 分3种情况,根据ctx.cancancel值判断:            // 1.原报发起撤回,当前流程删除,并回到待上报            // 2.审批人撤回审批通过,增加流程,并回到它审批中            // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中            // 5.会签审批人撤回审批通过(还有其他审批人未审批通过),仅修改本人流程状态            switch (change.cancancel) {                case 1: await this._userCheckCancel(change); break;                case 2: await this._auditCheckCancel(change); break;                case 4: await this._auditCheckCancelNo(change); break;                case 5: await this._auditCheckCancelAnd(change); break;                default: throw '不可撤回,请刷新页面重试';            }        }        /**         * 原报撤回,直接改动审批人状态         * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人         *         * 一审 1 A checking  ->  A uncheck status改   pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1         * ...         *         * @param stage         * @returns {Promise<void>}         * @private         */        async _userCheckCancel(change) {            const transaction = await this.db.beginTransaction();            try {                const updateData = change.curAuditors.map(x => {                    return {                        id: x.id,                        status: auditConst.status.uncheck,                        begin_time: null, opinion: null,                    };                });                await transaction.updateRows(this.tableName, updateData);                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));                // 变成待上报状态                await transaction.update(this.ctx.service.changeApply.tableName, {                    id: change.id,                    status: change.times === 1 ? auditConst.status.uncheck : auditConst.status.checkNo,                });                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        /**         * 审批人撤回审批通过,插入两条数据         *         * 一审 1 A checked             一审 1 A checked         * 二审 2 B checked   pre ->    二审 2 B checked         * 三审 3 C checking  cur       二审 3 B checkCancel  增                增extra_his     增tp_his         * 四审 4 D uncheck             二审 4 B checking     增                增pay_cur         *                             三审 5 C uncheck      order、status改         *                             四审 6 D uncheck      order改         *         * @param stage         * @returns {Promise<void>}         * @private         */        async _auditCheckCancel(change) {            if (change.curAuditors.length === 0 || change.curAuditors[0].order <= 1) {                throw '撤回用户数据错误';            }            const accountId = this.ctx.session.sessionUser.accountId;            const selfAuditor = change.preAuditors.find(x => { return x.aid === accountId; });            if (!selfAuditor) throw '撤回用户数据错误';            const time = new Date();            const transaction = await this.db.beginTransaction();            try {                // 顺移其后审核人流程顺序                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE caid = ? AND times = ? AND `order` > ?';                await transaction.query(sql, [change.id, selfAuditor.times, change.curAuditors[0].order]);                // 当前审批人2次添加至流程中                const checkCancelAuditors = [],                    checkingAuditors = [];                change.preAuditors.forEach(x => {                    checkCancelAuditors.push({                        tid: change.tid, caid: change.id, aid: x.aid,                        times: x.times, order: x.order + 1,                        status: x.aid === selfAuditor.aid ? auditConst.status.checkCancel : auditConst.status.checkSkip,                        begin_time: time, end_time: time, opinion: '',                        audit_type: x.audit_type, audit_order: x.audit_order,                    });                });                change.preAuditors.forEach(x => {                    checkingAuditors.push({                        tid: change.tid, caid: change.id, aid: x.aid,                        times: x.times, order: x.order + 2,                        status: auditConst.status.checking,                        begin_time: time, end_time: time, opinion: '',                        audit_type: x.audit_type, audit_order: x.audit_order,                    });                });                await transaction.insert(this.tableName, [...checkCancelAuditors, ...checkingAuditors]);                // 当前审批人变成待审批                await transaction.updateRows(this.tableName, change.curAuditors.map(x => {                    return { id: x.id, begin_time: null, status: auditConst.status.uncheck, order: x.order + 2 };                }));                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, this._.map(change.curAuditors, 'id'));                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        /**         * 审批人撤回审批退回原报         *         * 1# 一审 1 A checked              1# 一审 1 A checked         *    二审 2 B checkNo   pre   ->      二审 2 B checkNo         *    三审 3 C uncheck                 二审 3 B checkCancel    增       pay: 2#0 -> 1#3   jl: 2#0 -> 1#3   增tp_his   增extra_his         *                                    二审 4 B checking       增       pay: 2#0 -> 1#4         *                                    三审 5 C uncheck        order改         *         * 2# 一审 1 A uncheck              2#                        删       pay: 2#0删   jl: 2#0删         *    二审 2 B uncheck         *    三审 3 C uncheck         *         * @param stage         * @returns {Promise<void>}         * @private         */        async _auditCheckCancelNo(change) {            const accountId = this.ctx.session.sessionUser.accountId;            const selfAuditor = change.preAuditors.find(x => { return x.aid === accountId && x.status === auditConst.status.checkNo; });            if (!selfAuditor) throw '该标段由他人审批退回,您不可撤回';            const time = new Date();            const transaction = await this.db.beginTransaction();            try {                // 整理上一个流程审核人状态更新                // 顺移其后审核人流程顺序                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE caid = ? AND times = ? AND `order` > ?';                await transaction.query(sql, [change.id, selfAuditor.times, selfAuditor.order]);                // 当前审批人2次添加至流程中                const checkCancelAuditors = [],                    checkingAuditors = [];                change.preAuditors.forEach(x => {                    checkCancelAuditors.push({                        tid: change.tid, caid: change.id, aid: x.aid,                        times: x.times, order: x.order + 1,                        status: x.aid === selfAuditor.aid ? auditConst.status.checkCancel : auditConst.status.checkSkip,                        begin_time: time, end_time: time, opinion: '',                        audit_type: x.audit_type, audit_order: x.audit_order,                    });                });                change.preAuditors.forEach(x => {                    checkingAuditors.push({                        tid: change.tid, caid: change.id, aid: x.aid,                        times: x.times, order: x.order + 2,                        status: auditConst.status.checking,                        begin_time: time, end_time: time, opinion: '',                        audit_type: x.audit_type, audit_order: x.audit_order,                    });                });                await transaction.insert(this.tableName, [...checkCancelAuditors, ...checkingAuditors]);                // 删除当前次审批流                await transaction.delete(this.tableName, { caid: change.id, times: change.times });                // 回退数据                await this.ctx.service.changeApplyHistory.returnHistory(transaction, change.id);                await transaction.delete(this.ctx.service.changeApplyHistory.tableName, { caid: change.id });                // // 设置变更立项为审批中                // await transaction.update(this.ctx.service.changeApply.tableName, {                //     id: change.id,                //     time: change.times - 1,                //     status: auditConst.status.checking,                // });                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        /**         * 会签未全部审批通过时,撤回仅修改本人状态         *         * @param stage         * @returns {Promise<void>}         * @private         */        async _auditCheckCancelAnd(change) {            const accountId = this.ctx.session.sessionUser.accountId;            const selfAuditor = change.flowAuditors.find(x => { return x.aid === accountId; });            if (!selfAuditor || selfAuditor.status !== auditConst.status.checked) throw '不可撤回';            const transaction = await this.db.beginTransaction();            try {                await transaction.update(this.tableName, {                    id: selfAuditor.id, status: auditConst.status.checking, opinion: '', end_time: null,                });                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        /**         * 重新审批变更令         * @param { string } cid - 查询的清单         * @return {Promise<*>} - 可用的变更令列表         */        async checkRevise(change) {            const accountId = this.ctx.session.sessionUser.accountId;            const time = new Date();            // 初始化事务            const transaction = await this.db.beginTransaction();            let result = false;            try {                const pid = this.ctx.session.sessionProject.id;                // 获取审核人列表                const auditors = await this.getUniqAuditor(change.id, change.times); // 全部参与的审批人                // 添加到消息推送表                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '发起修订');                const records = [{ pid, type: pushType.changeProject, uid: change.uid, status: auditConst.status.revise, content: noticeContent }];                auditors.forEach(auditor => {                    records.push({                        pid,                        type: pushType.changeApply,                        uid: auditor.aid,                        status: auditConst.status.revise,                        content: noticeContent,                    });                });                await transaction.insert('zh_notice', records);                const order = change.auditors.length > 0 ? change.auditors.reduce((rst, a) => { return Math.max(rst, a.order); }, 0) + 1 : 1; // 最大值 + 1                // 当前审批人添加至流程中                const checkReviseAuditor = {                    tid: change.tid, caid: change.id, aid: change.uid,                    times: change.times, order,                    status: auditConst.status.revise,                    begin_time: time, end_time: time, opinion: '',                    audit_type: auditType.key.common, audit_order: 0,                };                await transaction.insert(this.tableName, checkReviseAuditor);                // 修订人加入审批流程                const newAuditors = auditors.map(x => {                    return {                        aid: x.aid, tid: change.tid, caid: change.id,                        times: change.times + 1, order: x.audit_order, status: auditConst.status.uncheck,                        audit_type: x.audit_type, audit_order: x.audit_order,                    };                });                await transaction.insert(this.tableName, newAuditors);                // 生成内容保存表至zh_change_history中,用于撤销修订回退                const changeData = await transaction.get(this.ctx.service.changeApply.tableName, { id: change.id });                const changeList = await this.ctx.service.changeApplyList.getAllDataByCondition({ where: { caid: change.id } });                await this.ctx.service.changeApplyHistory.saveHistory(transaction, changeData, changeList);                // 设置变更立项修订状态                await transaction.update(this.ctx.service.changeApply.tableName, {                    id: change.id,                    notice_code: null,                    notice_uid: null,                    decimal: null,                    status: auditConst.status.revise,                    times: change.times + 1,                    final_auditor_str: '',                });                await transaction.commit();                result = true;            } catch (error) {                console.log(error);                await transaction.rollback();                result = false;            }            return result;        }        /**         * 撤销修订变更令         * @param { string } cid - 查询的清单         * @return {Promise<*>} - 可用的变更令列表         */        async cancelRevise(change) {            const time = new Date();            // 初始化事务            const transaction = await this.db.beginTransaction();            let result = false;            try {                const pid = this.ctx.session.sessionProject.id;                // 获取审核人列表                const auditors = await this.getUniqAuditor(change.id, change.times - 1); // 全部参与的审批人                // 添加到消息推送表                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '撤销修订');                const records = [{ pid, type: pushType.changeProject, uid: change.uid, status: auditConst.status.cancelRevise, content: noticeContent }];                auditors.forEach(auditor => {                    records.push({                        pid,                        type: pushType.changeApply,                        uid: auditor.aid,                        status: auditConst.status.cancelRevise,                        content: noticeContent,                    });                });                await transaction.insert('zh_notice', records);                const lastAudit = await this.getLastAudit(change.id, change.times - 1);                // 新增一个撤销修订状态到审批流程中                const revise_audit = {                    tid: change.tid, caid: change.id, aid: this.ctx.session.sessionUser.accountId,                    times: change.times - 1, order: lastAudit.order + 1, status: auditConst.status.cancelRevise,                    begin_time: time, end_time: time, opinion: '',                    audit_type: lastAudit.audit_type, audit_order: lastAudit.audit_order,                };                await transaction.insert(this.tableName, revise_audit);                await transaction.delete(this.tableName, { caid: change.id, times: change.times });                await this.ctx.service.changeApplyHistory.returnHistory(transaction, change.id);                await transaction.delete(this.ctx.service.changeApplyHistory.tableName, { caid: change.id });                await transaction.commit();                result = true;            } catch (error) {                console.log(error);                await transaction.rollback();                result = false;            }            return result;        }        /**         * 重新审批变更申请         * @param { string } cid - 查询的清单         * @return {Promise<*>} - 可用的变更令列表         */        async checkAgain(change) {            const accountId = this.ctx.session.sessionUser.accountId;            // 初始化事务            const time = new Date();            const transaction = await this.db.beginTransaction();            let result = false;            try {                const noYbAuditors = change.auditors.filter(x => { return x.audit_order !== 0; });                const noYbMaxOrder = noYbAuditors[noYbAuditors.length - 1].order;                const maxOrder = change.auditors[change.auditors.length - 1].order;                const audits = change.auditors.filter(x => { return x.order === noYbMaxOrder; });                if (!audits || audits.length === 0 || maxOrder < 1) throw '审核数据错误';                const selfAudit = audits.find(x => { return x.aid === accountId; });                if (!selfAudit) throw '当前标段您无权审批';                // 当前审批人2次添加至流程中                const checkAgainAuditors = [];                audits.forEach(x => {                    checkAgainAuditors.push({                        tid: change.tid, caid: change.id, aid: x.aid,                        times: x.times, order: maxOrder + 1,                        status: !selfAudit || x.aid === selfAudit.aid ? auditConst.status.checkAgain : auditConst.status.checkSkip,                        begin_time: time, end_time: time, opinion: '',                        audit_type: x.audit_type, audit_order: x.audit_order,                    });                });                const checkingAuditors = [];                audits.forEach(x => {                    checkingAuditors.push({                        tid: change.tid, caid: change.id, aid: x.aid,                        times: x.times, order: maxOrder + 2,                        status: auditConst.status.checking,                        begin_time: time,                        audit_type: x.audit_type, audit_order: x.audit_order,                    });                });                await transaction.insert(this.tableName, checkAgainAuditors);                const checkingAuditors_result = await transaction.insert(this.tableName, checkingAuditors);                // 获取刚批量添加的所有list                // for (let j = 0; j < checkingAuditors.length; j++) {                //     checkingAuditors[j].id = checkingAuditors_result.insertId + j;                // }                // 设置变更令审批中                await transaction.update(this.ctx.service.changeApply.tableName, {                    id: change.id,                    status: auditConst.status.checking,                    notice_code: null,                    notice_uid: null,                    decimal: null,                    final_auditor_str: '',                });                //  检查三方特殊推送                await this.ctx.service.specMsg.addChangeApplyMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.change, pushOperate.change_apply.flow);                await transaction.commit();                result = true;            } catch (error) {                await transaction.rollback();                result = false;            }            return result;        }        async getAuditorGroup(caId, times) {            const auditors = await this.getAuditors(caId, times); // 全部参与的审批人            return this.ctx.helper.groupAuditors(auditors, 'order', true);        }        async getUserGroup(caId, times) {            const group = await this.getAuditorGroup(caId, times);            const sql =                'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As caid, 0 As `order`, 1 As audit_type, 0 As audit_order' +                '  FROM ' + this.ctx.service.changeApply.tableName + ' As s' +                '  LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' +                '  ON s.uid = pa.id' +                '  WHERE s.id = ?';            const sqlParam = [times, caId, caId];            const user = await this.db.queryOne(sql, sqlParam);            user.audit_order = 0;            group.unshift([ user ]);            return group;        }        async getUniqUserGroup(caId, times) {            const group = await this.getAuditorGroup(caId, times);            const sql =                'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As caid, 0 As `order`, 1 As audit_type, 0 As audit_order' +                '  FROM ' + this.ctx.service.changeApply.tableName + ' As s' +                '  LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' +                '  ON s.uid = pa.id' +                '  WHERE s.id = ?';            const sqlParam = [times, caId, caId];            const user = await this.db.queryOne(sql, sqlParam);            user.audit_order = 0;            group.unshift([ user ]);            return this.ctx.helper.groupAuditorsUniq(group);        }        async getAuditorHistory(caId, times, reverse = false) {            const history = [];            if (times >= 1) {                for (let i = 1; i <= times; i++) {                    const auditors = await this.getAuditors(caId, i);                    const group = this.ctx.helper.groupAuditors(auditors);                    const historyGroup = [];                    // 找出group里audit_order最大值                    const max_info = group.length > 0 ? this._.maxBy(group, function(item) {                        return item && item[0] && item[0].audit_order;                    }) : null;                    const max_order = max_info ? max_info[0].audit_order : -1;                    for (const g of group) {                        const his = {                            beginYear: '', beginDate: '', beginTime: '', endYear: '', endDate: '', endTime: '', begin_time: null, end_time: null,                            audit_type: g[0].audit_type, audit_order: g[0].audit_order,                            auditors: g,                        };                        if (his.audit_type === auditType.key.common) {                            his.name = g[0].name;                        } else {                            his.name = this.ctx.helper.transFormToChinese(his.audit_order) + '审';                        }                        his.is_final = his.audit_order === max_order;                        if (g[0].begin_time) {                            his.begin_time = g[0].begin_time;                            const beginTime = this.ctx.moment(g[0].begin_time);                            his.beginYear = beginTime.format('YYYY');                            his.beginDate = beginTime.format('MM-DD');                            his.beginTime = beginTime.format('HH:mm:ss');                        }                        let end_time;                        g.forEach(x => {                            if (x.status === auditConst.status.checkSkip) return;                            if (!his.status || x.status === auditConst.status.checking) his.status = x.status;                            if (x.end_time && (!end_time || x.end_time > end_time)) {                                end_time = x.end_time;                                if (his.status !== auditConst.status.checking) his.status = x.status;                            }                        });                        if (end_time) {                            his.end_time = end_time;                            const endTime = this.ctx.moment(end_time);                            his.endYear = endTime.format('YYYY');                            his.endDate = endTime.format('MM-DD');                            his.endTime = endTime.format('HH:mm:ss');                        }                        historyGroup.push(his);                    }                    if (reverse) {                        history.push(historyGroup.reverse());                    } else {                        history.push(historyGroup);                    }                }            }            return history;        }        async getUniqAuditor(caId, times) {            const auditors = await this.getAuditors(caId, times); // 全部参与的审批人            const result = [];            auditors.forEach(x => {                if (result.findIndex(r => { return x.aid === r.aid && x.audit_order === r.audit_order; }) < 0) {                    result.push(x);                }            });            return result;        }        async saveAudit(caId, times, sp_group, data) {            const transaction = await this.db.beginTransaction();            try {                const auditors = await this.getAuditGroupByList(caId, times);                const now_audit = this._.find(auditors, { aid: data.old_aid });                if (data.operate !== 'del') {                    const exist = await this.getDataByCondition({ caid: caId, times, aid: data.new_aid });                    if (exist) throw '该审核人已存在,请勿重复添加';                }                if (data.operate === 'add') {                    if (now_audit.status !== auditConst.status.uncheck && now_audit.status !== auditConst.status.checking) {                        throw '当前人下无法操作新增';                    }                    const newAudit = {                        tid: this.ctx.tender.id, caid: caId, aid: data.new_aid,                        order: now_audit.order + 1,                        audit_order: now_audit.audit_order + 1, audit_type: auditType.key.common,                        times, status: auditConst.status.uncheck,                    };                    // order+1                    await this._syncOrderByDelete(transaction, caId, now_audit.order + 1, times, '+');                    await transaction.insert(this.tableName, newAudit);                    // 更新审批流程页数据,如果存在                } else if (data.operate === 'add-sibling') {                    if (now_audit.status !== auditConst.status.uncheck && now_audit.status !== auditConst.status.checking) {                        throw '当前人下无法操作新增';                    }                    const newAudit = {                        tid: this.ctx.tender.id, caid: caId, aid: data.new_aid,                        order: now_audit.order,                        audit_order: now_audit.audit_order, audit_type: now_audit.audit_type,                        times, status: auditConst.status.uncheck,                    };                    await transaction.insert(this.tableName, newAudit);                } else if (data.operate === 'del') {                    if (now_audit.status !== auditConst.status.uncheck) {                        throw '当前人无法操作删除';                    }                    const flowAuditors = auditors.filter(x => { return x.audit_order === now_audit.audit_order; });                    await transaction.delete(this.tableName, { caid: caId, times, aid: now_audit.aid, order: now_audit.order });                    if (flowAuditors.length === 1) await this._syncOrderByDelete(transaction, caId, now_audit.order, times);                    // 旧的更新为is_old为1                    // await transaction.update(this.tableName, { is_old: 1 }, {                    //     where: {                    //         sid: stageId,                    //         times,                    //         aid: data.old_aid,                    //     }                    // });                } else if (data.operate === 'change') {                    const nowAudit = await this.getDataByCondition({ caid: caId, times, aid: now_audit.aid, order: now_audit.order });                    if (now_audit.status !== auditConst.status.uncheck || !nowAudit) {                        throw '当前人无法操作替换';                    }                    nowAudit.aid = data.new_aid;                    await transaction.update(this.tableName, nowAudit);                    // 旧的更新为is_old为1                    // await transaction.update(this.tableName, { is_old: 1 }, {                    //     where: {                    //         sid: stageId,                    //         times,                    //         aid: data.old_aid,                    //     }                    // });                }                if (this.ctx.tender.info.shenpi.change === shenpiConst.sp_status.gdspl) {                    const newAuditors = await transaction.select(this.tableName, { where: { caid: caId, times }, orders: [['order', 'asc']] });                    const newAuditorGroup = this.ctx.helper.groupAuditors(newAuditors, 'order', true);                    const uniqNewAuditorGroup = this.ctx.helper.groupAuditorsUniq(newAuditorGroup);                    // await this.ctx.service.shenpiAudit.updateAuditListWithAuditType(transaction, this.ctx.tender.id, this.ctx.tender.info.shenpi.change, shenpiConst.sp_type.change, uniqNewAuditorGroup, sp_group);                } else if (this.ctx.tender.info.shenpi.change === shenpiConst.sp_status.gdzs) {                    const newAuditors = await this.getAuditGroupByList(caId, times, transaction);                    await this.ctx.service.shenpiAudit.updateAuditList(transaction, this.ctx.tender.id, this.ctx.tender.info.shenpi.change, shenpiConst.sp_type.change, this._.map(newAuditors, 'aid'));                }                // 更新到审批流程方法                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        async changeSpGroup(change, sp_group) {            const transaction = await this.db.beginTransaction();            try {                const group = await this.ctx.service.shenpiGroup.getDataById(sp_group);                if (!group) {                    throw '该固定审批组不存在,请刷新页面重新获取';                }                const shenpiList = await this.ctx.service.shenpiAudit.getAllDataByCondition({ where: { tid: this.ctx.tender.id, sp_type: shenpiConst.sp_type.change, sp_status: shenpiConst.sp_status.gdspl, sp_group: group.id } });                await this.ctx.service.shenpiAudit.noYbShenpiList(change.uid, shenpiList);                await this.updateNewAuditors(change, shenpiList, transaction);                await transaction.update(this.ctx.service.changeApply.tableName, { id: change.id, sp_group: group.id });                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }            return true;        }        async updateNewAuditList(change, newList) {            const transaction = await this.db.beginTransaction();            try {                await this.updateNewAuditors(change, newList, transaction);                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        async updateNewAuditors(change, newList, transaction) {            // 先删除旧的审批流,再添加新的            await transaction.delete(this.tableName, { caid: change.id, times: change.times });            const newAuditors = [];            for (const auditor of newList) {                newAuditors.push({                    tid: change.tid, caid: change.id, aid: auditor.audit_id,                    times: change.times, order: auditor.audit_order, status: auditConst.status.uncheck,                    audit_type: auditor.audit_type, audit_order: auditor.audit_order,                });            }            if (newAuditors.length > 0) await transaction.insert(this.tableName, newAuditors);        }        async updateLastAudit(change, auditList, lastId) {            const transaction = await this.db.beginTransaction();            try {                // 先判断auditList里的aid是否与lastId相同,相同则删除并重新更新order                const existAudit = auditList.find(x => { return x.aid === lastId; });                let order = auditList.length > 0 ? auditList.reduce((rst, a) => { return Math.max(rst, a.order); }, 0) + 1 : 1; // 最大值 + 1                if (existAudit) {                    await transaction.delete(this.tableName, { caid: change.id, times: change.times, aid: lastId });                    const sameOrder = auditList.filter(x => { return x.order === existAudit.order; });                    if (sameOrder.length === 1) {                        const updateData = [];                        auditList.forEach(x => {                            if (x.order <= existAudit.order) return;                            updateData.push({ id: x.id, order: x.order - 1, audit_order: x.audit_order - 1 });                        });                        if (updateData.length > 0) {                            await transaction.updateRows(updateData);                        }                        order = order - 1;                    }                }                // 添加终审                const newAuditor = {                    tid: change.tid, caid: change.id, aid: lastId,                    times: change.times, order, status: auditConst.status.uncheck,                    audit_type: auditType.key.common, audit_order: order,                };                await transaction.insert(this.tableName, newAuditor);                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        /**         * 取待审批期列表(wap用)         *         * @param auditorId         * @return {Promise<*>}         */        async getAuditChangeApplyByWap(auditorId) {            const sql =                'SELECT sa.`aid`, sa.`times`, sa.`begin_time`, sa.`end_time`, sa.`tid`, sa.`caid`,' +                '    s.*,' +                '    t.`name` as `t_name`, t.`project_id`, t.`type`, t.`user_id`,' +                '    ti.`deal_info`, ti.`decimal` ' +                '  FROM ?? AS sa' +                '    Left Join ?? AS s On sa.`caid` = s.`id`' +                '    Left Join ?? As t On sa.`tid` = t.`id`' +                '    Left Join ?? AS ti ON ti.`tid` = t.`id`' +                '  WHERE sa.`aid` = ? and sa.`status` = ?';            const sqlParam = [                this.tableName,                this.ctx.service.changeApply.tableName,                this.ctx.service.tender.tableName,                this.ctx.service.tenderInfo.tableName,                auditorId,                auditConst.status.checking,            ];            return await this.db.query(sql, sqlParam);        }    }    return ChangeApplyAudit;};
 |