Jelajahi Sumber

通知重新发送代码no.1 up

ellisran 1 tahun lalu
induk
melakukan
20ef3c93d8

+ 14 - 0
app/const/account_permission.js

@@ -94,10 +94,24 @@ const PermissionCheck = {
         return this.check(permission, 'viewPmData');
     },
 };
+// 用户重新发送权限默认值
+const noticeAgain = {
+    checked: false,
+    sp: {
+        advance: { checked: false, name: '预付款' },
+        ledger: { checked: false, name: '台账审批' },
+        revise: { checked: false, name: '台账修订' },
+        stage: { checked: false, name: '计量台账' },
+        change: { checked: false, name: '工程变更' },
+        material: { checked: false, name: '材料调差' },
+        settle: { checked: false, name: '结算期' },
+    },
+};
 
 module.exports = {
     tenderPermission: create_tender,
     tenderPermissionList: create_tender_group,
     permission,
     PermissionCheck,
+    noticeAgain,
 };

+ 26 - 0
app/const/project_setting.js

@@ -0,0 +1,26 @@
+'use strict';
+
+/**
+ * 项目的通用 相关常量
+ *
+ * @author Mai
+ * @date 2018/9/25
+ * @version
+ */
+// 重新发送通知设置
+const notice_setting = {
+    fixed: 6, // 固定间隔
+    activity: { // 活动间隔
+        first: 6,
+        second: 6,
+        later: 6,
+    },
+    mode: 'fixed', // 默认模式
+    shield_times: { // 屏蔽时间段
+        start: null,
+        end: null,
+    },
+};
+module.exports = {
+    noticeSetting: notice_setting,
+};

+ 26 - 1
app/controller/setting_controller.js

@@ -15,6 +15,7 @@ const scheduleConst = require('../const/schedule');
 const settingMenu = require('../../config/menu').settingMenu;
 const accountGroup = require('../const/account_group').group;
 const permission = require('../const/account_permission').permission;
+const noticeAgainConst = require('../const/account_permission').noticeAgain;
 const projectLog = require('../const/project_log');
 const imType = require('../const/tender').imType;
 const S2b = require('../lib/s2b');
@@ -22,6 +23,7 @@ const measureType = require('../const/tender').measureType;
 const sendToWormhole = require('stream-wormhole');
 const path = require('path');
 const funSet = require('../const/fun_set');
+const projectSettingConst = require('../const/project_setting');
 
 module.exports = app => {
 
@@ -236,7 +238,7 @@ module.exports = app => {
                 // 获取数据规则
                 const page = ctx.page;
                 const pageSize = ctx.pageSize;
-                const columns = ['id', 'account', 'name', 'company', 'role', 'mobile', 'auth_mobile', 'telephone', 'enable', 'is_admin', 'bind', 'account_group', 'permission', 'cooperation'];
+                const columns = ['id', 'account', 'name', 'company', 'role', 'mobile', 'auth_mobile', 'telephone', 'enable', 'is_admin', 'bind', 'account_group', 'permission', 'cooperation', 'notice_again'];
                 // 过滤数据
                 ctx.service.projectAccount.searchFilter(ctx.request.query, projectId, columns);
                 ctx.sort = ['id', 'desc'];
@@ -259,6 +261,8 @@ module.exports = app => {
 
                 const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: projectId}});
 
+                const noticeSet = projectData.notice_setting ? JSON.parse(projectData.notice_setting) : ctx.helper._.cloneDeep(projectSettingConst.noticeSetting);
+
                 const renderData = {
                     projectData,
                     accountData,
@@ -270,6 +274,8 @@ module.exports = app => {
                     user_total,
                     unitList,
                     company,
+                    noticeAgainConst,
+                    noticeSet,
                     // rule: JSON.stringify(frontRule),
                 };
                 await this.layout('setting/user_permission.ejs', renderData, 'setting/user_permission_modal.ejs');
@@ -828,6 +834,25 @@ module.exports = app => {
             }
         }
 
