stage_audit.js 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date 2019/2/27
  7. * @version
  8. */
  9. const auditConst = require('../const/audit').stage;
  10. const smsTypeConst = require('../const/sms_type');
  11. const SMS = require('../lib/sms');
  12. const SmsAliConst = require('../const/sms_alitemplate');
  13. const wxConst = require('../const/wechat_template');
  14. const payConst = require('../const/deal_pay');
  15. const pushType = require('../const/audit').pushType;
  16. module.exports = app => {
  17. class StageAudit extends app.BaseService {
  18. /**
  19. * 构造函数
  20. *
  21. * @param {Object} ctx - egg全局变量
  22. * @return {void}
  23. */
  24. constructor(ctx) {
  25. super(ctx);
  26. this.tableName = 'stage_audit';
  27. }
  28. /**
  29. * 获取 审核人信息
  30. *
  31. * @param {Number} stageId - 期id
  32. * @param {Number} auditorId - 审核人id
  33. * @param {Number} times - 第几次审批
  34. * @return {Promise<*>}
  35. */
  36. async getAuditor(stageId, auditorId, times = 1) {
  37. const sql =
  38. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
  39. 'FROM ?? AS la, ?? AS pa ' +
  40. 'WHERE la.`sid` = ? and la.`aid` = ? and la.`times` = ?' +
  41. ' and la.`aid` = pa.`id`';
  42. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, auditorId, times];
  43. return await this.db.queryOne(sql, sqlParam);
  44. }
  45. /**
  46. * 获取 审核列表信息
  47. *
  48. * @param {Number} stageId - 期id
  49. * @param {Number} times - 第几次审批
  50. * @param {Number} order_sort - 列表排序方式
  51. * @return {Promise<*>}
  52. */
  53. async getAuditors(stageId, times = 1, order_sort = 'asc') {
  54. const sql =
  55. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' +
  56. 'FROM ?? AS la, ?? AS pa, (SELECT `aid`,(@i:=@i+1) as `sort` FROM ??, (select @i:=0) as it WHERE `sid` = ? AND `times` = ? GROUP BY `aid`) as g ' +
  57. 'WHERE la.`sid` = ? and la.`times` = ? and la.`aid` = pa.`id` and g.`aid` = la.`aid` order by la.`order` ' +
  58. order_sort;
  59. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.tableName, stageId, times, stageId, times];
  60. const result = await this.db.query(sql, sqlParam);
  61. const sql2 = 'SELECT COUNT(a.`aid`) as num FROM (SELECT `aid` FROM ?? WHERE `sid` = ? AND `times` = ? GROUP BY `aid`) as a';
  62. const sqlParam2 = [this.tableName, stageId, times];
  63. const count = await this.db.queryOne(sql2, sqlParam2);
  64. for (const i in result) {
  65. result[i].max_sort = count.num;
  66. }
  67. return result;
  68. }
  69. async getAllAuditors(tenderId) {
  70. const sql =
  71. 'SELECT sa.aid, sa.tid FROM ' +
  72. this.tableName +
  73. ' sa' +
  74. ' LEFT JOIN ' +
  75. this.ctx.service.tender.tableName +
  76. ' t On sa.tid = t.id' +
  77. ' WHERE t.id = ?' +
  78. ' GROUP BY sa.aid';
  79. const sqlParam = [tenderId];
  80. return this.db.query(sql, sqlParam);
  81. }
  82. /**
  83. * 获取标段审核人最后一位的名称
  84. *
  85. * @param {Number} tenderId - 标段id
  86. * @param {Number} auditorId - 审核人id
  87. * @param {Number} times - 第几次审批
  88. * @return {Promise<*>}
  89. */
  90. async getStatusName(stageId) {
  91. const sql =
  92. 'SELECT pa.`name` ' +
  93. 'FROM ?? AS sa, ?? AS pa ' +
  94. 'WHERE sa.`sid` = ?' +
  95. ' and sa.`aid` = pa.`id` and sa.`status` != ? ORDER BY sa.`times` DESC, sa.`order` DESC';
  96. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, auditConst.status.uncheck];
  97. return await this.db.queryOne(sql, sqlParam);
  98. }
  99. /**
  100. * 获取 当前审核人
  101. *
  102. * @param {Number} stageId - 期id
  103. * @param {Number} times - 第几次审批
  104. * @return {Promise<*>}
  105. */
  106. async getCurAuditor(stageId, times = 1) {
  107. const sql =
  108. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
  109. 'FROM ?? AS la, ?? AS pa ' +
  110. 'WHERE la.`sid` = ? and la.`status` = ? and la.`times` = ?' +
  111. ' and la.`aid` = pa.`id`';
  112. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, auditConst.status.checking, times];
  113. return await this.db.queryOne(sql, sqlParam);
  114. }
  115. /**
  116. * 获取 最新审核顺序
  117. *
  118. * @param {Number} stageId - 期id
  119. * @param {Number} times - 第几次审批
  120. * @return {Promise<number>}
  121. */
  122. async getNewOrder(stageId, times = 1) {
  123. const sql = 'SELECT Max(??) As max_order FROM ?? Where `sid` = ? and `times` = ?';
  124. const sqlParam = ['order', this.tableName, stageId, times];
  125. const result = await this.db.queryOne(sql, sqlParam);
  126. return result && result.max_order ? result.max_order + 1 : 1;
  127. }
  128. /**
  129. * 新增审核人
  130. *
  131. * @param {Number} stageId - 期id
  132. * @param {Number} auditorId - 审核人id
  133. * @param {Number} times - 第几次审批
  134. * @return {Promise<number>}
  135. */
  136. async addAuditor(stageId, auditorId, times = 1) {
  137. const newOrder = await this.getNewOrder(stageId, times);
  138. const data = {
  139. tid: this.ctx.tender.id,
  140. sid: stageId,
  141. aid: auditorId,
  142. times,
  143. order: newOrder,
  144. status: auditConst.status.uncheck,
  145. };
  146. const result = await this.db.insert(this.tableName, data);
  147. return (result.effectRows = 1);
  148. }
  149. /**
  150. * 移除审核人时,同步其后审核人order
  151. * @param transaction - 事务
  152. * @param {Number} stageId - 标段id
  153. * @param {Number} auditorId - 审核人id
  154. * @param {Number} times - 第几次审批
  155. * @return {Promise<*>}
  156. * @private
  157. */
  158. async _syncOrderByDelete(transaction, stageId, order, times) {
  159. this.initSqlBuilder();
  160. this.sqlBuilder.setAndWhere('sid', {
  161. value: stageId,
  162. operate: '=',
  163. });
  164. this.sqlBuilder.setAndWhere('order', {
  165. value: order,
  166. operate: '>=',
  167. });
  168. this.sqlBuilder.setAndWhere('times', {
  169. value: times,
  170. operate: '=',
  171. });
  172. this.sqlBuilder.setUpdateData('order', {
  173. value: 1,
  174. selfOperate: '-',
  175. });
  176. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  177. const data = await transaction.query(sql, sqlParam);
  178. return data;
  179. }
  180. /**
  181. * 移除审核人
  182. *
  183. * @param {Number} stageId - 期id
  184. * @param {Number} auditorId - 审核人id
  185. * @param {Number} times - 第几次审批
  186. * @return {Promise<boolean>}
  187. */
  188. async deleteAuditor(stageId, auditorId, times = 1) {
  189. const transaction = await this.db.beginTransaction();
  190. try {
  191. const condition = { sid: stageId, aid: auditorId, times };
  192. const auditor = await this.getDataByCondition(condition);
  193. if (!auditor) {
  194. throw '该审核人不存在';
  195. }
  196. await this._syncOrderByDelete(transaction, stageId, auditor.order, times);
  197. await transaction.delete(this.tableName, condition);
  198. await transaction.commit();
  199. } catch (err) {
  200. await transaction.rollback();
  201. throw err;
  202. }
  203. return true;
  204. }
  205. /**
  206. * 开始审批
  207. *
  208. * @param {Number} stageId - 期id
  209. * @param {Number} times - 第几次审批
  210. * @return {Promise<boolean>}
  211. */
  212. async start(stageId, times = 1) {
  213. const audit = await this.getDataByCondition({ sid: stageId, times, order: 1 });
  214. if (!audit) {
  215. throw '请先选择审批人,再上报数据';
  216. }
  217. const transaction = await this.db.beginTransaction();
  218. try {
  219. await transaction.update(this.tableName, {
  220. id: audit.id,
  221. status: auditConst.status.checking,
  222. begin_time: new Date(),
  223. });
  224. // 计算原报最终数据
  225. const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  226. // 复制一份下一审核人数据
  227. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, 1, transaction);
  228. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  229. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  230. await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
  231. // 更新期数据
  232. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  233. this.ctx.stage.tp_history.push({
  234. times: this.ctx.stage.curTimes,
  235. order: 0,
  236. contract_tp: tpData.contract_tp,
  237. qc_tp: tpData.qc_tp,
  238. yf_tp: yfPay.tp,
  239. sf_tp: sfPay.tp,
  240. });
  241. await transaction.update(this.ctx.service.stage.tableName, {
  242. id: stageId,
  243. status: auditConst.status.checking,
  244. contract_tp: tpData.contract_tp,
  245. qc_tp: tpData.qc_tp,
  246. yf_tp: yfPay.tp,
  247. sf_tp: sfPay.tp,
  248. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  249. cache_time_r: this.ctx.stage.cache_time_l,
  250. });
  251. // 添加短信通知-需要审批提醒功能
  252. // const smsUser = await this.ctx.service.projectAccount.getDataById(audit.aid);
  253. // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  254. // const smsType = JSON.parse(smsUser.sms_type);
  255. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
  256. // const tenderInfo = await this.ctx.service.tender.getDataById(audit.tid);
  257. // const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  258. // const sms = new SMS(this.ctx);
  259. // const tenderName = await sms.contentChange(tenderInfo.name);
  260. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  261. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  262. // const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  263. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
  264. // sms.send(smsUser.auth_mobile, content);
  265. // }
  266. // }
  267. const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  268. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  269. await this.ctx.helper.sendAliSms(audit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  270. qi: stageInfo.order,
  271. code: shenpiUrl,
  272. });
  273. // 微信模板通知
  274. const wechatData = {
  275. wap_url: shenpiUrl,
  276. qi: stageInfo.order,
  277. status: wxConst.status.check,
  278. tips: wxConst.tips.check,
  279. code: this.ctx.session.sessionProject.code,
  280. };
  281. await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  282. // todo 更新标段tender状态 ?
  283. await transaction.commit();
  284. } catch (err) {
  285. await transaction.rollback();
  286. throw err;
  287. }
  288. return true;
  289. }
  290. async _checked(pid, stageId, checkData, times) {
  291. const time = new Date();
  292. // 整理当前流程审核人状态更新
  293. const audit = await this.getDataByCondition({ sid: stageId, times, status: auditConst.status.checking });
  294. if (!audit) {
  295. throw '审核数据错误';
  296. }
  297. const nextAudit = await this.getDataByCondition({ sid: stageId, times, order: audit.order + 1 });
  298. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  299. const transaction = await this.db.beginTransaction();
  300. try {
  301. // 添加推送
  302. const noticeContent = await this.getNoticeContent(pid, audit.tid, stageId, audit.aid);
  303. const auditors = await this.getAuditGroupByListWithOwner(stageId, times);
  304. const records = [];
  305. auditors.forEach(audit => {
  306. records.push({
  307. pid,
  308. type: pushType.stage,
  309. uid: audit.aid,
  310. status: auditConst.status.checked,
  311. content: noticeContent,
  312. });
  313. });
  314. await transaction.insert('zh_notice', records);
  315. await transaction.update(this.tableName, {
  316. id: audit.id,
  317. status: checkData.checkType,
  318. opinion: checkData.opinion,
  319. end_time: time,
  320. });
  321. // 计算并合同支付最终数据
  322. const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  323. this.ctx.stage.tp_history.push({
  324. times,
  325. order: audit.order,
  326. contract_tp: tpData.contract_tp,
  327. qc_tp: tpData.qc_tp,
  328. yf_tp: yfPay.tp,
  329. sf_tp: sfPay.tp,
  330. });
  331. // 无下一审核人表示,审核结束
  332. if (nextAudit) {
  333. // 复制一份下一审核人数据
  334. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, nextAudit.order, transaction);
  335. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  336. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  337. await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
  338. // 流程至下一审批人
  339. await transaction.update(this.tableName, {
  340. id: nextAudit.id,
  341. status: auditConst.status.checking,
  342. begin_time: time,
  343. });
  344. // 同步 期信息
  345. await transaction.update(this.ctx.service.stage.tableName, {
  346. id: stageId,
  347. status: auditConst.status.checking,
  348. contract_tp: tpData.contract_tp,
  349. qc_tp: tpData.qc_tp,
  350. yf_tp: yfPay.tp,
  351. sf_tp: sfPay.tp,
  352. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  353. cache_time_r: this.ctx.stage.cache_time_l,
  354. });
  355. // 添加短信通知-需要审批提醒功能
  356. // const smsUser = await this.ctx.service.projectAccount.getDataById(nextAudit.aid);
  357. // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  358. // const smsType = JSON.parse(smsUser.sms_type);
  359. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
  360. // const tenderInfo = await this.ctx.service.tender.getDataById(nextAudit.tid);
  361. // const stageInfo = await this.ctx.service.stage.getDataById(nextAudit.sid);
  362. // const sms = new SMS(this.ctx);
  363. // const tenderName = await sms.contentChange(tenderInfo.name);
  364. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  365. // const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  366. // // const result = '';
  367. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  368. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
  369. // sms.send(smsUser.auth_mobile, content);
  370. // }
  371. // }
  372. const stageInfo = await this.ctx.service.stage.getDataById(nextAudit.sid);
  373. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  374. await this.ctx.helper.sendAliSms(nextAudit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  375. qi: stageInfo.order,
  376. code: shenpiUrl,
  377. });
  378. // 微信模板通知
  379. const wechatData = {
  380. wap_url: shenpiUrl,
  381. qi: stageInfo.order,
  382. status: wxConst.status.check,
  383. tips: wxConst.tips.check,
  384. code: this.ctx.session.sessionProject.code,
  385. };
  386. await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  387. } else {
  388. // 本期结束
  389. // 生成截止本期数据 final数据
  390. await this.ctx.service.stageBillsFinal.generateFinalData(transaction, this.ctx.tender, this.ctx.stage);
  391. await this.ctx.service.stagePosFinal.generateFinalData(transaction, this.ctx.tender, this.ctx.stage);
  392. // 同步 期信息
  393. await transaction.update(this.ctx.service.stage.tableName, {
  394. id: stageId,
  395. status: checkData.checkType,
  396. contract_tp: tpData.contract_tp,
  397. qc_tp: tpData.qc_tp,
  398. yf_tp: yfPay.tp,
  399. sf_tp: sfPay.tp,
  400. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  401. cache_time_r: this.ctx.stage.cache_time_l,
  402. });
  403. // 添加短信通知-审批通过提醒功能
  404. // const mobile_array = [];
  405. const stageInfo = await this.ctx.service.stage.getDataById(stageId);
  406. const auditList = await this.getAuditors(stageId, stageInfo.times);
  407. // const smsUser1 = await this.ctx.service.projectAccount.getDataById(stageInfo.user_id);
  408. // if (smsUser1.auth_mobile !== undefined && smsUser1.sms_type !== '' && smsUser1.sms_type !== null) {
  409. // const smsType = JSON.parse(smsUser1.sms_type);
  410. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.result.toString()) !== -1) {
  411. // mobile_array.push(smsUser1.auth_mobile);
  412. // }
  413. // }
  414. // for (const user of auditList) {
  415. // const smsUser = await this.ctx.service.projectAccount.getDataById(user.aid);
  416. // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  417. // const smsType = JSON.parse(smsUser.sms_type);
  418. // if (mobile_array.indexOf(smsUser.auth_mobile) === -1 && smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.result.toString()) !== -1) {
  419. // mobile_array.push(smsUser.auth_mobile);
  420. // }
  421. // }
  422. // }
  423. // if (mobile_array.length > 0) {
  424. // const tenderInfo = await this.ctx.service.tender.getDataById(stageInfo.tid);
  425. // const sms = new SMS(this.ctx);
  426. // const tenderName = await sms.contentChange(tenderInfo.name);
  427. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  428. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  429. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,审批通过。';
  430. // sms.send(mobile_array, content);
  431. // }
  432. const users = this._.uniq(this._.concat(this._.map(auditList, 'aid'), stageInfo.user_id));
  433. await this.ctx.helper.sendAliSms(users, smsTypeConst.const.JL, smsTypeConst.judge.result.toString(), SmsAliConst.template.stage_result, {
  434. qi: stageInfo.order,
  435. status: SmsAliConst.status.success,
  436. });
  437. // 微信模板通知
  438. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  439. const wechatData = {
  440. wap_url: shenpiUrl,
  441. qi: stageInfo.order,
  442. status: wxConst.status.success,
  443. tips: wxConst.tips.success,
  444. code: this.ctx.session.sessionProject.code,
  445. };
  446. await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.result.toString(), wxConst.template.stage, wechatData);
  447. }
  448. await transaction.commit();
  449. } catch (err) {
  450. await transaction.rollback();
  451. throw err;
  452. }
  453. }
  454. async _checkNo(pid, stageId, checkData, times) {
  455. console.log('checkData', checkData);
  456. const time = new Date();
  457. // 整理当前流程审核人状态更新
  458. const audit = await this.getDataByCondition({ sid: stageId, times, status: auditConst.status.checking });
  459. if (!audit) {
  460. throw '审核数据错误';
  461. }
  462. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  463. const sql = 'SELECT `tid`, `sid`, `aid`, `order` FROM ?? WHERE `sid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  464. const sqlParam = [this.tableName, stageId, times];
  465. const auditors = await this.db.query(sql, sqlParam);
  466. let order = 1;
  467. for (const a of auditors) {
  468. a.times = times + 1;
  469. a.order = order;
  470. a.status = auditConst.status.uncheck;
  471. order++;
  472. }
  473. const transaction = await this.db.beginTransaction();
  474. try {
  475. // 添加推送
  476. const noticeContent = await this.getNoticeContent(pid, audit.tid, stageId, audit.aid);
  477. const records = [
  478. {
  479. pid,
  480. type: pushType.stage,
  481. uid: this.ctx.stage.user_id,
  482. status: auditConst.status.checkNo,
  483. content: noticeContent,
  484. },
  485. ];
  486. auditors.forEach(audit => {
  487. records.push({
  488. pid,
  489. type: pushType.stage,
  490. uid: audit.aid,
  491. status: auditConst.status.checkNo,
  492. content: noticeContent,
  493. });
  494. });
  495. await transaction.insert(this.ctx.service.noticePush.tableName, records);
  496. // 计算并合同支付最终数据
  497. const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  498. this.ctx.stage.tp_history.push({
  499. times,
  500. order: audit.order,
  501. contract_tp: tpData.contract_tp,
  502. qc_tp: tpData.qc_tp,
  503. yf_tp: yfPay.tp,
  504. sf_tp: sfPay.tp,
  505. });
  506. await transaction.update(this.tableName, {
  507. id: audit.id,
  508. status: checkData.checkType,
  509. opinion: checkData.opinion,
  510. end_time: time,
  511. });
  512. // 同步 期信息
  513. await transaction.update(this.ctx.service.stage.tableName, {
  514. id: stageId,
  515. status: checkData.checkType,
  516. contract_tp: tpData.contract_tp,
  517. qc_tp: tpData.qc_tp,
  518. times: times + 1,
  519. yf_tp: yfPay.tp,
  520. sf_tp: sfPay.tp,
  521. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  522. cache_time_r: this.ctx.stage.cache_time_l,
  523. });
  524. // 拷贝新一次审核流程列表
  525. await transaction.insert(this.tableName, auditors);
  526. // 计算该审批人最终数据
  527. await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  528. // 复制一份最新数据给原报
  529. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times + 1, 0, transaction);
  530. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  531. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  532. // 添加短信通知-审批退回提醒功能
  533. // const mobile_array = [];
  534. const stageInfo = await this.ctx.service.stage.getDataById(stageId);
  535. const auditList = await this.getAuditors(stageId, stageInfo.times);
  536. // const smsUser1 = await this.ctx.service.projectAccount.getDataById(stageInfo.user_id);
  537. // if (smsUser1.auth_mobile !== '' && smsUser1.auth_mobile !== undefined && smsUser1.sms_type !== '' && smsUser1.sms_type !== null) {
  538. // const smsType = JSON.parse(smsUser1.sms_type);
  539. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.result.toString()) !== -1) {
  540. // mobile_array.push(smsUser1.auth_mobile);
  541. // }
  542. // }
  543. // for (const user of auditList) {
  544. // const smsUser = await this.ctx.service.projectAccount.getDataById(user.aid);
  545. // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  546. // const smsType = JSON.parse(smsUser.sms_type);
  547. // if (mobile_array.indexOf(smsUser.auth_mobile) === -1 && smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.result.toString()) !== -1) {
  548. // mobile_array.push(smsUser.auth_mobile);
  549. // }
  550. // }
  551. // }
  552. // if (mobile_array.length > 0) {
  553. // const tenderInfo = await this.ctx.service.tender.getDataById(stageInfo.tid);
  554. // const sms = new SMS(this.ctx);
  555. // const tenderName = await sms.contentChange(tenderInfo.name);
  556. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  557. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  558. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,审批退回。';
  559. // sms.send(mobile_array, content);
  560. // }
  561. const users = this._.uniq(this._.concat(this._.map(auditList, 'aid'), stageInfo.user_id));
  562. await this.ctx.helper.sendAliSms(users, smsTypeConst.const.JL, smsTypeConst.judge.result.toString(), SmsAliConst.template.stage_result, {
  563. qi: stageInfo.order,
  564. status: SmsAliConst.status.back,
  565. });
  566. // 微信模板通知
  567. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  568. const wechatData = {
  569. wap_url: shenpiUrl,
  570. qi: stageInfo.order,
  571. status: wxConst.status.back,
  572. tips: wxConst.tips.back,
  573. code: this.ctx.session.sessionProject.code,
  574. };
  575. await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.result.toString(), wxConst.template.stage, wechatData);
  576. await transaction.commit();
  577. } catch (err) {
  578. await transaction.rollback();
  579. throw err;
  580. }
  581. }
  582. async _checkNoPre(pid, stageId, checkData, times) {
  583. const time = new Date();
  584. // 整理当前流程审核人状态更新
  585. const audit = await this.getDataByCondition({ sid: stageId, times, status: auditConst.status.checking });
  586. if (!audit || audit.order <= 1) {
  587. throw '审核数据错误';
  588. }
  589. // 添加重新审批后,不能用order-1,取groupby值里的上一个才对
  590. // const preAuditor = await this.getDataByCondition({sid: stageId, times: times, order: audit.order - 1});
  591. const auditors2 = await this.getAuditGroupByList(stageId, times);
  592. const auditorIndex = await auditors2.findIndex(function(item) {
  593. return item.aid === audit.aid;
  594. });
  595. const preAuditor = auditors2[auditorIndex - 1];
  596. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  597. const transaction = await this.db.beginTransaction();
  598. try {
  599. // 添加推送
  600. const noticeContent = await this.getNoticeContent(pid, audit.tid, stageId, audit.aid);
  601. const records = [
  602. {
  603. pid,
  604. type: pushType.stage,
  605. uid: this.ctx.stage.user_id,
  606. status: auditConst.status.checkNoPre,
  607. content: noticeContent,
  608. },
  609. ];
  610. auditors2.forEach(audit => {
  611. records.push({
  612. pid,
  613. type: pushType.stage,
  614. uid: audit.aid,
  615. status: auditConst.status.checkNoPre,
  616. content: noticeContent,
  617. });
  618. });
  619. await transaction.insert('zh_notice', records);
  620. // 计算并合同支付最终数据
  621. const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  622. this.ctx.stage.tp_history.push({
  623. times,
  624. order: audit.order,
  625. contract_tp: tpData.contract_tp,
  626. qc_tp: tpData.qc_tp,
  627. yf_tp: yfPay.tp,
  628. sf_tp: sfPay.tp,
  629. });
  630. // 同步 期信息
  631. await transaction.update(this.ctx.service.stage.tableName, {
  632. id: stageId,
  633. contract_tp: tpData.contract_tp,
  634. qc_tp: tpData.qc_tp,
  635. times,
  636. yf_tp: yfPay.tp,
  637. sf_tp: sfPay.tp,
  638. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  639. cache_time_r: this.ctx.stage.cache_time_l,
  640. });
  641. await transaction.update(this.tableName, {
  642. id: audit.id,
  643. status: checkData.checkType,
  644. opinion: checkData.opinion,
  645. end_time: time,
  646. });
  647. // 顺移气候审核人流程顺序
  648. this.initSqlBuilder();
  649. this.sqlBuilder.setAndWhere('sid', { value: this.ctx.stage.id, operate: '=' });
  650. this.sqlBuilder.setAndWhere('order', { value: audit.order, operate: '>' });
  651. this.sqlBuilder.setUpdateData('order', { value: 2, selfOperate: '+' });
  652. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  653. const data = await transaction.query(sql, sqlParam);
  654. // 上一审批人,当前审批人 再次添加至流程
  655. const newAuditors = [];
  656. newAuditors.push({
  657. tid: audit.tid,
  658. sid: audit.sid,
  659. aid: preAuditor.aid,
  660. times: audit.times,
  661. order: audit.order + 1,
  662. status: auditConst.status.checking,
  663. begin_time: time,
  664. });
  665. newAuditors.push({
  666. tid: audit.tid,
  667. sid: audit.sid,
  668. aid: audit.aid,
  669. times: audit.times,
  670. order: audit.order + 2,
  671. status: auditConst.status.uncheck,
  672. });
  673. await transaction.insert(this.tableName, newAuditors);
  674. // 计算该审批人最终数据
  675. await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  676. // 复制一份最新数据给下一人
  677. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 1, transaction);
  678. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  679. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  680. await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
  681. // 同步 期信息
  682. await transaction.update(this.ctx.service.stage.tableName, {
  683. id: stageId,
  684. status: checkData.checkType,
  685. cache_time_r: this.ctx.stage.cache_time_l,
  686. });
  687. // 添加短信通知-需要审批提醒功能
  688. // const smsUser = await this.ctx.service.projectAccount.getDataById(preAuditor.aid);
  689. // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  690. // const smsType = JSON.parse(smsUser.sms_type);
  691. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
  692. // const tenderInfo = await this.ctx.service.tender.getDataById(audit.tid);
  693. // const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  694. // const sms = new SMS(this.ctx);
  695. // const tenderName = await sms.contentChange(tenderInfo.name);
  696. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  697. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  698. // const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  699. // // const result = '';
  700. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
  701. // sms.send(smsUser.auth_mobile, content);
  702. // }
  703. // }
  704. const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  705. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  706. await this.ctx.helper.sendAliSms(preAuditor.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  707. qi: stageInfo.order,
  708. code: shenpiUrl,
  709. });
  710. // 微信模板通知
  711. const wechatData = {
  712. wap_url: shenpiUrl,
  713. qi: stageInfo.order,
  714. status: wxConst.status.check,
  715. tips: wxConst.tips.check,
  716. code: this.ctx.session.sessionProject.code,
  717. };
  718. await this.ctx.helper.sendWechat(preAuditor.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  719. await transaction.commit();
  720. } catch (err) {
  721. await transaction.rollback();
  722. throw err;
  723. }
  724. }
  725. /**
  726. * 审批
  727. * @param {Number} stageId - 标段id
  728. * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果
  729. * @param {Number} times - 第几次审批
  730. * @return {Promise<void>}
  731. */
  732. async check(stageId, checkData, times = 1) {
  733. if (checkData.checkType !== auditConst.status.checked && checkData.checkType !== auditConst.status.checkNo && checkData.checkType !== auditConst.status.checkNoPre) {
  734. throw '提交数据错误';
  735. }
  736. // // 整理当前流程审核人状态更新
  737. // const audit = await this.getDataByCondition({sid: stageId, times: times, status: auditConst.status.checking});
  738. // if (!audit) {
  739. // throw '审核数据错误';
  740. // }
  741. // const time = new Date();
  742. const pid = this.ctx.session.sessionProject.id;
  743. switch (checkData.checkType) {
  744. case auditConst.status.checked:
  745. await this._checked(pid, stageId, checkData, times);
  746. break;
  747. case auditConst.status.checkNo:
  748. await this._checkNo(pid, stageId, checkData, times);
  749. break;
  750. case auditConst.status.checkNoPre:
  751. await this._checkNoPre(pid, stageId, checkData, times);
  752. break;
  753. default:
  754. throw '无效审批操作';
  755. }
  756. }
  757. /**
  758. * 审批
  759. * @param {Number} stageId - 标段id
  760. * @param {Number} times - 第几次审批
  761. * @return {Promise<void>}
  762. */
  763. async checkAgain(stageId, times = 1) {
  764. const time = new Date();
  765. // 整理当前流程审核人状态更新
  766. const audit = (
  767. await this.getAllDataByCondition({
  768. where: { sid: stageId, times },
  769. orders: [['order', 'desc']],
  770. limit: 1,
  771. offset: 0,
  772. })
  773. )[0];
  774. if (!audit || audit.order < 1) {
  775. throw '审核数据错误';
  776. }
  777. const transaction = await this.db.beginTransaction();
  778. try {
  779. // 当前审批人2次添加至流程中
  780. const newAuditors = [];
  781. newAuditors.push({
  782. tid: audit.tid,
  783. sid: audit.sid,
  784. aid: audit.aid,
  785. times: audit.times,
  786. order: audit.order + 1,
  787. status: auditConst.status.checkAgain,
  788. begin_time: time,
  789. end_time: time,
  790. opinion: '',
  791. });
  792. newAuditors.push({
  793. tid: audit.tid,
  794. sid: audit.sid,
  795. aid: audit.aid,
  796. times: audit.times,
  797. order: audit.order + 2,
  798. status: auditConst.status.checking,
  799. begin_time: time,
  800. });
  801. await transaction.insert(this.tableName, newAuditors);
  802. // 复制一份最新数据给下一人
  803. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 1, transaction);
  804. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 2, transaction);
  805. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  806. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  807. await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
  808. // 本期结束
  809. // 生成截止本期数据 final数据
  810. await this.ctx.service.stageBillsFinal.delGenerateFinalData(transaction, this.ctx.tender, this.ctx.stage);
  811. await this.ctx.service.stagePosFinal.delGenerateFinalData(transaction, this.ctx.tender, this.ctx.stage);
  812. // 同步 期信息
  813. await transaction.update(this.ctx.service.stage.tableName, {
  814. id: stageId,
  815. status: auditConst.status.checking,
  816. cache_time_r: this.ctx.stage.cache_time_l,
  817. });
  818. // 添加短信通知-需要审批提醒功能
  819. // const smsUser = await this.ctx.service.projectAccount.getDataById(audit.aid);
  820. // if (smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  821. // const smsType = JSON.parse(smsUser.sms_type);
  822. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
  823. // const tenderInfo = await this.ctx.service.tender.getDataById(audit.tid);
  824. // const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  825. // const sms = new SMS(this.ctx);
  826. // const tenderName = await sms.contentChange(tenderInfo.name);
  827. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  828. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  829. // const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  830. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
  831. // sms.send(smsUser.auth_mobile, content);
  832. // }
  833. // }
  834. const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  835. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  836. await this.ctx.helper.sendAliSms(audit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  837. qi: stageInfo.order,
  838. code: shenpiUrl,
  839. });
  840. // 微信模板通知
  841. const wechatData = {
  842. wap_url: shenpiUrl,
  843. qi: stageInfo.order,
  844. status: wxConst.status.check,
  845. tips: wxConst.tips.check,
  846. code: this.ctx.session.sessionProject.code,
  847. };
  848. await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  849. await transaction.commit();
  850. } catch (err) {
  851. await transaction.rollback();
  852. throw err;
  853. }
  854. }
  855. /**
  856. * 获取审核人需要审核的期列表
  857. *
  858. * @param auditorId
  859. * @return {Promise<*>}
  860. */
  861. async getAuditStage(auditorId) {
  862. const sql =
  863. 'SELECT sa.`aid`, sa.`times`, sa.`order`, sa.`begin_time`, sa.`end_time`, sa.`tid`, sa.`sid`,' +
  864. ' s.`order` As `sorder`, s.`status` As `sstatus`,' +
  865. ' t.`name`, t.`project_id`, t.`type`, t.`user_id` ' +
  866. ' FROM ?? AS sa, ?? AS s, ?? As t ' +
  867. ' WHERE ((sa.`aid` = ? and sa.`status` = ?) OR (s.`user_id` = ? and sa.`status` = ? and s.`status` = ? and sa.`times` = (s.`times`-1)))' +
  868. ' and sa.`sid` = s.`id` and sa.`tid` = t.`id` ORDER BY sa.`begin_time` DESC';
  869. const sqlParam = [
  870. this.tableName,
  871. this.ctx.service.stage.tableName,
  872. this.ctx.service.tender.tableName,
  873. auditorId,
  874. auditConst.status.checking,
  875. auditorId,
  876. auditConst.status.checkNo,
  877. auditConst.status.checkNo,
  878. ];
  879. return await this.db.query(sql, sqlParam);
  880. }
  881. /**
  882. * 获取 某时间后 审批进度 更新的期
  883. * @param {Number} pid - 查询标段
  884. * @param {Number} uid - 查询人
  885. * @param {Date} time - 查询时间
  886. * @return {Promise<*>}
  887. */
  888. async getNoticeStage(pid, uid, time) {
  889. // const sql = 'SELECT * FROM (SELECT t.`name`, t.`project_id`, t.`type`, t.`user_id`, ' +
  890. // ' s.`order` As `s_order`, s.`status` As `s_status`, ' +
  891. // ' sa.`aid`, sa.`times`, sa.`order`, sa.`end_time`, sa.`tid`, sa.`sid`, sa.`status`, ' +
  892. // ' pa.`name` As `su_name`, pa.role As `su_role`, pa.company As `su_company`' +
  893. // ' FROM (SELECT * FROM ?? WHERE `user_id` = ? OR `id` in (SELECT `tid` FROM ?? WHERE `aid` = ? GROUP BY `tid`)) As t' +
  894. // ' LEFT JOIN ?? As s On t.`id` = s.`tid`' +
  895. // ' LEFT JOIN ?? As sa ON s.`id` = sa.`sid`' +
  896. // ' LEFT JOIN ?? As pa ON sa.`aid` = pa.`id`' +
  897. // ' WHERE sa.`end_time` > ? and t.`project_id` = ?' +
  898. // ' ORDER By sa.`end_time` DESC LIMIT 1000) as new_t GROUP BY new_t.`tid`' +
  899. // ' ORDER By new_t.`end_time`';
  900. // const sqlParam = [this.ctx.service.tender.tableName, uid, this.tableName, uid, this.ctx.service.stage.tableName, this.tableName,
  901. // this.ctx.service.projectAccount.tableName, time, pid];
  902. // return await this.db.query(sql, sqlParam);
  903. let notice = await this.db.select('zh_notice', {
  904. where: { pid, type: pushType.stage, uid },
  905. orders: [['create_time', 'desc']],
  906. limit: 10,
  907. offset: 0,
  908. });
  909. notice = notice.map(v => {
  910. const extra = JSON.parse(v.content);
  911. delete v.content;
  912. return { ...v, ...extra };
  913. });
  914. return notice;
  915. }
  916. /**
  917. * 用于添加推送所需的content内容
  918. * @param {Number} pid 项目id
  919. * @param {Number} tid 台账id
  920. * @param {Number} sid 期id
  921. * @param {Number} uid 审核人id
  922. */
  923. async getNoticeContent(pid, tid, sid, uid) {
  924. const noticeSql =
  925. 'SELECT * FROM (SELECT ' +
  926. ' t.`id` As `tid`, t.`name`, s.`order`, pa.`name` As `su_name`, pa.role As `su_role`' +
  927. ' FROM (SELECT * FROM ?? WHERE `id` = ? ) As t' +
  928. ' LEFT JOIN ?? As s On s.`id` = ?' +
  929. ' LEFT JOIN ?? As pa ON pa.`id` = ?' +
  930. ' WHERE t.`project_id` = ? ) as new_t GROUP BY new_t.`tid`';
  931. const noticeSqlParam = [this.ctx.service.tender.tableName, tid, this.ctx.service.stage.tableName, sid, this.ctx.service.projectAccount.tableName, uid, pid];
  932. const content = await this.db.query(noticeSql, noticeSqlParam);
  933. return content.length ? JSON.stringify(content[0]) : '';
  934. }
  935. /**
  936. * 获取审核人流程列表
  937. *
  938. * @param auditorId
  939. * @return {Promise<*>}
  940. */
  941. async getAuditGroupByList(stageId, times) {
  942. const sql =
  943. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`aid`, la.`order` ' +
  944. 'FROM ?? AS la, ?? AS pa ' +
  945. 'WHERE la.`sid` = ? and la.`times` = ? and la.`aid` = pa.`id` GROUP BY la.`aid` ORDER BY la.`order`';
  946. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, times];
  947. return await this.db.query(sql, sqlParam);
  948. // const sql = 'SELECT `tid`, `sid`, `aid`, `order` FROM ?? WHERE `sid` = ? and `times` = ? GROUP BY `aid`';
  949. // const sqlParam = [this.tableName, stageId, times];
  950. // return await this.db.query(sql, sqlParam);
  951. }
  952. /**
  953. * 获取审核人流程列表
  954. *
  955. * @param auditorId
  956. * @return {Promise<*>}
  957. */
  958. async getAuditGroupByListWithOwner(stageId, times) {
  959. const result = await this.getAuditGroupByList(stageId, times);
  960. const sql =
  961. 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As sid, 0 As `order`' +
  962. ' FROM ' +
  963. this.ctx.service.stage.tableName +
  964. ' As s' +
  965. ' LEFT JOIN ' +
  966. this.ctx.service.projectAccount.tableName +
  967. ' As pa' +
  968. ' ON s.user_id = pa.id' +
  969. ' WHERE s.id = ?';
  970. const sqlParam = [times, stageId, stageId];
  971. const user = await this.db.queryOne(sql, sqlParam);
  972. result.unshift(user);
  973. return result;
  974. }
  975. /**
  976. * 复制上一期的审批人列表给最新一期
  977. *
  978. * @param transaction - 新增一期的事务
  979. * @param {Object} preStage - 上一期
  980. * @param {Object} newStage - 最新一期
  981. * @return {Promise<*>}
  982. */
  983. async copyPreStageAuditors(transaction, preStage, newStage) {
  984. const auditors = await this.getAuditGroupByList(preStage.id, preStage.times);
  985. const newAuditors = [];
  986. for (const a of auditors) {
  987. const na = {
  988. tid: preStage.tid,
  989. sid: newStage.id,
  990. aid: a.aid,
  991. times: newStage.times,
  992. order: newAuditors.length + 1,
  993. status: auditConst.status.uncheck,
  994. };
  995. newAuditors.push(na);
  996. }
  997. const result = await transaction.insert(this.tableName, newAuditors);
  998. return (result.effectRows = auditors.length);
  999. }
  1000. /**
  1001. * 移除审核人
  1002. *
  1003. * @param {Number} stageId - 期id
  1004. * @param {Number} status - 期状态
  1005. * @param {Number} status - 期次数
  1006. * @return {Promise<boolean>}
  1007. */
  1008. async getAuditorByStatus(stageId, status, times = 1) {
  1009. let auditor = null;
  1010. let sql = '';
  1011. let sqlParam = '';
  1012. switch (status) {
  1013. case auditConst.status.checking:
  1014. case auditConst.status.checked:
  1015. case auditConst.status.checkNoPre:
  1016. sql =
  1017. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`order` ' +
  1018. 'FROM ?? AS la, ?? AS pa ' +
  1019. 'WHERE la.`sid` = ? and la.`status` = ? and la.`aid` = pa.`id` order by la.`times` desc, la.`order` desc';
  1020. sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, status];
  1021. auditor = await this.db.queryOne(sql, sqlParam);
  1022. break;
  1023. case auditConst.status.checkNo:
  1024. sql =
  1025. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`order` ' +
  1026. 'FROM ?? AS la, ?? AS pa ' +
  1027. 'WHERE la.`sid` = ? and la.`status` = ? and la.`times` = ? and la.`aid` = pa.`id` order by la.`times` desc, la.`order` desc';
  1028. sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, auditConst.status.checkNo, parseInt(times) - 1];
  1029. auditor = await this.db.queryOne(sql, sqlParam);
  1030. break;
  1031. case auditConst.status.uncheck:
  1032. default:
  1033. break;
  1034. }
  1035. return auditor;
  1036. }
  1037. /**
  1038. * 取某一期已批准审核信息(报表用)
  1039. *
  1040. * @param {Number} stageId - 期id
  1041. * @param {Number} times - 期次数
  1042. * @return {Promise<boolean>}
  1043. */
  1044. async getStageAudit(stageId, times = 1) {
  1045. const sql = 'SELECT a1.aid, a1.begin_time, a1.end_time, a1.status, a1.opinion ' + 'FROM ?? AS a1 ' + 'WHERE a1.`sid` = ? and a1.`times` = ? ' + 'ORDER BY a1.order';
  1046. const sqlParam = [this.tableName, stageId, times];
  1047. const rst = await this.db.query(sql, sqlParam);
  1048. return rst;
  1049. }
  1050. /**
  1051. * 取待审批期列表(wap用)
  1052. *
  1053. * @param auditorId
  1054. * @return {Promise<*>}
  1055. */
  1056. async getAuditStageByWap(auditorId) {
  1057. const sql =
  1058. 'SELECT sa.`aid`, sa.`times`, sa.`begin_time`, sa.`end_time`, sa.`tid`, sa.`sid`,' +
  1059. // ' s.`order` As `sorder`, s.`status` As `sstatus`, s.`s_time`, s.`contract_tp`, s.`qc_tp`, s.`pre_contract_tp`, s.`pre_qc_tp`, s.`yf_tp`, s.`pre_yf_tp`, ' +
  1060. ' s.*,' +
  1061. ' t.`name`, t.`project_id`, t.`type`, t.`user_id`,' +
  1062. ' ti.`deal_info` ' +
  1063. ' FROM ?? AS sa, ?? AS s, ?? As t, ?? AS ti ' +
  1064. ' WHERE sa.`aid` = ? and sa.`status` = ?' +
  1065. ' and sa.`sid` = s.`id` and sa.`tid` = t.`id` and ti.`tid` = t.`id`';
  1066. const sqlParam = [
  1067. this.tableName,
  1068. this.ctx.service.stage.tableName,
  1069. this.ctx.service.tender.tableName,
  1070. this.ctx.service.tenderInfo.tableName,
  1071. auditorId,
  1072. auditConst.status.checking,
  1073. ];
  1074. return await this.db.query(sql, sqlParam);
  1075. }
  1076. /**
  1077. * 删除 某期 某次 全审批流程
  1078. * 私有,不做判断,不补全最新一轮审批人数据,不计算缓存
  1079. * @param {Number} sid - 标段id
  1080. * @param {Number} times - 第几次审批
  1081. * @param transaction - 删除事务
  1082. * @return {Promise<void>}
  1083. */
  1084. async _timesDelete(sid, times, transaction) {
  1085. // 审批流程
  1086. await transaction.delete(this.tableName, { sid, times });
  1087. await transaction.delete(this.ctx.service.pos.tableName, { add_stage: sid, add_times: times });
  1088. await transaction.delete(this.ctx.service.stageBills.tableName, { sid, times });
  1089. await transaction.delete(this.ctx.service.stagePos.tableName, { sid, times });
  1090. await transaction.delete(this.ctx.service.stageDetail.tableName, { sid, times });
  1091. await transaction.delete(this.ctx.service.stageChange.tableName, { sid, stimes: times });
  1092. await transaction.delete(this.ctx.service.stagePay.tableName, { sid, stimes: times });
  1093. await transaction.delete(this.ctx.service.pay.tableName, { csid: sid, cstimes: times });
  1094. // 其他台账
  1095. await this.ctx.service.stageJgcl.deleteStageTimesData(sid, times, transaction);
  1096. await this.ctx.service.stageOther.deleteStageTimesData(sid, times, transaction);
  1097. await this.ctx.service.stageBonus.deleteStageTimesData(sid, times, transaction);
  1098. }
  1099. /**
  1100. * 删除本次审批流程
  1101. * @param {Number} stageId - 标段id
  1102. * @param {Number} times - 第几次审批
  1103. * @return {Promise<void>}
  1104. */
  1105. async timesDelete() {
  1106. const transaction = await this.db.beginTransaction();
  1107. try {
  1108. // 删除最新一次数据
  1109. await this._timesDelete(this.ctx.stage.id, this.ctx.stage.times, transaction);
  1110. // 审批退回,未重新上报时,需删除最新两次数据
  1111. const isCheckNo = this.ctx.stage.status === auditConst.status.checkNo;
  1112. const nowTimes = isCheckNo ? this.ctx.stage.times - 1 : this.ctx.stage.times;
  1113. if (isCheckNo) {
  1114. await this._timesDelete(this.ctx.stage.id, nowTimes, transaction);
  1115. }
  1116. // 添加上一次审批人
  1117. const sql = 'SELECT `tid`, `sid`, `aid`, `order` FROM ?? WHERE `sid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  1118. const sqlParam = [this.tableName, this.ctx.stage.id, nowTimes - 1];
  1119. const auditors = await this.db.query(sql, sqlParam);
  1120. let order = 1;
  1121. for (const a of auditors) {
  1122. a.times = nowTimes;
  1123. a.order = order;
  1124. a.status = auditConst.status.uncheck;
  1125. order++;
  1126. }
  1127. // 拷贝新一次审核流程列表
  1128. await transaction.insert(this.tableName, auditors);
  1129. // 计算缓存
  1130. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  1131. // 计算并合同支付最终数据
  1132. const lastAudit = await this.getDataByCondition({
  1133. sid: this.ctx.stage.id,
  1134. times: nowTimes - 1,
  1135. status: auditConst.status.checkNo,
  1136. });
  1137. if (!lastAudit) throw '审批数据错误';
  1138. await this.ctx.service.stagePay.copyStagePays4DeleteTimes(this.ctx.stage, nowTimes, 0, lastAudit.times, lastAudit.order, transaction);
  1139. const stagePay = await this.ctx.service.stagePay.getAuditorStageData(this.ctx.stage.id, lastAudit.times, lastAudit.order);
  1140. const yfPay = stagePay.find(function(x) {
  1141. return x.ptype === payConst.payType.yf;
  1142. });
  1143. const sfPay = stagePay.find(function(x) {
  1144. return x.ptype === payConst.payType.sf;
  1145. });
  1146. // 同步 期信息
  1147. const time = new Date();
  1148. await transaction.update(this.ctx.service.stage.tableName, {
  1149. id: this.ctx.stage.id,
  1150. status: auditConst.status.checkNo,
  1151. contract_tp: tpData.contract_tp,
  1152. qc_tp: tpData.qc_tp,
  1153. times: nowTimes,
  1154. yf_tp: yfPay.tp,
  1155. sf_tp: sfPay.tp,
  1156. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  1157. cache_time_l: time,
  1158. cache_time_r: time,
  1159. });
  1160. await transaction.commit();
  1161. } catch (err) {
  1162. await transaction.rollback();
  1163. throw err;
  1164. }
  1165. }
  1166. }
  1167. return StageAudit;
  1168. };