| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 | 'use strict';/** * * * @author Mai * @date 2019/2/27 * @version */const auditConst = require('../const/audit').financial;const auditType = require('../const/audit').auditType;const shenpiConst = require('../const/shenpi');const pushType = require('../const/audit').pushType;module.exports = app => {    class FinancialPayAudit extends app.BaseService {        /**         * 构造函数         *         * @param {Object} ctx - egg全局变量         * @return {void}         */        constructor(ctx) {            super(ctx);            this.tableName = 'financial_pay_audit';        }        async getAuditorGroup(fpId, times) {            const auditors = await this.getAuditors(fpId, times); // 全部参与的审批人            return this.ctx.helper.groupAuditors(auditors, 'order', true);        }        async getUserGroup(fpId, times) {            const group = await this.getAuditorGroup(fpId, times);            const sql =                'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As fpid, 0 As `order`, 1 As audit_type, 0 As audit_order' +                '  FROM ' + this.ctx.service.financialPay.tableName + ' As s' +                '  LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' +                '  ON s.uid = pa.id' +                '  WHERE s.id = ?';            const sqlParam = [times, fpId, fpId];            const user = await this.db.queryOne(sql, sqlParam);            user.audit_order = 0;            group.unshift([ user ]);            return group;        }        async getUniqUserGroup(fpId, times) {            const group = await this.getAuditorGroup(fpId, times);            const sql =                'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As fpid, 0 As `order`, 1 As audit_type, 0 As audit_order' +                '  FROM ' + this.ctx.service.financialPay.tableName + ' As s' +                '  LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' +                '  ON s.uid = pa.id' +                '  WHERE s.id = ?';            const sqlParam = [times, fpId, fpId];            const user = await this.db.queryOne(sql, sqlParam);            user.audit_order = 0;            group.unshift([ user ]);            return this.ctx.helper.groupAuditorsUniq(group);        }        async getAuditorHistory(fpId, times, reverse = false) {            const history = [];            if (times >= 1) {                for (let i = 1; i <= times; i++) {                    const auditors = await this.getAuditors(fpId, 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(fpId, times) {            const auditors = await this.getAuditors(fpId, 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;        }        /**         * 获取 审核列表信息         *         * @param {Number} cpId - 变更立项id         * @param {Number} times - 第几次审批         * @return {Promise<*>}         */        async getAuditors(fpId, 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 `cpid` = ? 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.`cpid` = ? 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, cpId, times, cpId, times];            // const result = await this.db.query(sql, sqlParam);            // const sql2 = 'SELECT COUNT(a.`aid`) as num FROM (SELECT `aid` FROM ?? WHERE `cpid` = ? AND `times` = ? GROUP BY `aid`) as a';            // const sqlParam2 = [this.tableName, cpId, 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.fpid = ? AND la.times = ?' + ybSql +                '  ORDER BY la.order ' + order_sort;            const sqlParam = [fpId, 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;        }        async getCurAuditors(fpId, 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.`fpid` = ? and la.`status` = ? and la.`times` = ?';            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, auditConst.status.checking, times];            return await this.db.query(sql, sqlParam);        }        /**         * 获取审核人流程列表         *         * @param auditorId         * @return {Promise<*>}         */        async getAuditGroupByList(fpId, times, transaction = false) {            const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`fpid`, 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.`fpid` = ? and la.`times` = ? GROUP BY la.`aid` ORDER BY la.`order`';            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, times];            return transaction !== false ? await transaction.query(sql, sqlParam) : await this.db.query(sql, sqlParam);        }        async getAuditorsByStatus(fpId, status, times = 1) {            let auditor = [];            let sql = '';            let sqlParam = '';            let cur;            switch (status) {                case auditConst.status.checking :                case auditConst.status.checked :                case auditConst.status.cancelRevise :                    cur = await this.db.queryOne('SELECT * From ?? where fpid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, fpId, times, status]);                    if (!cur) return [];                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`fpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' +                        '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +                        '  WHERE la.`fpid` = ? and la.`order` = ? and times = ?';                    sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, cur.order, times];                    auditor = await this.db.query(sql, sqlParam);                    break;                case auditConst.status.checkNo :                    cur = await this.db.queryOne('SELECT * From ?? where fpid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, fpId, parseInt(times) - 1, status]);                    if (!cur) return [];                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`fpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' +                        '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +                        '  WHERE la.`fpid` = ? and la.`order` = ? and la.`times` = ?';                    sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, fpId, cur.order, parseInt(times) - 1];                    auditor = await this.db.query(sql, sqlParam);                    break;                case auditConst.status.uncheck:                default:                    break;            }            return auditor;        }        /**         * 获取审核人流程列表(包括原报)         * @param {Number} materialId 调差id         * @param {Number} times 审核次数         * @return {Promise<Array>} 查询结果集(包括原报)         */        async getAuditorsWithOwner(fpId, times = 1) {            const result = await this.getAuditGroupByList(fpId, times);            const sql =                'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As fpid, 0 As `order`' +                '  FROM ' +                this.ctx.service.financialPay.tableName +                ' As s' +                '  LEFT JOIN ' +                this.ctx.service.projectAccount.tableName +                ' As pa' +                '  ON s.uid = pa.id' +                '  WHERE s.id = ?';            const sqlParam = [times, fpId, fpId];            const user = await this.db.queryOne(sql, sqlParam);            result.unshift(user);            return result;        }        async updateNewAuditList(financialPay, newList) {            const transaction = await this.db.beginTransaction();            try {                await this.updateNewAuditors(financialPay, newList, transaction);                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        async updateNewAuditors(financialPay, newList, transaction) {            // 先删除旧的审批流,再添加新的            await transaction.delete(this.tableName, { fpid: financialPay.id, times: financialPay.times });            const newAuditors = [];            for (const auditor of newList) {                newAuditors.push({                    spid: financialPay.spid, tid: financialPay.tid, fpid: financialPay.id, aid: auditor.audit_id,                    times: financialPay.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);        }        /**         * 开始审批         * @param {Number} cpId - 方案id         * @param {Number} times - 第几次审批         * @return {Promise<boolean>}         */        async start(fpId, times = 1) {            const audits = await this.getAllDataByCondition({ where: { fpid: fpId, times, order: 1 } });            if (audits.length === 0) {                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.financialPay.tableName, {                    id: fpId, status: auditConst.status.checking,                    entities: await this.ctx.service.financialPayContract.getEntities(fpId),                });                // todo 更新标段tender状态 ?                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }            return true;        }        /**         * 审批         * @param {Object} fp - 资金支付信息         * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果         * @return {Promise<void>}         */        async check(fp, checkData) {            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, fp, checkData);                    break;                case auditConst.status.checkNo:                    await this._checkNo(pid, fp, checkData);                    break;                default:                    throw '无效审批操作';            }        }        async _checked(pid, fp, checkData) {            const accountId = this.ctx.session.sessionUser.accountId;            const time = new Date();            // 整理当前流程审核人状态更新            const audits = fp.curAuditors;            if (audits.length === 0) throw '审核数据错误';            const selfAudit = audits.find(x => { return x.aid === accountId; });            if (!selfAudit) throw '当前标段您无权审批';            // const flowAudits = await this.getAllDataByCondition({ where: { cpid: cpId, times, order: selfAudit.order } });            const nextAudits = fp.nextAuditors;            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, fp.spid, fp.id, selfAudit.aid, checkData.opinion);                // 添加推送                const records = [];                const auditors = await this.getAuditorsWithOwner(fp.id, fp.times);                auditors.forEach(audit => {                    records.push({ pid, type: pushType.financial, 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.financialPay.tableName, {                            id: fp.id, status: auditConst.status.checking,                        });                    } else {                        // 本期结束                        // 生成截止本期数据 final数据                        // 同步 期信息                        await transaction.update(this.ctx.service.financialPay.tableName, {                            id: fp.id, status: checkData.checkType,                        });                    }                } else {                    // 同步 期信息                    await transaction.update(this.ctx.service.financialPay.tableName, {                        id: fp.id,                        status: auditConst.status.checking,                    });                }                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        async _checkNo(pid, fp, checkData) {            const accountId = this.ctx.session.sessionUser.accountId;            const time = new Date();            const audits = fp.curAuditors;            if (!audits) throw '审核数据错误';            const selfAudit = audits.find(x => { return x.aid === accountId; });            if (!selfAudit) throw '当前标段您无权审批';            const auditors = await this.getUniqAuditor(fp.id, fp.times); // 全部参与的审批人            const newAuditors = auditors.map(x => {                return {                    aid: x.aid, spid: fp.spid, tid: fp.tid, fpid: fp.id,                    times: fp.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);                // 添加到消息推送表                const noticeContent = await this.getNoticeContent(pid, fp.spid, fp.id, selfAudit.aid, checkData.opinion);                const records = [{ pid, type: pushType.financial, uid: fp.uid, status: auditConst.status.checkNo, content: noticeContent }];                auditors.forEach(audit => {                    records.push({ pid, type: pushType.financial, uid: audit.aid, status: auditConst.status.checkNo, content: noticeContent });                });                await transaction.insert(this.ctx.service.noticePush.tableName, records);                // 同步期信息                await transaction.update(this.ctx.service.financialPay.tableName, {                    id: fp.id, status: checkData.checkType,                    times: fp.times + 1,                    entities: '',                });                // 拷贝新一次审核流程列表                await transaction.insert(this.tableName, newAuditors);                await transaction.commit();            } catch (err) {                await transaction.rollback();                throw err;            }        }        /**         * 获取审核人需要审核的期列表         *         * @param auditorId         * @return {Promise<*>}         */        async getAuditFinancial(auditorId) {            const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`spid`, ma.`fpid`,' +                '    m.`status` As `fpstatus`, m.`code` As `fpcode`,' +                '    sp.`name`, sp.`project_id` ' +                '  FROM ?? AS ma LEFT JOIN ?? AS m ON  ma.`fpid` = m.`id` LEFT JOIN ?? As sp ON ma.`spid` = sp.`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.financialPay.tableName, this.ctx.service.subProject.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 fpidArr = [];            for (const r of result) {                if (fpidArr.indexOf(r.fpid) === -1) {                    filterResult.push(r);                    fpidArr.push(r.fpid);                }            }            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} cpId 方案id         * @param {Number} uid 审批人id         */        async getNoticeContent(pid, spid, fpId, uid, opinion = '') {            const noticeSql = 'SELECT * FROM (SELECT ' +                '  sp.`id` As `spid`, ma.`fpid`, m.`code`, sp.`name`, pa.`name` As `su_name`, pa.role As `su_role`' +                '  FROM (SELECT * FROM ?? WHERE `id` = ? ) As sp' +                '  LEFT JOIN ?? As m On sp.`id` = m.`spid` AND m.`id` = ?' +                '  LEFT JOIN ?? As ma ON m.`id` = ma.`fpid`' +                '  LEFT JOIN ?? As pa ON pa.`id` = ?' +                '  WHERE sp.`project_id` = ? ) as new_t GROUP BY new_t.`spid`';            const noticeSqlParam = [this.ctx.service.subProject.tableName, spid, this.ctx.service.financialPay.tableName, fpId, 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]) : '';        }    }    return FinancialPayAudit;};
 |