+        async updateProjectSet(ctx) {
+            try {
+                const projectId = ctx.session.sessionProject.id;
+                const data = JSON.parse(ctx.request.body.data);
+                const allowField = ['notice_setting']; // 暂时只一个
+                if (!ctx.helper._.includes(allowField, data.field)) {
+                    throw '非配置的字段无法使用本方法';
+                }
+                const result = await ctx.service.project.updateProjectSet(projectId, data.field, data.pData);
+                if (!result) {
+                    throw '提交数据失败';
+                }
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         /**
          * 检测账户是否存在
          *

+ 66 - 3
app/extend/helper.js

@@ -1121,7 +1121,7 @@ module.exports = {
     },
 
     // 企业微信和公众号微信通知共用
-    async sendWechat(userId, type, judge, template, data = {}) {
+    async sendWechat(userId, type, judge, template, data = {}, tender = this.ctx.tender) {
         const wechats = [];
         const qywxs = {};
         if (!userId || (userId instanceof Array && userId.length === 0)) return;
@@ -1141,8 +1141,8 @@ module.exports = {
 
         if (wechats.length > 0 || !_.isEmpty(qywxs)) {
             const wx = new WX(this.ctx);
-            const tenderName = await wx.contentChange(this.ctx.tender.data.name);
-            const projectName = await wx.contentChange(this.ctx.tender.info.deal_info.buildName);
+            const tenderName = await wx.contentChange(tender.data.name);
+            const projectName = await wx.contentChange(tender.info.deal_info.buildName);
             const param = {
                 projectName,
                 tenderName,
@@ -1687,4 +1687,67 @@ module.exports = {
         str = str.length > 20 ? str.substring(0, 17) + '...' : str;
         return str;
     },
+    calculateNextSendTime(startTime, intervalHours = 6, quietStart = 0, quietEnd = 8) {
+        function parseTime(timeStr, day = 0) {
+            const [hours, minutes] = timeStr.split(':').map(Number);
+            const date = new Date();
+            date.setDate(date.getDate() + day);
+            date.setHours(hours, minutes, 0, 0); // Set hours and minutes, reset seconds and milliseconds
+            return date;
+        }
+        // let startDateTime = parseTime(startTime);
+        let startDateTime = startTime;
+        let endDateTime = new Date(startDateTime.getTime() + intervalHours * 60 * 60 * 1000);
+        if (quietStart && quietEnd) {
+            quietStart = (quietStart < 10 ? '0' + quietStart : quietStart) + ':00';
+            quietEnd = (quietEnd < 10 ? '0' + quietEnd : quietEnd) + ':00';
+            let quietStartDateTime = parseTime(quietStart);
+            let quietEndDateTime = parseTime(quietEnd);
+            if (quietEndDateTime <= quietStartDateTime) {
+                quietEndDateTime.setDate(quietEndDateTime.getDate() + 1);
+            }
+            // 判断非屏蔽时间内是否大于间隔时间,大于则要再隔天发送
+            const hours = this.calculateHoursBetween(quietStart, quietEnd);
+            if (hours < intervalHours) {
+                // 除法算出差的天数
+                const days = Math.floor(intervalHours / hours);
+                quietStartDateTime = parseTime(quietStart, days);
+                quietEndDateTime = parseTime(quietEnd, days);
+                intervalHours %= hours;
+                startDateTime.setDate(startDateTime.getDate() + days);
+            } else if (startDateTime >= quietEndDateTime) {
+                quietStartDateTime = parseTime(quietStart, 1);
+            }
+
+            if (quietEndDateTime <= quietStartDateTime) {
+                quietEndDateTime.setDate(quietEndDateTime.getDate() + 1);
+            }
+            if (startDateTime >= quietStartDateTime) {
+                startDateTime = quietEndDateTime;
+            }
+            endDateTime = new Date(startDateTime.getTime() + intervalHours * 60 * 60 * 1000);
+            if (endDateTime >= quietStartDateTime && endDateTime < quietEndDateTime) {
+                const timeIntoQuiet = (quietStartDateTime - startDateTime) / (60 * 60 * 1000);
+                const remainingTime = intervalHours - timeIntoQuiet;
+                endDateTime = new Date(quietEndDateTime.getTime() + remainingTime * 60 * 60 * 1000);
+            }
+        }
+        // return endDateTime.getHours().toString().padStart(2, '0') + ':' +
+        //     endDateTime.getMinutes().toString().padStart(2, '0');
+        return endDateTime;
+    },
+    calculateHoursBetween(start, end) {
+        // 将时间字符串转换为 Date 对象
+        const startTime = new Date(`2000-01-01T${start}`);
+        const endTime = new Date(`2000-01-01T${end}`);
+        // 检查是否跨越了当前日期和隔天日期
+        if (endTime < startTime) {
+            endTime.setDate(endTime.getDate() + 1); // 将结束时间调整到隔天
+        }
+        // 计算两个时间之间的毫秒数差距
+        const timeDifference = endTime - startTime;
+        // 将毫秒数转换为小时并返回
+        const hours = timeDifference / (1000 * 60 * 60);
+        return 24 - hours;
+    },
 };

+ 1 - 0
app/public/js/change_revise.js

@@ -2047,6 +2047,7 @@ $(document).ready(() => {
         billsTreeSpreadObj.refreshOperationValid(billsSheet);
         billsTreeSpreadObj.loadExprToInput(billsSheet);
         checkList.loadHisCheckData();
+        console.log(billsTree);
     }, null);
     $.divResizer({
         select: '#revise-resize',

+ 8 - 0
app/public/js/setting.js

@@ -166,6 +166,14 @@ $(document).ready(() => {
         }
         // 协作赋值
         $('#edit-user2 input:radio[name="cooperation"][value="' + account.cooperation + '"]').prop('checked', true);
+        // 微信通知赋值
+        if (account.notice_again) {
+            const noticeAgain = JSON.parse(account.notice_again);
+            $('#again_all').prop('checked', noticeAgain.checked);
+            for (const sp in noticeAgainConst.sp) {
+                $('#again_' + sp).prop('checked', noticeAgain.sp[sp] !== undefined ? noticeAgain.sp[sp] : false);
+            }
+        }
     });
 
     // 选择创建标段功能后可选协作办公

+ 1 - 0
app/router.js

@@ -90,6 +90,7 @@ module.exports = app => {
     // 项目信息
     app.get('/setting/info', sessionAuth, 'settingController.info');
     app.post('/setting/updateinfo/:id', sessionAuth, 'settingController.updateinfo');
+    app.post('/setting/update/projectset', sessionAuth, 'settingController.updateProjectSet');
     // 账号设置
     app.get('/setting/user', sessionAuth, 'settingController.user');
     app.get('/setting/user/permission/set', sessionAuth, 'settingController.userPermissionSet');

+ 94 - 0
app/schedule/shenpi_again.js

@@ -0,0 +1,94 @@
+'use strict';
+
+/**
+ * 审批信息重新发送定时任务
+ *
+ * @author CaiAoLin
+ * @date 2017/10/26
+ * @version
+ */
+
+const Subscription = require('egg').Subscription;
+const projectSettingConst = require('../const/project_setting');
+const smsTypeConst = require('../const/sms_type');
+class ShenpiAgain extends Subscription {
+    static get schedule() {
+        return {
+            interval: '1m',
+            type: 'all',
+        };
+    }
+
+    async subscribe() {
+        const ctx = this.ctx;
+        const list = await ctx.service.noticeAgain.getAllDataByCondition({ where: { status: 1 } });
+        if (list.length === 0) return;
+        const pidList = ctx.helper._.uniq(ctx.helper._.map(list, 'pid'));
+        const projectsNoticeSetting = await ctx.service.project.getAllDataByCondition({
+            columns: ['id', 'notice_setting'],
+            where: { id: pidList },
+        });
+        const uidList = ctx.helper._.uniq(ctx.helper._.map(list, 'uid'));
+        const usersNoticeSetting = await ctx.service.projectAccount.getAllDataByCondition({
+            columns: ['id', 'project_id', 'notice_again'],
+            where: { id: uidList },
+        });
+        const tidList = ctx.helper._.uniq(ctx.helper._.map(list, 'tid'));
+        const tenderList = await ctx.service.tender.getAllDataByCondition({ columns: ['id', 'name'], where: { id: tidList } });
+        const tenderInfoList = await ctx.service.tenderInfo.getAllDataByCondition({ columns: ['id', 'tid', 'deal_info'], where: { tid: tidList } });
+        const updateData = [];
+        const deleteData = [];
+        for (const i of list) {
+            if (i.times > 10) continue;// 发超过10次就不发了吧
+            const uinfo = ctx.helper._.find(usersNoticeSetting, { id: i.uid });
+            const notice_again = uinfo.notice_again ? JSON.parse(uinfo.notice_again) : null;
+            if (!notice_again) continue;
+            const pinfo = ctx.helper._.find(projectsNoticeSetting, { id: i.pid });
+            const notice_setting = pinfo.notice_setting ? JSON.parse(pinfo.notice_setting) : ctx.helper._.cloneDeep(projectSettingConst.noticeSetting);
+            const interval = notice_setting.mode === 'fixed' ? notice_setting.fixed : (i.times === 0 ? notice_setting.activity.first : (i.times === 1 ? notice_setting.activity.second : notice_setting.activity.later));
+            const send_time = ctx.helper.calculateNextSendTime(i.last_time, interval, notice_setting.shield_times.start, notice_setting.shield_times.end);
+            console.log(new Date(), send_time);
+            if (new Date() > send_time) {
+                // 判断是否已经sp_type的sp_id已经完成审批或者是否存在,如果已经审批或不存在则需要删除
+                const spInfo = await ctx.service.noticeAgain.getSpResult(i.table_name, i.sp_id);
+                if (!spInfo) {
+                    deleteData.push(i.id);
+                    continue;
+                } else {
+                    const status = spInfo.status || spInfo.audit_status;
+                    if (status !== 2) {
+                        updateData.push({
+                            id: i.id,
+                            status: 2,
+                        });
+                        continue;
+                    }
+                }
+                // 重发信息并记录到更新表里数据
+                const t = ctx.helper._.find(tenderList, { id: i.tid });
+                const tenderInfo = ctx.helper._.find(tenderInfoList, { tid: i.tid });
+                const newTenderInfo = {
+                    deal_info: { buildName: '' },
+                };
+                if (tenderInfo) {
+                    newTenderInfo.deal_info = JSON.parse(tenderInfo.deal_info);
+                }
+                const tender = {
+                    data: t,
+                    info: newTenderInfo,
+                };
+                console.log(tender);
+                // await ctx.helper.sendWechat(i.uid, i.sms_type, smsTypeConst.judge.approval.toString(), i.template, JSON.parse(i.wx_data), tender);
+                updateData.push({
+                    id: i.id,
+                    times: i.times + 1,
+                    last_time: ctx.helper.dateTran(new Date(), 'YYYY-MM-DD HH:mm'),
+                });
+            }
+        }
+        // if (updateData.length > 0) await ctx.service.noticeAgain.defaultUpdateRows(updateData);
+        // if (deleteData.length > 0) await ctx.service.noticeAgain.deleteById(deleteData);
+    }
+}
+
+module.exports = ShenpiAgain;

+ 40 - 8
app/service/advance_audit.js

@@ -242,8 +242,18 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(audit.audit_id, smsTypeConst.const.YFK, smsTypeConst.judge.approval.toString(), wxConst.template.advance, wechatData);
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.YFK, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.tender.id,
+                    uid: audit.audit_id,
+                    sp_type: 'advance',
+                    sp_id: audit.id,
+                    table_name: this.tableName,
+                    template: wxConst.template.advance,
+                    wx_data: wechatData,
+                });
                 // 审批通过 - 检查三方特殊推送
-                await this.ctx.service.specMsg.addAdvanceMsg(transaction, pid, advanceInfo, pushOperate.advance.flow);
+                await this.ctx.service.specMsg.addAdvanceMsg(transaction, this.ctx.session.sessionProject.id, advanceInfo, pushOperate.advance.flow);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -271,6 +281,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 获取推送必要信息
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, advanceId, audit.audit_id, checkData.opinion);
                 // 添加推送
@@ -305,6 +316,16 @@ module.exports = app => {
                         code: this.ctx.session.sessionProject.code,
                     };
                     await this.ctx.helper.sendWechat(nextAudit.audit_id, smsTypeConst.const.YFK, smsTypeConst.judge.approval.toString(), wxConst.template.advance, wechatData);
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.YFK, {
+                        pid,
+                        tid: this.ctx.tender.id,
+                        uid: nextAudit.audit_id,
+                        sp_type: 'advance',
+                        sp_id: nextAudit.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.advance,
+                        wx_data: wechatData,
+                    });
                     // 审批通过 - 检查三方特殊推送
                     await this.ctx.service.specMsg.addAdvanceMsg(transaction, pid, advanceInfo, pushOperate.advance.flow);
                 } else {
@@ -357,7 +378,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
-
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 添加到消息推送表
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, advanceId, audit.audit_id, checkData.opinion);
                 const records = [{ pid, type: pushType.advance, uid: this.ctx.advance.uid, status: auditConst.status.checkNo, content: noticeContent }];
@@ -421,6 +442,7 @@ module.exports = app => {
                 });
                 await transaction.insert(this.ctx.service.noticePush.tableName, records);
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 顺移气候审核人流程顺序
                 this.initSqlBuilder();
                 this.sqlBuilder.setAndWhere('vid', { value: advanceId, operate: '=' });
@@ -428,8 +450,7 @@ module.exports = app => {
                 this.sqlBuilder.setUpdateData('order', { value: 2, selfOperate: '+' });
                 const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
                 await transaction.query(sql, sqlParam);
-                const newAuditors = [];
-                newAuditors.push({
+                const newAuditors = {
                     tid: audit.tid,
                     vid: audit.vid,
                     audit_id: preAuditor.audit_id,
@@ -437,15 +458,17 @@ module.exports = app => {
                     order: audit.order + 1,
                     status: auditConst.status.checking,
                     create_time: time,
-                });
-                newAuditors.push({
+                };
+                const checking_result = await transaction.insert(this.tableName, newAuditors);
+                const uncheckNewAuditors = {
                     tid: audit.tid,
                     vid: audit.vid,
                     audit_id: audit.audit_id,
                     times: audit.times,
                     order: audit.order + 2,
                     status: auditConst.status.uncheck,
-                });
+                };
+                await transaction.insert(this.tableName, uncheckNewAuditors);
                 // 微信模板通知
                 const advanceInfo = await this.ctx.service.advance.getDataById(advanceId);
                 const shenpiUrl = await this.ctx.helper.urlToShort(
@@ -460,7 +483,16 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(preAuditor.audit_id, smsTypeConst.const.YFK, smsTypeConst.judge.approval.toString(), wxConst.template.advance, wechatData);
-                await transaction.insert(this.tableName, newAuditors);
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.YFK, {
+                    pid,
+                    tid: this.ctx.tender.id,
+                    uid: preAuditor.audit_id,
+                    sp_type: 'advance',
+                    sp_id: checking_result.insertId,
+                    table_name: this.tableName,
+                    template: wxConst.template.advance,
+                    wx_data: wechatData,
+                });
                 // 检查三方特殊推送
                 await this.ctx.service.specMsg.addAdvanceMsg(transaction, pid, advanceInfo, pushOperate.advance.flow);
                 await transaction.commit();

+ 27 - 1
app/service/change.js

@@ -872,6 +872,7 @@ module.exports = app => {
                     cin_time: Date.parse(new Date()) / 1000,
                 };
                 await this.transaction.update(this.ctx.service.changeAudit.tableName, audit_update);
+                await this.ctx.service.noticeAgain.stopNoticeAgain(this.transaction, this.ctx.service.changeAudit.tableName, curAudit.id);
                 // 清单数据更新
                 const bills_list = postData.bills_list.split(',');
                 let total_price = 0;
@@ -962,6 +963,17 @@ module.exports = app => {
                         c_name: changeData.name,
                     };
                     await this.ctx.helper.sendWechat(nextAudit.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                    // 重新发送配置
+                    await this.ctx.service.noticeAgain.addNoticeAgain(this.transaction, smsTypeConst.const.BG, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: changeData.tid,
+                        uid: nextAudit.uid,
+                        sp_type: 'change',
+                        sp_id: nextAudit.id,
+                        table_name: this.ctx.service.changeAudit.tableName,
+                        template: wxConst.template.change,
+                        wx_data: wechatData,
+                    });
                 }
                 change_update.total_price = total_price;
                 const options = {
@@ -998,6 +1010,7 @@ module.exports = app => {
                     sin_time: new Date(),
                 };
                 await this.transaction.update(this.ctx.service.changeAudit.tableName, audit_update);
+                await this.ctx.service.noticeAgain.stopNoticeAgain(this.transaction, this.ctx.service.changeAudit.tableName, postData.audit_id);
                 // 设置变更令终止
                 const change_update = {
                     w_code: postData.w_code,
@@ -1055,6 +1068,7 @@ module.exports = app => {
                     sin_time: new Date(),
                 };
                 await this.transaction.update(this.ctx.service.changeAudit.tableName, audit_update);
+                await this.ctx.service.noticeAgain.stopNoticeAgain(this.transaction, this.ctx.service.changeAudit.tableName, postData.audit_id);
                 // 新增新一次的审批人列表
                 // 获取当前次数审批人列表
                 const auditList = await this.ctx.service.changeAudit.getListGroupByTimes(changeInfo.cid, changeInfo.times);
@@ -1172,6 +1186,7 @@ module.exports = app => {
                     sin_time: new Date(),
                 };
                 await this.transaction.update(this.ctx.service.changeAudit.tableName, audit_update);
+                await this.ctx.service.noticeAgain.stopNoticeAgain(this.transaction, this.ctx.service.changeAudit.tableName, postData.audit_id);
                 // 获取当前审批人信息
                 const auditInfo = await this.ctx.service.changeAudit.getDataById(postData.audit_id);
 
@@ -1197,7 +1212,7 @@ module.exports = app => {
                     status: audit.flow.auditStatus.checking,
                     sin_time: new Date(),
                 };
-                await this.transaction.insert(this.ctx.service.changeAudit.tableName, insert_audit1);
+                const result_audit = await this.transaction.insert(this.ctx.service.changeAudit.tableName, insert_audit1);
                 usort++;
                 // 新增2个审批人到审批列表中
                 const insert_audit2 = {
@@ -1275,6 +1290,17 @@ module.exports = app => {
                     c_name: changeInfo.name,
                 };
                 await this.ctx.helper.sendWechat(lastauditInfo.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                // 重新发送配置
+                await this.ctx.service.noticeAgain.addNoticeAgain(this.transaction, smsTypeConst.const.BG, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.change.tid,
+                    uid: lastauditInfo.uid,
+                    sp_type: 'change',
+                    sp_id: result_audit.insertId,
+                    table_name: this.ctx.service.changeAudit.tableName,
+                    template: wxConst.template.change,
+                    wx_data: wechatData,
+                });
             } catch (error) {
                 console.log(error);
                 await this.transaction.rollback();

+ 24 - 1
app/service/change_apply_audit.js

@@ -287,6 +287,16 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                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状态 ?
                 //  检查三方特殊推送
@@ -408,7 +418,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
-
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 获取推送必要信息
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, caId, audit.aid, checkData.opinion);
                 // 添加推送
@@ -436,6 +446,16 @@ module.exports = app => {
                         c_name: this.ctx.change.name,
                     };
                     await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                    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 {
@@ -501,6 +521,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 添加到消息推送表
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, caId, audit.aid, checkData.opinion);
                 const records = [{ pid, type: pushType.changeApply, uid: this.ctx.change.uid, status: auditConst.status.checkNo, content: noticeContent }];
@@ -622,6 +643,7 @@ module.exports = app => {
                     begin_time: null,
                     opinion: null,
                 });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 // 变成待上报状态
                 await transaction.update(this.ctx.service.changeApply.tableName, {
                     id: change.id,
@@ -688,6 +710,7 @@ module.exports = app => {
                 await transaction.insert(this.tableName, newAuditors);
                 // 当前审批人变成待审批
                 await transaction.update(this.tableName, { id: curAudit.id, order: curAudit.order + 2, begin_time: null, status: auditConst.status.uncheck });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();

+ 14 - 0
app/service/change_audit.js

@@ -679,6 +679,17 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(audit.uid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                // 重新发送配置
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.BG, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.change.tid,
+                    uid: audit.uid,
+                    sp_type: 'change',
+                    sp_id: audit.id,
+                    table_name: this.tableName,
+                    template: wxConst.template.change,
+                    wx_data: wechatData,
+                });
                 await transaction.delete(this.ctx.service.changeHistory.tableName, { cid });
                 await transaction.commit();
             } catch (err) {
@@ -762,6 +773,7 @@ module.exports = app => {
                     sin_time: null,
                     sdesc: null,
                 });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 // 原报变成checking状态
                 const ybAudit = await this.getDataByCondition({ cid: change.cid, times: change.times, usite: 0, status: auditConst.auditStatus.checked });
                 await transaction.update(this.tableName, {
@@ -846,6 +858,7 @@ module.exports = app => {
                 await transaction.insert(this.tableName, newAuditors);
                 // 当前审批人变成待审批
                 await transaction.update(this.tableName, { id: curAudit.id, usort: curAudit.usort + 2, sin_time: null, status: auditConst.auditStatus.uncheck });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 // 审批列表数据也要回退
                 const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({
                     where: { cid: change.cid },
@@ -911,6 +924,7 @@ module.exports = app => {
                 // 整理当前流程审核人状态更新
                 // 删除当前审批人
                 await transaction.delete(this.tableName, { id: curAudit.id });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 // 添加撤回人到审批流程中
                 const newAuditors = [];
                 newAuditors.push({

+ 24 - 1
app/service/change_plan_audit.js

@@ -291,6 +291,16 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                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 this.ctx.service.specMsg.addChangePlanMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.change, pushOperate.change_plan.flow);
                 await transaction.delete(this.ctx.service.changePlanHistory.tableName, { cpid: cpId });
@@ -411,7 +421,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
-
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 获取推送必要信息
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
                 // 添加推送
@@ -447,6 +457,16 @@ module.exports = app => {
                         c_name: this.ctx.change.name,
                     };
                     await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                    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.addChangePlanMsg(transaction, pid, this.ctx.change, pushOperate.change_plan.flow);
                 } else {
@@ -513,6 +533,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 添加到消息推送表
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
                 const records = [{ pid, type: pushType.changePlan, uid: this.ctx.change.uid, status: auditConst.status.checkNo, content: noticeContent }];
@@ -663,6 +684,7 @@ module.exports = app => {
                     begin_time: null,
                     opinion: null,
                 });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 // 清单审批值删除并重算变更金额
                 await this.ctx.service.changePlanList.delAuditAmount(transaction, change.id);
                 // 变成待上报状态
@@ -731,6 +753,7 @@ module.exports = app => {
                 await transaction.insert(this.tableName, newAuditors);
                 // 当前审批人变成待审批
                 await transaction.update(this.tableName, { id: curAudit.id, order: curAudit.order + 2, begin_time: null, status: auditConst.status.uncheck });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 // 清除上一人的值并调整spamount值
                 const updateList = [];
                 const changeList = await this.ctx.service.changePlanList.getAllDataByCondition({

+ 25 - 0
app/service/change_project_audit.js

@@ -289,6 +289,16 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                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.changeProjectHistory.tableName, { cpid: cpId });
                 // todo 更新标段tender状态 ?
                 //  检查三方特殊推送
@@ -412,6 +422,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
 
                 // 获取推送必要信息
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
@@ -441,6 +452,16 @@ module.exports = app => {
                         c_name: this.ctx.change.name,
                     };
                     await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
+                    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.addChangeProjectMsg(transaction, pid, this.ctx.change, pushOperate.change_project.flow);
                 } else {
@@ -504,6 +525,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 添加到消息推送表
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
                 const records = [{ pid, type: pushType.changeProject, uid: this.ctx.change.uid, status: auditConst.status.back, content: noticeContent }];
@@ -555,6 +577,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
 
                 // 获取推送必要信息
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
@@ -669,6 +692,7 @@ module.exports = app => {
                     begin_time: null,
                     opinion: null,
                 });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 // 变成待上报状态
                 await transaction.update(this.ctx.service.changeProject.tableName, {
                     id: change.id,
@@ -735,6 +759,7 @@ module.exports = app => {
                 await transaction.insert(this.tableName, newAuditors);
                 // 当前审批人变成待审批
                 await transaction.update(this.tableName, { id: curAudit.id, order: curAudit.order + 2, begin_time: null, status: auditConst.status.uncheck });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, curAudit.id);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();

+ 33 - 3
app/service/ledger_audit.js

@@ -287,7 +287,16 @@ module.exports = app => {
                     begin_time: Date.parse(new Date()),
                 };
                 await this.ctx.helper.sendWechat(audit.audit_id, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), wxConst.template.ledger, wechatData);
-
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.TZ, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: tenderId,
+                    uid: audit.audit_id,
+                    sp_type: 'ledger',
+                    sp_id: audit.id,
+                    table_name: this.tableName,
+                    template: wxConst.template.ledger,
+                    wx_data: wechatData,
+                });
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -347,6 +356,7 @@ module.exports = app => {
                 await transaction.insert('zh_notice', records);
                 // 更新当前审核流程
                 await transaction.update(this.tableName, { id: audit.id, status: checkType, opinion, end_time: time });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 const begin_audit = await this.getDataByCondition({
                     tender_id: tenderId,
                     audit_order: 1,
@@ -375,6 +385,16 @@ module.exports = app => {
                             begin_time: Date.parse(begin_audit.begin_time),
                         };
                         await this.ctx.helper.sendWechat(nextAudit.audit_id, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), wxConst.template.ledger, wechatData);
+                        await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.TZ, {
+                            pid: this.ctx.session.sessionProject.id,
+                            tid: tenderId,
+                            uid: nextAudit.audit_id,
+                            sp_type: 'ledger',
+                            sp_id: nextAudit.id,
+                            table_name: this.tableName,
+                            template: wxConst.template.ledger,
+                            wx_data: wechatData,
+                        });
                     } else {
                         // 同步标段信息
                         await transaction.update(this.ctx.service.tender.tableName, {
@@ -475,7 +495,7 @@ module.exports = app => {
             try {
                 // 当前审批人2次添加至流程中
                 await transaction.insert(this.tableName, checkAgainAuditor);
-                await transaction.insert(this.tableName, checkingAuditor);
+                const checking_result = await transaction.insert(this.tableName, checkingAuditor);
                 // 同步标段信息
                 await transaction.update(this.ctx.service.tender.tableName, {
                     id: tenderId,
@@ -491,7 +511,17 @@ module.exports = app => {
                         tips: wxConst.tips.check,
                         begin_time: Date.parse(time),
                     };
-                    await this.ctx.helper.sendWechat(otherAuditIds, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), wxConst.template.ledger, wechatData);
+                    await this.ctx.helper.sendWechat(selfAudit.audit_id, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), wxConst.template.ledger, wechatData);
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.TZ, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: this.ctx.tender.id,
+                        uid: selfAudit.audit_id,
+                        sp_type: 'ledger',
+                        sp_id: checking_result.insertId,
+                        table_name: this.tableName,
+                        template: wxConst.template.ledger,
+                        wx_data: wechatData,
+                    });
                 }
                 await transaction.commit();
             } catch (err) {

+ 55 - 43
app/service/material_audit.js

@@ -275,21 +275,16 @@ module.exports = app => {
                     hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
-
-
-                // 添加短信通知-需要审批提醒功能
-                // const smsUser = await this.ctx.service.projectAccount.getDataById(audit.aid);
-                // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '') {
-                //     const smsType = JSON.parse(smsUser.sms_type);
-                //     if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
-                //         const tenderInfo = await this.ctx.service.tender.getDataById(audit.tid);
-                //         const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
-                //         const sms = new SMS(this.ctx);
-                //         const tenderName = await sms.contentChange(tenderInfo.name);
-                //         const content = '【纵横计量支付】' + tenderName + '第' + stageInfo.order + '期,需要您审批。';
-                //         sms.send(smsUser.auth_mobile, content);
-                //     }
-                // }
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.TC, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.tender.id,
+                    uid: audit.aid,
+                    sp_type: 'material',
+                    sp_id: audit.id,
+                    table_name: this.tableName,
+                    template: wxConst.template.material,
+                    wx_data: wechatData,
+                });
 
                 // todo 更新标段tender状态 ?
                 // 检查三方特殊推送
@@ -323,7 +318,7 @@ module.exports = app => {
                 // 获取当前总金额及独立单价期的金额,添加到tp_data中,报表使用
                 const tp_data = await this.getTpData(transaction, materialId);
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time, tp_data: JSON.stringify(tp_data) });
-
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 获取推送必要信息
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, materialId, audit.aid, checkData.opinion);
                 // 添加推送
@@ -361,22 +356,18 @@ module.exports = app => {
                         hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                     };
                     await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.TC, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: this.ctx.tender.id,
+                        uid: nextAudit.aid,
+                        sp_type: 'material',
+                        sp_id: nextAudit.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.material,
+                        wx_data: wechatData,
+                    });
                     // 检查三方特殊推送
                     await this.ctx.service.specMsg.addMaterialMsg(transaction, pid, materialInfo, pushOperate.material.flow);
-
-                    // 添加短信通知-需要审批提醒功能
-                    // const smsUser = await this.ctx.service.projectAccount.getDataById(nextAudit.aid);
-                    // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '') {
-                    //     const smsType = JSON.parse(smsUser.sms_type);
-                    //     if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
-                    //         const tenderInfo = await this.ctx.service.tender.getDataById(nextAudit.tid);
-                    //         const stageInfo = await this.ctx.service.stage.getDataById(nextAudit.sid);
-                    //         const sms = new SMS(this.ctx);
-                    //         const tenderName = await sms.contentChange(tenderInfo.name);
-                    //         const content = '【纵横计量支付】' + tenderName + '第' + stageInfo.order + '期,需要您审批。';
-                    //         sms.send(smsUser.auth_mobile, content);
-                    //     }
-                    // }
                 } else {
                     // 本期结束
                     // 同步 期信息
@@ -509,6 +500,7 @@ module.exports = app => {
             try {
                 const tp_data = await this.getTpData(transaction, materialId);
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time, tp_data: JSON.stringify(tp_data), });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 添加到消息推送表
                 const noticeContent = await this.getNoticeContent(pid, audit.tid, materialId, audit.aid, checkData.opinion);
                 const records = [{ pid, type: pushType.material, uid: this.ctx.material.user_id, status: auditConst.status.checkNo, content: noticeContent }];
@@ -614,6 +606,7 @@ module.exports = app => {
                 });
                 await transaction.insert('zh_notice', records);
                 await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time, tp_data: JSON.stringify(tp_data) });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 // 顺移气候审核人流程顺序
                 this.initSqlBuilder();
                 this.sqlBuilder.setAndWhere('mid', { value: materialId, operate: '=' });
@@ -621,17 +614,18 @@ module.exports = app => {
                 this.sqlBuilder.setUpdateData('order', { value: 2, selfOperate: '+' });
                 const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
                 const data = await transaction.query(sql, sqlParam);
-                const newAuditors = [];
-                newAuditors.push({
+                const newAuditors = {
                     tid: audit.tid, mid: audit.mid, aid: preAuditor.aid,
                     times: audit.times, order: audit.order + 1, status: auditConst.status.checking,
                     begin_time: time,
                     tp_data: JSON.stringify(tp_data),
-                });
-                newAuditors.push({
+                };
+                const checking_result = await transaction.insert(this.tableName, newAuditors);
+                const uncheckNewAuditors = {
                     tid: audit.tid, mid: audit.mid, aid: audit.aid,
                     times: audit.times, order: audit.order + 2, status: auditConst.status.uncheck,
-                });
+                };
+                await transaction.insert(this.tableName, uncheckNewAuditors);
 
                 // 微信模板通知
                 const begin_audit = await this.getDataByCondition({
@@ -649,10 +643,18 @@ module.exports = app => {
                     hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                 };
                 await this.ctx.helper.sendWechat(preAuditor.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.TC, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.tender.id,
+                    uid: preAuditor.aid,
+                    sp_type: 'material',
+                    sp_id: checking_result.id,
+                    table_name: this.tableName,
+                    template: wxConst.template.material,
+                    wx_data: wechatData,
+                });
                 // 检查三方特殊推送
                 await this.ctx.service.specMsg.addMaterialMsg(transaction, pid, materialInfo, pushOperate.material.flow);
-
-                await transaction.insert(this.tableName, newAuditors);
                 await transaction.commit();
             } catch (error) {
                 await transaction.rollback();
@@ -729,19 +731,19 @@ module.exports = app => {
                 const material_decimal = materialInfo && materialInfo.decimal ? JSON.parse(materialInfo.decimal) : materialConst.decimal;
                 const tp_data = await this.getTpData(transaction, materialId, material_decimal);
                 // 当前审批人2次添加至流程中
-                const newAuditors = [];
-                newAuditors.push({
+                const newAuditors = {
                     tid: audit.tid, mid: audit.mid, aid: audit.aid,
                     times: audit.times, order: audit.order + 1, status: auditConst.status.checkAgain,
                     begin_time: time, end_time: time, opinion: '',
-                });
-                newAuditors.push({
+                };
+                await transaction.insert(this.tableName, newAuditors);
+                const checkingNewAuditors = {
                     tid: audit.tid, mid: audit.mid, aid: audit.aid,
                     times: audit.times, order: audit.order + 2, status: auditConst.status.checking,
                     begin_time: time,
                     tp_data: JSON.stringify(tp_data),
-                });
-                await transaction.insert(this.tableName, newAuditors);
+                };
+                const checking_result = await transaction.insert(this.tableName, checkingNewAuditors);
 
                 // 本期结束
                 // 同步 期信息
@@ -759,6 +761,16 @@ module.exports = app => {
                     hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), material_decimal.tp), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.exponent_rate/100), material_decimal.tp)),
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.TC, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.tender.id,
+                    uid: audit.aid,
+                    sp_type: 'material',
+                    sp_id: checking_result.insertId,
+                    table_name: this.tableName,
+                    template: wxConst.template.material,
+                    wx_data: wechatData,
+                });
                 // 检查三方特殊推送
                 await this.ctx.service.specMsg.addMaterialMsg(transaction, pid, materialInfo, pushOperate.material.flow);
                 await transaction.commit();

+ 69 - 0
app/service/notice_again.js

@@ -0,0 +1,69 @@
+'use strict';
+
+/**
+ * 审批信息重新发送modal
+ *
+ * @author EllisRan
+ * @date 2024/01/18
+ * @version
+ */
+const smsTypeConst = require('../const/sms_type');
+module.exports = app => {
+    class NoticeAgain extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'notice_again';
+        }
+
+        // 入库并判断
+        async addNoticeAgain(transaction, type, datas) {
+            // 判断用户是否已绑定公众号或者企业微信, 并判断用户是否开启了重新发送并且钩上对应的审批
+            const userInfo = await this.ctx.service.projectAccount.getDataById(datas.uid);
+            if (!userInfo.wx_type || userInfo.wx_type === '') return;
+            const wxType = JSON.parse(userInfo.wx_type);
+            // 没开启发送功能不应该生成重新发送入库数据
+            if (wxType[type] && wxType[type].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
+                datas.status = 0;
+                const notice_again = userInfo.notice_again ? JSON.parse(userInfo.notice_again) : null;
+                if ((userInfo.wx_openid || userInfo.qywx_userid) && notice_again && notice_again.checked && notice_again.sp[datas.sp_type]) {
+                    datas.status = 1;
+                }
+                datas.in_time = new Date();
+                datas.last_time = this.ctx.helper.dateTran(new Date(), 'YYYY-MM-DD HH:mm');
+                datas.times = 0;
+                datas.wx_data = JSON.stringify(datas.wx_data);
+                datas.sms_type = type;
+                await transaction.insert(this.tableName, datas);
+            }
+        }
+
+        async stopNoticeAgain(transaction, table_name, sp_id, status = 2) {
+            // 判断sp_id是字符还是数组
+            // if (sp_id instanceof Array) {
+            //     for (const id of sp_id) {
+            //         await transaction.update(this.tableName, { status }, { where: { table_name, sp_id: id } });
+            //     }
+            // } else {
+            await transaction.update(this.tableName, { status }, { where: { table_name, sp_id } });
+            // }
+        }
+        // 针对撤回删除对应的重新发送数据
+        async deleteNoticeAgain(transaction, table_name, sp_id) {
+            // 判断sp_id是字符还是数组
+            await transaction.delete(this.tableName, { table_name, sp_id });
+        }
+
+        async getSpResult(table_name, sp_id) {
+            const sql = 'SELECT * FROM ?? WHERE id = ?';
+            const sqlParam = [table_name, sp_id];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+    }
+    return NoticeAgain;
+};

+ 9 - 0
app/service/project.js

@@ -286,6 +286,15 @@ module.exports = app => {
             });
             return result.affectedRows === 1;
         }
+
+        async updateProjectSet(id, field, datas) {
+            const updateData = {
+                id,
+            };
+            updateData[field] = JSON.stringify(datas);
+            const result = await this.db.update(this.tableName, updateData);
+            return result.affectedRows === 1;
+        }
     }
 
     return Project;

+ 11 - 0
app/service/project_account.js

@@ -16,6 +16,7 @@ const SmsAliConst = require('../const/sms_alitemplate');
 const loginWay = require('../const/setting').loginWay;
 const smsTypeConst = require('../const/sms_type').type;
 const pageShowConst = require('../const/page_show').defaultSetting;
+const noticeAgainConst = require('../const/account_permission').noticeAgain;
 module.exports = app => {
 
     class ProjectAccount extends app.BaseService {
@@ -681,6 +682,16 @@ module.exports = app => {
             } else {
                 updateData.cooperation = 0;
             }
+            const notice_again = {
+                checked: data.again_all !== undefined && data.again_all !== null,
+                sp: {},
+            };
+            delete data.again_all;
+            for (const sp in noticeAgainConst.sp) {
+                notice_again.sp[sp] = data['again_' + sp] !== undefined && data['again_' + sp] !== null;
+                delete data['again_' + sp];
+            }
+            updateData.notice_again = JSON.stringify(notice_again);
             delete data.id;
             updateData.permission = JSON.stringify(data);
 

+ 21 - 0
app/service/revise_audit.js

@@ -274,6 +274,16 @@ module.exports = app => {
                     begin_time: Date.parse(time),
                 };
                 await this.ctx.helper.sendWechat(audit.audit_id, smsTypeConst.const.XD, smsTypeConst.judge.approval.toString(), wxConst.template.revise, wechatData);
+                await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.XD, {
+                    pid: this.ctx.session.sessionProject.id,
+                    tid: this.ctx.tender.id,
+                    uid: audit.audit_id,
+                    sp_type: 'revise',
+                    sp_id: audit.id,
+                    table_name: this.tableName,
+                    template: wxConst.template.revise,
+                    wx_data: wechatData,
+                });
                 // 其他参与人
                 const auditList = await this.getAuditors(revise.id, times);
                 const users = this._.pull(this._.map(auditList, 'user_id'), audit.audit_id);
@@ -386,6 +396,7 @@ module.exports = app => {
                     opinion,
                     end_time: time,
                 });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                 if (checkType === auditConst.status.checked) {
                     const nextAudit = await this.getDataByCondition({
                         rid: revise.id,
@@ -417,6 +428,16 @@ module.exports = app => {
                             begin_time: Date.parse(revise.begin_time),
                         };
                         await this.ctx.helper.sendWechat(nextAudit.audit_id, smsTypeConst.const.XD, smsTypeConst.judge.approval.toString(), wxConst.template.revise, wechatData);
+                        await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.XD, {
+                            pid: this.ctx.session.sessionProject.id,
+                            tid: this.ctx.tender.id,
+                            uid: nextAudit.audit_id,
+                            sp_type: 'revise',
+                            sp_id: nextAudit.id,
+                            table_name: this.tableName,
+                            template: wxConst.template.revise,
+                            wx_data: wechatData,
+                        });
                         // 其他参与人
                         const users = this._.pull(this._.map(auditList, 'audit_id'), audit.audit_id);
                         users.push(revise.uid);

+ 70 - 8
app/service/settle_audit.js

@@ -386,7 +386,18 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.JS, smsTypeConst.judge.approval.toString(), wxConst.template.settle, wechatData);
-
+                for (const a of audits) {
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JS, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: settle.tid,
+                        uid: a.audit_id,
+                        sp_type: 'settle',
+                        sp_id: a.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.settle,
+                        wx_data: wechatData,
+                    });
+                }
                 // todo 上报/审批 - 检查三方特殊推送
                 // await this.ctx.service.specMsg.addSettleMsg(transaction, this.ctx.session.sessionProject.id, settle, pushOperate.settle.flow);
                 await transaction.commit();
@@ -423,6 +434,7 @@ module.exports = app => {
                     audit_status: auditConst.settle.status.checked, opinion: opinion,
                     audit_time: time,
                 });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, selfAudit.id);
                 let auditStatus;
                 if (settle.curAuditors.length === 1 || selfAudit.audit_type !== auditType.key.and) {
                     // 或签更新他人审批状态
@@ -436,6 +448,7 @@ module.exports = app => {
                                 opinion: '',
                                 audit_time: time,
                             });
+                            await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                         }
                         if (updateOther.length > 0) transaction.updateRows(this.tableName, updateOther);
                     }
@@ -466,6 +479,18 @@ module.exports = app => {
                             code: this.ctx.session.sessionProject.code,
                         };
                         await this.ctx.helper.sendWechat(users, smsTypeConst.const.JS, smsTypeConst.judge.approval.toString(), wxConst.template.settle, wechatData);
+                        for (const a of nextAudits) {
+                            await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JS, {
+                                pid: this.ctx.session.sessionProject.id,
+                                tid: this.ctx.tender.id,
+                                uid: a.audit_id,
+                                sp_type: 'settle',
+                                sp_id: a.id,
+                                table_name: this.tableName,
+                                template: wxConst.template.settle,
+                                wx_data: wechatData,
+                            });
+                        }
                     } else {
                         // 添加短信通知-审批通过提醒功能
                         const users = this._.uniq(settle.userIds);
@@ -537,6 +562,7 @@ module.exports = app => {
                     }
                 });
                 await transaction.updateRows(this.tableName, updateData);
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
                 // 更新 期信息
                 await transaction.update(this.ctx.service.settle.tableName, {
                     id: settle.id, audit_status: auditConst.settle.status.checkNo, audit_times: settle.audit_times + 1,
@@ -604,12 +630,13 @@ module.exports = app => {
                     }
                 }
                 await transaction.updateRows(this.tableName, updateData);
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
 
                 // 顺移其后审核人流程顺序
                 const sql = 'UPDATE ' + this.tableName + ' SET `active_order` = `active_order` + 2 WHERE settle_id = ? AND audit_times = ? AND `active_order` > ?';
                 await transaction.query(sql, [settle.id, selfAudit.audit_times, selfAudit.active_order]);
                 // 上一审批人,当前审批人 再次添加至流程
-                const newAuditors = [];
+                const newAuditors = [], uncheckAuditors = [];
                 preAuditors.forEach(x => {
                     newAuditors.push({
                         tid: x.tid, settle_id: x.settle_id, audit_id: x.audit_id,
@@ -619,8 +646,13 @@ module.exports = app => {
                         name: x.name, company: x.company, role: x.role, mobile: x.mobile,
                     });
                 });
+                const checkingAuditors_result = await transaction.insert(this.tableName, newAuditors);
+                // 获取刚批量添加的所有list
+                for (let j = 0; j < newAuditors.length; j++) {
+                    newAuditors[j].id = checkingAuditors_result.insertId + j;
+                }
                 settle.flowAuditors.forEach(x => {
-                    newAuditors.push({
+                    uncheckAuditors.push({
                         tid: x.tid, settle_id: x.settle_id, audit_id: x.audit_id,
                         audit_times: x.audit_times, active_order: selfAudit.active_order + 2,
                         audit_status: auditConst.settle.status.uncheck,
@@ -628,7 +660,7 @@ module.exports = app => {
                         name: x.name, company: x.company, role: x.role, mobile: x.mobile,
                     });
                 });
-                await transaction.insert(this.tableName, newAuditors);
+                await transaction.insert(this.tableName, uncheckAuditors);
 
                 const preAuditorIds = preAuditors.map(x => { return x.audit_id; });
                 // todo 锁定本人数据,保留锁定数据相关确认状态
@@ -654,7 +686,18 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.JS, smsTypeConst.judge.approval.toString(), wxConst.template.settle, wechatData);
-
+                for (const a of newAuditors) {
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JS, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: settle.tid,
+                        uid: a.audit_id,
+                        sp_type: 'settle',
+                        sp_id: a.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.settle,
+                        wx_data: wechatData,
+                    });
+                }
                 // todo 上报/审批 - 检查三方特殊推送
                 // await this.ctx.service.specMsg.addSettleMsg(transaction, pid, settle, pushOperate.settle.flow);
                 await transaction.commit();
@@ -711,7 +754,12 @@ module.exports = app => {
                         name: x.name, company: x.company, role: x.role, mobile: x.mobile,
                     });
                 });
-                await transaction.insert(this.tableName, [...checkAgainAuditors, ...checkingAuditors]);
+                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.settle.tableName, {
@@ -734,7 +782,18 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.JS, smsTypeConst.judge.approval.toString(), wxConst.template.settle, wechatData);
-
+                for (const a of checkingAuditors) {
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JS, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: settle.tid,
+                        uid: a.audit_id,
+                        sp_type: 'settle',
+                        sp_id: a.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.settle,
+                        wx_data: wechatData,
+                    });
+                }
                 // todo 上报/审批 - 检查三方特殊推送
                 // await this.ctx.service.specMsg.addSettleMsg(transaction, this.ctx.session.sessionProject.id, settle, pushOperate.settle.flow);
                 await transaction.commit();
@@ -767,6 +826,7 @@ module.exports = app => {
                     }
                 });
                 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.settle.tableName, {
                     id: settle.id, audit_times: settle.audit_times,
                     audit_status: settle.audit_times === 1 ? auditConst.settle.status.uncheck : auditConst.settle.status.checkNo,
@@ -831,6 +891,7 @@ module.exports = app => {
                 await transaction.updateRows(this.tableName, settle.curAuditors.map(x => { return {
                     id: x.id, audit_time: null, status: auditConst.settle.status.uncheck, active_order: x.active_order + 2
                 }}));
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, this._.map(settle.curAuditors, 'id'));
                 // 同步 期信息
                 await transaction.update(this.ctx.service.settle.tableName, {
                     id: settle.id, audit_times: settle.audit_times,
@@ -865,6 +926,7 @@ module.exports = app => {
                 // 整理当前流程审核人状态更新
                 // 删除当前审批人
                 await transaction.delete(this.tableName, { id: settle.curAuditors.map(x => { return x.id; }) });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, this._.map(settle.curAuditors, 'id'));
                 // 添加撤回人到审批流程中
                 const newAuditors = [];
                 settle.preAuditors.forEach(x => {
@@ -1004,4 +1066,4 @@ module.exports = app => {
     }
 
     return SettleAudit;
-};
+};

+ 74 - 5
app/service/stage_audit.js

@@ -374,6 +374,19 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
+                // 重新发送配置
+                for (const a of audits) {
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JL, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: this.ctx.tender.id,
+                        uid: a.aid,
+                        sp_type: 'stage',
+                        sp_id: a.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.stage,
+                        wx_data: wechatData,
+                    });
+                }
 
                 // 上报/审批 - 检查三方特殊推送
                 await this.ctx.service.specMsg.addStageMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.stage, pushOperate.stage.flow);
@@ -446,6 +459,7 @@ module.exports = app => {
                     opinion: checkData.opinion,
                     end_time: time,
                 });
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, selfAudit.id);
                 // 计算并合同支付最终数据
                 const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
                 const stageTp = {
@@ -469,6 +483,7 @@ module.exports = app => {
                                 opinion: '',
                                 end_time: time,
                             });
+                            await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
                         }
                         if (updateOther.length > 0) transaction.updateRows(this.tableName, updateOther);
                     }
@@ -514,6 +529,19 @@ module.exports = app => {
                             code: this.ctx.session.sessionProject.code,
                         };
                         await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
+                        // 重新发送配置
+                        for (const a of nextAudits) {
+                            await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JL, {
+                                pid: this.ctx.session.sessionProject.id,
+                                tid: this.ctx.tender.id,
+                                uid: a.aid,
+                                sp_type: 'stage',
+                                sp_id: a.id,
+                                table_name: this.tableName,
+                                template: wxConst.template.stage,
+                                wx_data: wechatData,
+                            });
+                        }
                     } else {
                         await this.ctx.service.revisePrice.doPriceUsed(this.ctx.stage, transaction);
                         await this.ctx.service.tenderTag.saveTenderTag(this.ctx.tender.id, {stage_time: new Date()}, transaction);
@@ -649,9 +677,10 @@ module.exports = app => {
                         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'));
                 // 同步 期信息
                 await transaction.update(this.ctx.service.stage.tableName, {
                     id: stageId,
@@ -789,7 +818,7 @@ module.exports = app => {
                     }
                 }
                 await transaction.updateRows(this.tableName, updateData);
-
+                await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
                 // 顺移其后审核人流程顺序
                 const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE sid = ? AND times = ? AND `order` > ?';
                 await transaction.query(sql, [stageId, selfAudit.times, selfAudit.order]);
@@ -803,15 +832,22 @@ module.exports = app => {
                         audit_type: x.audit_type, audit_order: x.audit_order,
                     });
                 });
+                // 获取ids值
+                const newAuditors_result = await transaction.insert(this.tableName, newAuditors);
+                // 获取刚批量添加的所有list
+                for (let j = 0; j < preAuditors.length; j++) {
+                    newAuditors[j].id = newAuditors_result.insertId + j;
+                }
+                const newFlowAuditors = [];
                 flowAudits.forEach(x => {
-                    newAuditors.push({
+                    newFlowAuditors.push({
                         tid: this.ctx.stage.tid, sid: this.ctx.stage.id, aid: x.aid,
                         times: x.times, order: selfAudit.order + 2,
                         status: auditConst.status.uncheck,
                         audit_type: x.audit_type, audit_order: x.audit_order,
                     });
                 });
-                await transaction.insert(this.tableName, newAuditors);
+                await transaction.insert(this.tableName, newFlowAuditors);
                 // 计算该审批人最终数据
                 await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
                 // 复制一份最新数据给下一人
@@ -849,6 +885,19 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
+                // 重新发送配置
+                for (const a of newAuditors) {
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JL, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: this.ctx.tender.id,
+                        uid: a.aid,
+                        sp_type: 'stage',
+                        sp_id: a.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.stage,
+                        wx_data: wechatData,
+                    });
+                }
 
                 // 上报/审批 - 检查三方特殊推送
                 await this.ctx.service.specMsg.addStageMsg(transaction, pid, this.ctx.stage, pushOperate.stage.flow);
@@ -949,7 +998,11 @@ module.exports = app => {
                     });
                 });
                 await transaction.insert(this.tableName, checkAgainAuditors);
-                await transaction.insert(this.tableName, checkingAuditors);
+                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 this.ctx.service.stagePay.clearCacheOrder(this.ctx.stage, transaction);
@@ -1003,6 +1056,19 @@ module.exports = app => {
                     code: this.ctx.session.sessionProject.code,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
+                // 重新发送配置
+                for (const a of checkingAuditors) {
+                    await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JL, {
+                        pid: this.ctx.session.sessionProject.id,
+                        tid: this.ctx.tender.id,
+                        uid: a.aid,
+                        sp_type: 'stage',
+                        sp_id: a.id,
+                        table_name: this.tableName,
+                        template: wxConst.template.stage,
+                        wx_data: wechatData,
+                    });
+                }
 
                 // 上报/审批 - 检查三方特殊推送
                 await this.ctx.service.specMsg.addStageMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.stage, pushOperate.stage.flow);
@@ -1053,6 +1119,7 @@ module.exports = app => {
                     }
                 });
                 await transaction.updateRows(this.tableName, updateData);
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
                 // 计算并合同支付最终数据
                 const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(stage, transaction);
                 const stageTp = {
@@ -1154,6 +1221,7 @@ module.exports = app => {
                 await transaction.updateRows(this.tableName, stage.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(stage.curAuditors, 'id'));
                 // 计算并合同支付最终数据
                 const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
                 const stageTp = {
@@ -1225,6 +1293,7 @@ module.exports = app => {
                 // 整理当前流程审核人状态更新
                 // 删除当前审批人
                 await transaction.delete(this.tableName, { id: stage.curAuditors.map(x => { return x.id; }) });
+                await this.ctx.service.noticeAgain.deleteNoticeAgain(transaction, this.tableName, this._.map(stage.curAuditors, 'id'));
                 // 添加撤回人到审批流程中
                 const newAuditors = [];
                 stage.preAuditors.forEach(x => {

+ 1 - 0
app/view/setting/user_permission.ejs

@@ -92,6 +92,7 @@
 </div>
 <script>
     const allPermission = JSON.parse('<%- permissionStr %>');
+    const noticeAgainConst = JSON.parse(unescape('<%- escape(JSON.stringify(noticeAgainConst)) %>'));
     const unitList = JSON.parse(unescape('<%- escape(JSON.stringify(unitList)) %>'));
 </script>
 <script src="/public/js/bootstrap/drawer.js"></script>

+ 265 - 0
app/view/setting/user_permission_modal.ejs

@@ -125,6 +125,26 @@
                     <% } %>
                 </div>
             <% } %>
+                <div class="form-group permission-div">
+                    <h6 class="modal-title permission-title">微信通知<a href="#wx-setting" data-toggle="modal" data-target="#wx-setting" class="pull-right"><i style="font-size: 16px" class="fa fa-cog"></i></a></h6>
+                    <div class="form-check form-check-inline">
+                        <span style="margin:0 .5rem 0 1rem; font-weight: bold;">重新发送</span>
+                        <div class="custom-control custom-switch">
+                            <input type="checkbox" class="custom-control-input" id="again_all" name="again_all" value="1">
+                            <label class="custom-control-label" for="again_all"></label>
+                        </div>
+                    </div>
+                    <div style="margin: .5rem 0 0 2rem">
+                        <div class="row">
+                            <% for (const sp in noticeAgainConst.sp) { %>
+                            <div class="col-2 form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="again_<%- sp %>" name="again_<%- sp %>" value="1">
+                                <label class="form-check-label" for="again_<%- sp %>"><%- noticeAgainConst.sp[sp].name %></label>
+                            </div>
+                            <% } %>
+                        </div>
+                    </div>
+                </div>
         </div>
         <div class="modal-footer">
             <input type="hidden" name="id" value="">
@@ -133,3 +153,248 @@
         </div>
     </form>
 </section>
+<div class="modal fade" data-backdrop="static" id="wx-setting">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">微信通知设置(所有人共用)</h5>
+            </div>
+            <div class="modal-body">
+                <div style="margin: 0 0 .5rem .5rem">消息重新发送间隔</div>
+                <div style="margin-left: 1rem;margin-bottom: .5rem">
+                    <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" id="wx_send_type_1" name="wx_send_type" value="fixed" <% if (noticeSet.mode === 'fixed') { %>checked<% } %>>
+                        <label class="form-check-label" for="wx_send_type_1">固定间隔</label>
+                    </div>
+                    <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" id="wx_send_type_2" name="wx_send_type" value="activity" <% if (noticeSet.mode === 'activity') { %>checked<% } %>>
+                        <label class="form-check-label" for="wx_send_type_2">活动间隔</label>
+                    </div>
+                </div>
+                <div style="margin-left: 1.5rem;margin-top: .25rem; <% if (noticeSet.mode === 'activity') { %>display: none;<% } %>" id="wx_type_1">
+                    <div class="w-100 d-inline-flex"><label class="right-duiqi">每间隔:</label>
+                        <input id="fixed_time" style="width: 50px;" class="time-input form-control form-control-sm" type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="24" value="<%- noticeSet.fixed %>">
+                        <div class="dropdown-menu">
+                            <% for (let i = 1; i <= 24;i++) { %>
+                            <a class="dropdown-item change_time" href="javascript:void(0);" data-value="<%- i %>"><%- i %></a>
+                            <% } %>
+                        </div><label style="line-height: 25px;">&nbsp; 小时</label>
+                    </div>
+                </div>
+                <div style="margin-left: 1.5rem;margin-top: .25rem; <% if (noticeSet.mode === 'fixed') { %>display: none;<% } %>" id="wx_type_2">
+                    <div class="w-100 d-inline-flex"><label class="right-duiqi">第1次间隔:</label>
+                        <input id="activity_first_time" style="width: 50px;" class="time-input form-control form-control-sm" type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="24" value="<%- noticeSet.activity.first %>">
+                        <div class="dropdown-menu">
+                            <% for (let i = 1; i <= 24;i++) { %>
+                                <a class="dropdown-item change_time" href="javascript:void(0);" data-value="<%- i %>"><%- i %></a>
+                            <% } %>
+                        </div><label style="line-height: 25px;">&nbsp; 小时</label>
+                    </div>
+                    <div class="w-100 d-inline-flex"><label class="right-duiqi">第2次间隔:</label>
+                        <input id="activity_second_time" style="width: 50px;" class="time-input form-control form-control-sm" type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="24" value="<%- noticeSet.activity.second %>">
+                        <div class="dropdown-menu">
+                            <% for (let i = 1; i <= 24;i++) { %>
+                                <a class="dropdown-item change_time" href="javascript:void(0);" data-value="<%- i %>"><%- i %></a>
+                            <% } %>
+                        </div><label style="line-height: 25px;">&nbsp; 小时</label>
+                    </div>
+                    <div class="w-100 d-inline-flex"><label class="right-duiqi">往后间隔:</label>
+                        <input id="activity_later_time" style="width: 50px;" class="time-input form-control form-control-sm" type="number" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" min="0" step="1" max="24" value="<%- noticeSet.activity.later %>">
+                        <div class="dropdown-menu">
+                            <% for (let i = 1; i <= 24;i++) { %>
+                                <a class="dropdown-item change_time" href="javascript:void(0);" data-value="<%- i %>"><%- i %></a>
+                            <% } %>
+                        </div><label style="line-height: 25px;">&nbsp; 小时</label>
+                    </div>
+                </div>
+                <div style="margin: .5rem 0 .5rem .5rem">消息屏蔽时段</div>
+                <div style="margin-left: 1rem;margin-bottom: .5rem">
+                    <div class="input-group input-group-sm mb-2">
+                        <div class="input-group-prepend">
+                            <span class="input-group-text">开始时间:</span>
+                        </div>
+                        <select class="form-control form-control-sm" id="shield_start_time" style="width: 90px!important;flex: none;">
+                            <option value="">不限制</option>
+                            <% for (let i = 0; i <= 23;i++) { %>
+                                <option value="<%- i %>" <% if (noticeSet.shield_times.start === i) { %>selected<% } %>><%- (i < 10 ? '0' + i : i) + ':00' %></option>
+                            <% } %>
+                        </select>
+                        <span class="text-danger mx-2"></span>
+                    </div>
+                    <div class="input-group input-group-sm mb-2">
+                        <div class="input-group-prepend">
+                            <span class="input-group-text">截止时间:</span>
+                        </div>
+                        <select class="form-control form-control-sm" id="shield_end_time" style="width: 90px!important;flex: none;">
+                            <option value="">不限制</option>
+                            <% for (let i = 0; i <= 23;i++) { %>
+                                <option value="<%- i %>" <% if (noticeSet.shield_times.end === i) { %>selected<% } %>><%- (i < 10 ? '0' + i : i) + ':00' %></option>
+                            <% } %>
+                        </select>
+                        <span class="text-danger mx-2"></span>
+                    </div>
+                </div>
+<!--                <div style="margin-left: 1rem;margin-bottom: .5rem">开始时间:<input style="width: 130px" type="time" placeholder="00:00~07:00" /></div>-->
+<!--                <div style="margin-left: 1rem;margin-bottom: .5rem">截止时间:<input style="width: 130px" type="time" placeholder="00:00~07:00" /></div>-->
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="set_wx_button">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    $(function (){
+       $('input[name="wx_send_type"]').on('click', function () {
+           const type = $(this).val();
+           if (type === 'fixed') {
+               $('#wx_type_1').show();
+               $('#wx_type_2').hide();
+           } else {
+               $('#wx_type_1').hide();
+               $('#wx_type_2').show();
+           }
+       });
+        $('.change_time').on('click', function () {
+            $(this).parents('.dropdown-menu').siblings('input').val(parseInt($(this).data('value')));
+            $(this).parents('.dropdown-menu').hide();
+        });
+        $('.time-input').on('click', function () {
+            $(this).siblings('.dropdown-menu').show();
+        });
+        // 回车提交
+        $('.time-input').on('keypress', function () {
+            if(window.event.keyCode === 13) {
+                $(this).blur();
+            }
+        });
+        $('.time-input').blur(function () {
+            const _self = $(this);
+            setTimeout(function () {
+                _self.siblings('.dropdown-menu').hide();
+                let rate = parseFloat(_self.val());
+                if (_.isNaN(rate)) {
+                    toastr.error('请输入1-24之前的整数值');
+                    _self.val(6);
+                    return;
+                }
+                rate = _.round(rate);
+                if(rate < 1 || rate > 24) {
+                    toastr.error('请输入1-24之前的整数值');
+                    _self.val(6);
+                    return;
+                }
+            }, 500);
+        });
+        $('#set_wx_button').click(function () {
+            let flag = false;
+            // 判断屏蔽时段是否合理
+            const start_time = $('#shield_start_time').val() !== '' ? parseInt($('#shield_start_time').val()) : null;
+            const end_time = $('#shield_end_time').val() !== '' ? parseInt($('#shield_end_time').val()) : null;
+            if ((start_time === null && end_time !== null) || (start_time !== null && end_time === null)) {
+                toastr.error('屏蔽时段必须配置开始和截止时间,要么都为不限制');
+                flag = true;
+            } else if (start_time !== null && end_time !== null && start_time === end_time) {
+                toastr.error('屏蔽时间段开始和截止时间不能相同');
+                flag = true;
+            }
+            if (flag) {
+                return false;
+            }
+            const pData = {
+                mode: $('#wx-setting input[name="wx_send_type"]:checked').val(),
+                fixed: parseInt($('#fixed_time').val()),
+                activity: {
+                    first: parseInt($('#activity_first_time').val()),
+                    second: parseInt($('#activity_second_time').val()),
+                    later: parseInt($('#activity_later_time').val()),
+                },
+                shield_times: {
+                    start: start_time,
+                    end: end_time,
+                }
+            };
+            console.log(pData);
+            postData('/setting/update/projectset', { field: 'notice_setting', pData }, function (result) {
+                toastr.success('设置成功');
+                $('#wx-setting').modal('hide');
+            });
+        });
+
+        function calculateNextSendTime(startTime, intervalHours = 6, quietStart = "00:00", quietEnd = "08:00") {
+            function parseTime(timeStr, day = 0) {
+                const [hours, minutes] = timeStr.split(":").map(Number);
+                const date = new Date();
+                date.setDate(date.getDate() + day);
+                date.setHours(hours, minutes, 0, 0); // Set hours and minutes, reset seconds and milliseconds
+                return date;
+            }
+
+            // let startDateTime = parseTime(startTime);
+            let startDateTime = startTime;
+            let endDateTime = new Date(startDateTime.getTime() + intervalHours * 60 * 60 * 1000);
+            if (quietStart && quietEnd) {
+                let quietStartDateTime = parseTime(quietStart);
+                let quietEndDateTime = parseTime(quietEnd);
+                if (quietEndDateTime <= quietStartDateTime) {
+                    quietEndDateTime.setDate(quietEndDateTime.getDate() + 1);
+                }
+                // 判断非屏蔽时间内是否大于间隔时间,大于则要再隔天发送
+                const hours = calculateHoursBetween(quietStart, quietEnd);
+                if (hours < intervalHours) {
+                    // 除法算出差的天数
+                    const days = Math.floor(intervalHours / hours);
+                    quietStartDateTime = parseTime(quietStart, days);
+                    quietEndDateTime = parseTime(quietEnd, days);
+                    intervalHours %= hours;
+                    startDateTime.setDate(startDateTime.getDate() + days);
+                } else if (startDateTime >= quietEndDateTime) {
+                    quietStartDateTime = parseTime(quietStart, 1);
+                }
+
+                if (quietEndDateTime <= quietStartDateTime) {
+                    quietEndDateTime.setDate(quietEndDateTime.getDate() + 1);
+                }
+                if (startDateTime >= quietStartDateTime) {
+                    startDateTime = quietEndDateTime;
+                }
+                endDateTime = new Date(startDateTime.getTime() + intervalHours * 60 * 60 * 1000);
+                if (endDateTime >= quietStartDateTime && endDateTime < quietEndDateTime) {
+                    let timeIntoQuiet = (quietStartDateTime - startDateTime) / (60 * 60 * 1000);
+                    let remainingTime = intervalHours - timeIntoQuiet;
+                    endDateTime = new Date(quietEndDateTime.getTime() + remainingTime * 60 * 60 * 1000);
+                }
+            }
+
+            // return endDateTime.getHours().toString().padStart(2, '0') + ':' +
+            //     endDateTime.getMinutes().toString().padStart(2, '0');
+            return endDateTime;
+        }
+
+        function calculateHoursBetween(start, end) {
+            // 将时间字符串转换为 Date 对象
+            const startTime = new Date(`2000-01-01T${start}`);
+            const endTime = new Date(`2000-01-01T${end}`);
+
+            // 检查是否跨越了当前日期和隔天日期
+            if (endTime < startTime) {
+                endTime.setDate(endTime.getDate() + 1); // 将结束时间调整到隔天
+            }
+
+            // 计算两个时间之间的毫秒数差距
+            const timeDifference = endTime - startTime;
+
+            // 将毫秒数转换为小时并返回
+            const hours = timeDifference / (1000 * 60 * 60);
+
+            return 24 - hours;
+        }
+
+        let firstSendTime = new Date("2024-1-12 19:23");
+        console.log(firstSendTime);
+        let secondSendTime = calculateNextSendTime(firstSendTime);
+
+        console.log("Second send time:", secondSendTime);
+    });
+</script>

+ 25 - 1
sql/update.sql

@@ -11,6 +11,30 @@ CREATE TABLE `zh_change_settle_list`  (
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '变更已结算清单表(用于修订和重审时生成,防止已结算变更清单被删除)';
 
-ALTER TABLE `zh_rpt_archive_encryption` 
+CREATE TABLE `zh_notice_again`  (
+  `id` int NOT NULL AUTO_INCREMENT,
+  `pid` int NOT NULL COMMENT '项目id',
+  `tid` int NOT NULL COMMENT '标段id',
+  `uid` int NOT NULL COMMENT '用户id',
+  `sp_type` varchar(30) NOT NULL COMMENT '审批类型',
+  `sp_id` int NOT NULL COMMENT '审批类型对应审核表的id',
+  `status` tinyint(2) NOT NULL DEFAULT 1 COMMENT '0为待执行,1为执行中,2为已审',
+  `table_name` varchar(255) NOT NULL COMMENT '对应的审批表名称',
+  `sms_type` varchar(10) NOT NULL COMMENT '模版通知类型',
+  `template` tinyint(2) NOT NULL COMMENT '对应的微信通知模板id',
+  `wx_data` json NOT NULL COMMENT '微信通知的json',
+  `times` tinyint(3) NOT NULL DEFAULT 0 COMMENT '重复发送次数',
+  `in_time` datetime NULL COMMENT '添加时间',
+  `last_time` datetime NULL DEFAULT NULL COMMENT '上一次发送时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '审批信息重新发送表';
+
+ALTER TABLE `zh_project_account`
+ADD COLUMN `notice_again` json NULL DEFAULT NULL COMMENT '审批信息重新发送设置';
+
+ALTER TABLE `zh_project`
+ADD COLUMN `notice_setting` json NULL DEFAULT NULL COMMENT '重新发送通知设置';
+
+ALTER TABLE `zh_rpt_archive_encryption`
 ADD COLUMN `business_id` VARCHAR(45) NULL COMMENT '有其他业务情况,统一在这里标识(类uuid),要结合stage_id来看,如stage_id > 0,则是标段和期,< 0则是其他业务,如 -300 -> 变更令;-301 - 变更方案;-302 - 变更立项 -303 - 变更申请' AFTER `stage_id`,
 ADD COLUMN `tender_id` INT(11) NULL AFTER `business_id`;