stage_audit.js 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224
  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. const time = new Date();
  456. // 整理当前流程审核人状态更新
  457. const audit = await this.getDataByCondition({ sid: stageId, times, status: auditConst.status.checking });
  458. if (!audit) {
  459. throw '审核数据错误';
  460. }
  461. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  462. const sql = 'SELECT `tid`, `sid`, `aid`, `order` FROM ?? WHERE `sid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  463. const sqlParam = [this.tableName, stageId, times];
  464. const auditors = await this.db.query(sql, sqlParam);
  465. let order = 1;
  466. for (const a of auditors) {
  467. a.times = times + 1;
  468. a.order = order;
  469. a.status = auditConst.status.uncheck;
  470. order++;
  471. }
  472. const transaction = await this.db.beginTransaction();
  473. try {
  474. // 添加推送
  475. const noticeContent = await this.getNoticeContent(pid, audit.tid, stageId, audit.aid);
  476. const records = [
  477. {
  478. pid,
  479. type: pushType.stage,
  480. uid: this.ctx.stage.user_id,
  481. status: auditConst.status.checkNo,
  482. content: noticeContent,
  483. },
  484. ];
  485. auditors.forEach(audit => {
  486. records.push({
  487. pid,
  488. type: pushType.stage,
  489. uid: audit.aid,
  490. status: auditConst.status.checkNo,
  491. content: noticeContent,
  492. });
  493. });
  494. await transaction.insert('zh_notice', records);
  495. // 计算并合同支付最终数据
  496. const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  497. this.ctx.stage.tp_history.push({
  498. times,
  499. order: audit.order,
  500. contract_tp: tpData.contract_tp,
  501. qc_tp: tpData.qc_tp,
  502. yf_tp: yfPay.tp,
  503. sf_tp: sfPay.tp,
  504. });
  505. await transaction.update(this.tableName, {
  506. id: audit.id,
  507. status: checkData.checkType,
  508. opinion: checkData.opinion,
  509. end_time: time,
  510. });
  511. // 同步 期信息
  512. await transaction.update(this.ctx.service.stage.tableName, {
  513. id: stageId,
  514. status: checkData.checkType,
  515. contract_tp: tpData.contract_tp,
  516. qc_tp: tpData.qc_tp,
  517. times: times + 1,
  518. yf_tp: yfPay.tp,
  519. sf_tp: sfPay.tp,
  520. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  521. cache_time_r: this.ctx.stage.cache_time_l,
  522. });
  523. // 拷贝新一次审核流程列表
  524. await transaction.insert(this.tableName, auditors);
  525. // 计算该审批人最终数据
  526. await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  527. // 复制一份最新数据给原报
  528. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times + 1, 0, transaction);
  529. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  530. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  531. // 添加短信通知-审批退回提醒功能
  532. // const mobile_array = [];
  533. const stageInfo = await this.ctx.service.stage.getDataById(stageId);
  534. const auditList = await this.getAuditors(stageId, stageInfo.times);
  535. // const smsUser1 = await this.ctx.service.projectAccount.getDataById(stageInfo.user_id);
  536. // if (smsUser1.auth_mobile !== '' && smsUser1.auth_mobile !== undefined && smsUser1.sms_type !== '' && smsUser1.sms_type !== null) {
  537. // const smsType = JSON.parse(smsUser1.sms_type);
  538. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.result.toString()) !== -1) {
  539. // mobile_array.push(smsUser1.auth_mobile);
  540. // }
  541. // }
  542. // for (const user of auditList) {
  543. // const smsUser = await this.ctx.service.projectAccount.getDataById(user.aid);
  544. // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  545. // const smsType = JSON.parse(smsUser.sms_type);
  546. // if (mobile_array.indexOf(smsUser.auth_mobile) === -1 && smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.result.toString()) !== -1) {
  547. // mobile_array.push(smsUser.auth_mobile);
  548. // }
  549. // }
  550. // }
  551. // if (mobile_array.length > 0) {
  552. // const tenderInfo = await this.ctx.service.tender.getDataById(stageInfo.tid);
  553. // const sms = new SMS(this.ctx);
  554. // const tenderName = await sms.contentChange(tenderInfo.name);
  555. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  556. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  557. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,审批退回。';
  558. // sms.send(mobile_array, content);
  559. // }
  560. const users = this._.uniq(this._.concat(this._.map(auditList, 'aid'), stageInfo.user_id));
  561. await this.ctx.helper.sendAliSms(users, smsTypeConst.const.JL, smsTypeConst.judge.result.toString(), SmsAliConst.template.stage_result, {
  562. qi: stageInfo.order,
  563. status: SmsAliConst.status.back,
  564. });
  565. // 微信模板通知
  566. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  567. const wechatData = {
  568. wap_url: shenpiUrl,
  569. qi: stageInfo.order,
  570. status: wxConst.status.back,
  571. tips: wxConst.tips.back,
  572. code: this.ctx.session.sessionProject.code,
  573. };
  574. await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.result.toString(), wxConst.template.stage, wechatData);
  575. await transaction.commit();
  576. } catch (err) {
  577. await transaction.rollback();
  578. throw err;
  579. }
  580. }
  581. async _checkNoPre(pid, stageId, checkData, times) {
  582. const time = new Date();
  583. // 整理当前流程审核人状态更新
  584. const audit = await this.getDataByCondition({ sid: stageId, times, status: auditConst.status.checking });
  585. if (!audit || audit.order <= 1) {
  586. throw '审核数据错误';
  587. }
  588. // 添加重新审批后,不能用order-1,取groupby值里的上一个才对
  589. // const preAuditor = await this.getDataByCondition({sid: stageId, times: times, order: audit.order - 1});
  590. const auditors2 = await this.getAuditGroupByList(stageId, times);
  591. const auditorIndex = await auditors2.findIndex(function(item) {
  592. return item.aid === audit.aid;
  593. });
  594. const preAuditor = auditors2[auditorIndex - 1];
  595. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  596. const transaction = await this.db.beginTransaction();
  597. try {
  598. // 添加推送
  599. const noticeContent = await this.getNoticeContent(pid, audit.tid, stageId, audit.aid);
  600. const records = [
  601. {
  602. pid,
  603. type: pushType.stage,
  604. uid: this.ctx.stage.user_id,
  605. status: auditConst.status.checkNoPre,
  606. content: noticeContent,
  607. },
  608. ];
  609. auditors2.forEach(audit => {
  610. records.push({
  611. pid,
  612. type: pushType.stage,
  613. uid: audit.aid,
  614. status: auditConst.status.checkNoPre,
  615. content: noticeContent,
  616. });
  617. });
  618. await transaction.insert('zh_notice', records);
  619. // 计算并合同支付最终数据
  620. const [yfPay, sfPay] = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  621. this.ctx.stage.tp_history.push({
  622. times,
  623. order: audit.order,
  624. contract_tp: tpData.contract_tp,
  625. qc_tp: tpData.qc_tp,
  626. yf_tp: yfPay.tp,
  627. sf_tp: sfPay.tp,
  628. });
  629. // 同步 期信息
  630. await transaction.update(this.ctx.service.stage.tableName, {
  631. id: stageId,
  632. contract_tp: tpData.contract_tp,
  633. qc_tp: tpData.qc_tp,
  634. times,
  635. yf_tp: yfPay.tp,
  636. sf_tp: sfPay.tp,
  637. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  638. cache_time_r: this.ctx.stage.cache_time_l,
  639. });
  640. await transaction.update(this.tableName, {
  641. id: audit.id,
  642. status: checkData.checkType,
  643. opinion: checkData.opinion,
  644. end_time: time,
  645. });
  646. // 顺移气候审核人流程顺序
  647. this.initSqlBuilder();
  648. this.sqlBuilder.setAndWhere('sid', { value: this.ctx.stage.id, operate: '=' });
  649. this.sqlBuilder.setAndWhere('order', { value: audit.order, operate: '>' });
  650. this.sqlBuilder.setUpdateData('order', { value: 2, selfOperate: '+' });
  651. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  652. const data = await transaction.query(sql, sqlParam);
  653. // 上一审批人,当前审批人 再次添加至流程
  654. const newAuditors = [];
  655. newAuditors.push({
  656. tid: audit.tid,
  657. sid: audit.sid,
  658. aid: preAuditor.aid,
  659. times: audit.times,
  660. order: audit.order + 1,
  661. status: auditConst.status.checking,
  662. begin_time: time,
  663. });
  664. newAuditors.push({
  665. tid: audit.tid,
  666. sid: audit.sid,
  667. aid: audit.aid,
  668. times: audit.times,
  669. order: audit.order + 2,
  670. status: auditConst.status.uncheck,
  671. });
  672. await transaction.insert(this.tableName, newAuditors);
  673. // 计算该审批人最终数据
  674. await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
  675. // 复制一份最新数据给下一人
  676. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 1, transaction);
  677. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  678. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  679. await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
  680. // 同步 期信息
  681. await transaction.update(this.ctx.service.stage.tableName, {
  682. id: stageId,
  683. status: checkData.checkType,
  684. cache_time_r: this.ctx.stage.cache_time_l,
  685. });
  686. // 添加短信通知-需要审批提醒功能
  687. // const smsUser = await this.ctx.service.projectAccount.getDataById(preAuditor.aid);
  688. // if (smsUser.auth_mobile !== '' && smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  689. // const smsType = JSON.parse(smsUser.sms_type);
  690. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
  691. // const tenderInfo = await this.ctx.service.tender.getDataById(audit.tid);
  692. // const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  693. // const sms = new SMS(this.ctx);
  694. // const tenderName = await sms.contentChange(tenderInfo.name);
  695. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  696. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  697. // const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  698. // // const result = '';
  699. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
  700. // sms.send(smsUser.auth_mobile, content);
  701. // }
  702. // }
  703. const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  704. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  705. await this.ctx.helper.sendAliSms(preAuditor.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  706. qi: stageInfo.order,
  707. code: shenpiUrl,
  708. });
  709. // 微信模板通知
  710. const wechatData = {
  711. wap_url: shenpiUrl,
  712. qi: stageInfo.order,
  713. status: wxConst.status.check,
  714. tips: wxConst.tips.check,
  715. code: this.ctx.session.sessionProject.code,
  716. };
  717. await this.ctx.helper.sendWechat(preAuditor.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  718. await transaction.commit();
  719. } catch (err) {
  720. await transaction.rollback();
  721. throw err;
  722. }
  723. }
  724. /**
  725. * 审批
  726. * @param {Number} stageId - 标段id
  727. * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果
  728. * @param {Number} times - 第几次审批
  729. * @return {Promise<void>}
  730. */
  731. async check(stageId, checkData, times = 1) {
  732. if (checkData.checkType !== auditConst.status.checked && checkData.checkType !== auditConst.status.checkNo && checkData.checkType !== auditConst.status.checkNoPre) {
  733. throw '提交数据错误';
  734. }
  735. // // 整理当前流程审核人状态更新
  736. // const audit = await this.getDataByCondition({sid: stageId, times: times, status: auditConst.status.checking});
  737. // if (!audit) {
  738. // throw '审核数据错误';
  739. // }
  740. // const time = new Date();
  741. const pid = this.ctx.session.sessionProject.id;
  742. switch (checkData.checkType) {
  743. case auditConst.status.checked:
  744. await this._checked(pid, stageId, checkData, times);
  745. break;
  746. case auditConst.status.checkNo:
  747. await this._checkNo(pid, stageId, checkData, times);
  748. break;
  749. case auditConst.status.checkNoPre:
  750. await this._checkNoPre(pid, stageId, checkData, times);
  751. break;
  752. default:
  753. throw '无效审批操作';
  754. }
  755. }
  756. /**
  757. * 审批
  758. * @param {Number} stageId - 标段id
  759. * @param {Number} times - 第几次审批
  760. * @return {Promise<void>}
  761. */
  762. async checkAgain(stageId, times = 1) {
  763. const time = new Date();
  764. // 整理当前流程审核人状态更新
  765. const audit = (
  766. await this.getAllDataByCondition({
  767. where: { sid: stageId, times },
  768. orders: [['order', 'desc']],
  769. limit: 1,
  770. offset: 0,
  771. })
  772. )[0];
  773. if (!audit || audit.order < 1) {
  774. throw '审核数据错误';
  775. }
  776. const transaction = await this.db.beginTransaction();
  777. try {
  778. // 当前审批人2次添加至流程中
  779. const newAuditors = [];
  780. newAuditors.push({
  781. tid: audit.tid,
  782. sid: audit.sid,
  783. aid: audit.aid,
  784. times: audit.times,
  785. order: audit.order + 1,
  786. status: auditConst.status.checkAgain,
  787. begin_time: time,
  788. end_time: time,
  789. opinion: '',
  790. });
  791. newAuditors.push({
  792. tid: audit.tid,
  793. sid: audit.sid,
  794. aid: audit.aid,
  795. times: audit.times,
  796. order: audit.order + 2,
  797. status: auditConst.status.checking,
  798. begin_time: time,
  799. });
  800. await transaction.insert(this.tableName, newAuditors);
  801. // 复制一份最新数据给下一人
  802. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 1, transaction);
  803. await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 2, transaction);
  804. await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
  805. await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
  806. await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
  807. // 本期结束
  808. // 生成截止本期数据 final数据
  809. await this.ctx.service.stageBillsFinal.delGenerateFinalData(transaction, this.ctx.tender, this.ctx.stage);
  810. await this.ctx.service.stagePosFinal.delGenerateFinalData(transaction, this.ctx.tender, this.ctx.stage);
  811. // 同步 期信息
  812. await transaction.update(this.ctx.service.stage.tableName, {
  813. id: stageId,
  814. status: auditConst.status.checking,
  815. cache_time_r: this.ctx.stage.cache_time_l,
  816. });
  817. // 添加短信通知-需要审批提醒功能
  818. // const smsUser = await this.ctx.service.projectAccount.getDataById(audit.aid);
  819. // if (smsUser.auth_mobile !== undefined && smsUser.sms_type !== '' && smsUser.sms_type !== null) {
  820. // const smsType = JSON.parse(smsUser.sms_type);
  821. // if (smsType[smsTypeConst.const.JL] !== undefined && smsType[smsTypeConst.const.JL].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
  822. // const tenderInfo = await this.ctx.service.tender.getDataById(audit.tid);
  823. // const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  824. // const sms = new SMS(this.ctx);
  825. // const tenderName = await sms.contentChange(tenderInfo.name);
  826. // const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
  827. // const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
  828. // const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  829. // const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
  830. // sms.send(smsUser.auth_mobile, content);
  831. // }
  832. // }
  833. const stageInfo = await this.ctx.service.stage.getDataById(audit.sid);
  834. const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
  835. await this.ctx.helper.sendAliSms(audit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  836. qi: stageInfo.order,
  837. code: shenpiUrl,
  838. });
  839. // 微信模板通知
  840. const wechatData = {
  841. wap_url: shenpiUrl,
  842. qi: stageInfo.order,
  843. status: wxConst.status.check,
  844. tips: wxConst.tips.check,
  845. code: this.ctx.session.sessionProject.code,
  846. };
  847. await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  848. await transaction.commit();
  849. } catch (err) {
  850. await transaction.rollback();
  851. throw err;
  852. }
  853. }
  854. /**
  855. * 获取审核人需要审核的期列表
  856. *
  857. * @param auditorId
  858. * @return {Promise<*>}
  859. */
  860. async getAuditStage(auditorId) {
  861. const sql =
  862. 'SELECT sa.`aid`, sa.`times`, sa.`order`, sa.`begin_time`, sa.`end_time`, sa.`tid`, sa.`sid`,' +
  863. ' s.`order` As `sorder`, s.`status` As `sstatus`,' +
  864. ' t.`name`, t.`project_id`, t.`type`, t.`user_id` ' +
  865. ' FROM ?? AS sa, ?? AS s, ?? As t ' +
  866. ' WHERE ((sa.`aid` = ? and sa.`status` = ?) OR (s.`user_id` = ? and sa.`status` = ? and s.`status` = ? and sa.`times` = (s.`times`-1)))' +
  867. ' and sa.`sid` = s.`id` and sa.`tid` = t.`id` ORDER BY sa.`begin_time` DESC';
  868. const sqlParam = [
  869. this.tableName,
  870. this.ctx.service.stage.tableName,
  871. this.ctx.service.tender.tableName,
  872. auditorId,
  873. auditConst.status.checking,
  874. auditorId,
  875. auditConst.status.checkNo,
  876. auditConst.status.checkNo,
  877. ];
  878. return await this.db.query(sql, sqlParam);
  879. }
  880. /**
  881. * 获取 某时间后 审批进度 更新的期
  882. * @param {Number} pid - 查询标段
  883. * @param {Number} uid - 查询人
  884. * @param {Date} time - 查询时间
  885. * @return {Promise<*>}
  886. */
  887. async getNoticeStage(pid, uid, time) {
  888. // const sql = 'SELECT * FROM (SELECT t.`name`, t.`project_id`, t.`type`, t.`user_id`, ' +
  889. // ' s.`order` As `s_order`, s.`status` As `s_status`, ' +
  890. // ' sa.`aid`, sa.`times`, sa.`order`, sa.`end_time`, sa.`tid`, sa.`sid`, sa.`status`, ' +
  891. // ' pa.`name` As `su_name`, pa.role As `su_role`, pa.company As `su_company`' +
  892. // ' FROM (SELECT * FROM ?? WHERE `user_id` = ? OR `id` in (SELECT `tid` FROM ?? WHERE `aid` = ? GROUP BY `tid`)) As t' +
  893. // ' LEFT JOIN ?? As s On t.`id` = s.`tid`' +
  894. // ' LEFT JOIN ?? As sa ON s.`id` = sa.`sid`' +
  895. // ' LEFT JOIN ?? As pa ON sa.`aid` = pa.`id`' +
  896. // ' WHERE sa.`end_time` > ? and t.`project_id` = ?' +
  897. // ' ORDER By sa.`end_time` DESC LIMIT 1000) as new_t GROUP BY new_t.`tid`' +
  898. // ' ORDER By new_t.`end_time`';
  899. // const sqlParam = [this.ctx.service.tender.tableName, uid, this.tableName, uid, this.ctx.service.stage.tableName, this.tableName,
  900. // this.ctx.service.projectAccount.tableName, time, pid];
  901. // return await this.db.query(sql, sqlParam);
  902. let notice = await this.db.select('zh_notice', {
  903. where: { pid, type: pushType.stage, uid },
  904. orders: [['create_time', 'desc']],
  905. limit: 10,
  906. offset: 0,
  907. });
  908. notice = notice.map(v => {
  909. const extra = JSON.parse(v.content);
  910. delete v.content;
  911. return { ...v, ...extra };
  912. });
  913. return notice;
  914. }
  915. /**
  916. * 用于添加推送所需的content内容
  917. * @param {Number} pid 项目id
  918. * @param {Number} tid 台账id
  919. * @param {Number} sid 期id
  920. * @param {Number} uid 审核人id
  921. */
  922. async getNoticeContent(pid, tid, sid, uid) {
  923. const noticeSql =
  924. 'SELECT * FROM (SELECT ' +
  925. ' t.`id` As `tid`, t.`name`, s.`order`, pa.`name` As `su_name`, pa.role As `su_role`' +
  926. ' FROM (SELECT * FROM ?? WHERE `id` = ? ) As t' +
  927. ' LEFT JOIN ?? As s On s.`id` = ?' +
  928. ' LEFT JOIN ?? As pa ON pa.`id` = ?' +
  929. ' WHERE t.`project_id` = ? ) as new_t GROUP BY new_t.`tid`';
  930. const noticeSqlParam = [this.ctx.service.tender.tableName, tid, this.ctx.service.stage.tableName, sid, this.ctx.service.projectAccount.tableName, uid, pid];
  931. const content = await this.db.query(noticeSql, noticeSqlParam);
  932. return content.length ? JSON.stringify(content[0]) : '';
  933. }
  934. /**
  935. * 获取审核人流程列表
  936. *
  937. * @param auditorId
  938. * @return {Promise<*>}
  939. */
  940. async getAuditGroupByList(stageId, times) {
  941. const sql =
  942. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`aid`, la.`order` ' +
  943. 'FROM ?? AS la, ?? AS pa ' +
  944. 'WHERE la.`sid` = ? and la.`times` = ? and la.`aid` = pa.`id` GROUP BY la.`aid` ORDER BY la.`order`';
  945. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, times];
  946. return await this.db.query(sql, sqlParam);
  947. // const sql = 'SELECT `tid`, `sid`, `aid`, `order` FROM ?? WHERE `sid` = ? and `times` = ? GROUP BY `aid`';
  948. // const sqlParam = [this.tableName, stageId, times];
  949. // return await this.db.query(sql, sqlParam);
  950. }
  951. /**
  952. * 获取审核人流程列表
  953. *
  954. * @param auditorId
  955. * @return {Promise<*>}
  956. */
  957. async getAuditGroupByListWithOwner(stageId, times) {
  958. const result = await this.getAuditGroupByList(stageId, times);
  959. const sql =
  960. 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As sid, 0 As `order`' +
  961. ' FROM ' +
  962. this.ctx.service.stage.tableName +
  963. ' As s' +
  964. ' LEFT JOIN ' +
  965. this.ctx.service.projectAccount.tableName +
  966. ' As pa' +
  967. ' ON s.user_id = pa.id' +
  968. ' WHERE s.id = ?';
  969. const sqlParam = [times, stageId, stageId];
  970. const user = await this.db.queryOne(sql, sqlParam);
  971. result.unshift(user);
  972. return result;
  973. }
  974. /**
  975. * 复制上一期的审批人列表给最新一期
  976. *
  977. * @param transaction - 新增一期的事务
  978. * @param {Object} preStage - 上一期
  979. * @param {Object} newStage - 最新一期
  980. * @return {Promise<*>}
  981. */
  982. async copyPreStageAuditors(transaction, preStage, newStage) {
  983. const auditors = await this.getAuditGroupByList(preStage.id, preStage.times);
  984. const newAuditors = [];
  985. for (const a of auditors) {
  986. const na = {
  987. tid: preStage.tid,
  988. sid: newStage.id,
  989. aid: a.aid,
  990. times: newStage.times,
  991. order: newAuditors.length + 1,
  992. status: auditConst.status.uncheck,
  993. };
  994. newAuditors.push(na);
  995. }
  996. const result = await transaction.insert(this.tableName, newAuditors);
  997. return (result.effectRows = auditors.length);
  998. }
  999. /**
  1000. * 移除审核人
  1001. *
  1002. * @param {Number} stageId - 期id
  1003. * @param {Number} status - 期状态
  1004. * @param {Number} status - 期次数
  1005. * @return {Promise<boolean>}
  1006. */
  1007. async getAuditorByStatus(stageId, status, times = 1) {
  1008. let auditor = null;
  1009. let sql = '';
  1010. let sqlParam = '';
  1011. switch (status) {
  1012. case auditConst.status.checking:
  1013. case auditConst.status.checked:
  1014. case auditConst.status.checkNoPre:
  1015. sql =
  1016. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`order` ' +
  1017. 'FROM ?? AS la, ?? AS pa ' +
  1018. 'WHERE la.`sid` = ? and la.`status` = ? and la.`aid` = pa.`id` order by la.`times` desc, la.`order` desc';
  1019. sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, status];
  1020. auditor = await this.db.queryOne(sql, sqlParam);
  1021. break;
  1022. case auditConst.status.checkNo:
  1023. sql =
  1024. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`order` ' +
  1025. 'FROM ?? AS la, ?? AS pa ' +
  1026. 'WHERE la.`sid` = ? and la.`status` = ? and la.`times` = ? and la.`aid` = pa.`id` order by la.`times` desc, la.`order` desc';
  1027. sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, auditConst.status.checkNo, parseInt(times) - 1];
  1028. auditor = await this.db.queryOne(sql, sqlParam);
  1029. break;
  1030. case auditConst.status.uncheck:
  1031. default:
  1032. break;
  1033. }
  1034. return auditor;
  1035. }
  1036. /**
  1037. * 取某一期已批准审核信息(报表用)
  1038. *
  1039. * @param {Number} stageId - 期id
  1040. * @param {Number} times - 期次数
  1041. * @return {Promise<boolean>}
  1042. */
  1043. async getStageAudit(stageId, times = 1) {
  1044. 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';
  1045. const sqlParam = [this.tableName, stageId, times];
  1046. const rst = await this.db.query(sql, sqlParam);
  1047. return rst;
  1048. }
  1049. /**
  1050. * 取待审批期列表(wap用)
  1051. *
  1052. * @param auditorId
  1053. * @return {Promise<*>}
  1054. */
  1055. async getAuditStageByWap(auditorId) {
  1056. const sql =
  1057. 'SELECT sa.`aid`, sa.`times`, sa.`begin_time`, sa.`end_time`, sa.`tid`, sa.`sid`,' +
  1058. // ' 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`, ' +
  1059. ' s.*,' +
  1060. ' t.`name`, t.`project_id`, t.`type`, t.`user_id`,' +
  1061. ' ti.`deal_info` ' +
  1062. ' FROM ?? AS sa, ?? AS s, ?? As t, ?? AS ti ' +
  1063. ' WHERE sa.`aid` = ? and sa.`status` = ?' +
  1064. ' and sa.`sid` = s.`id` and sa.`tid` = t.`id` and ti.`tid` = t.`id`';
  1065. const sqlParam = [
  1066. this.tableName,
  1067. this.ctx.service.stage.tableName,
  1068. this.ctx.service.tender.tableName,
  1069. this.ctx.service.tenderInfo.tableName,
  1070. auditorId,
  1071. auditConst.status.checking,
  1072. ];
  1073. return await this.db.query(sql, sqlParam);
  1074. }
  1075. /**
  1076. * 删除 某期 某次 全审批流程
  1077. * 私有,不做判断,不补全最新一轮审批人数据,不计算缓存
  1078. * @param {Number} sid - 标段id
  1079. * @param {Number} times - 第几次审批
  1080. * @param transaction - 删除事务
  1081. * @return {Promise<void>}
  1082. */
  1083. async _timesDelete(sid, times, transaction) {
  1084. // 审批流程
  1085. await transaction.delete(this.tableName, { sid, times });
  1086. await transaction.delete(this.ctx.service.pos.tableName, { add_stage: sid, add_times: times });
  1087. await transaction.delete(this.ctx.service.stageBills.tableName, { sid, times });
  1088. await transaction.delete(this.ctx.service.stagePos.tableName, { sid, times });
  1089. await transaction.delete(this.ctx.service.stageDetail.tableName, { sid, times });
  1090. await transaction.delete(this.ctx.service.stageChange.tableName, { sid, stimes: times });
  1091. await transaction.delete(this.ctx.service.stagePay.tableName, { sid, stimes: times });
  1092. await transaction.delete(this.ctx.service.pay.tableName, { csid: sid, cstimes: times });
  1093. // 其他台账
  1094. await this.ctx.service.stageJgcl.deleteStageTimesData(sid, times, transaction);
  1095. await this.ctx.service.stageOther.deleteStageTimesData(sid, times, transaction);
  1096. await this.ctx.service.stageBonus.deleteStageTimesData(sid, times, transaction);
  1097. }
  1098. /**
  1099. * 删除本次审批流程
  1100. * @param {Number} stageId - 标段id
  1101. * @param {Number} times - 第几次审批
  1102. * @return {Promise<void>}
  1103. */
  1104. async timesDelete() {
  1105. const transaction = await this.db.beginTransaction();
  1106. try {
  1107. // 删除最新一次数据
  1108. await this._timesDelete(this.ctx.stage.id, this.ctx.stage.times, transaction);
  1109. // 审批退回,未重新上报时,需删除最新两次数据
  1110. const isCheckNo = this.ctx.stage.status === auditConst.status.checkNo;
  1111. const nowTimes = isCheckNo ? this.ctx.stage.times - 1 : this.ctx.stage.times;
  1112. if (isCheckNo) {
  1113. await this._timesDelete(this.ctx.stage.id, nowTimes, transaction);
  1114. }
  1115. // 添加上一次审批人
  1116. const sql = 'SELECT `tid`, `sid`, `aid`, `order` FROM ?? WHERE `sid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  1117. const sqlParam = [this.tableName, this.ctx.stage.id, nowTimes - 1];
  1118. const auditors = await this.db.query(sql, sqlParam);
  1119. let order = 1;
  1120. for (const a of auditors) {
  1121. a.times = nowTimes;
  1122. a.order = order;
  1123. a.status = auditConst.status.uncheck;
  1124. order++;
  1125. }
  1126. // 拷贝新一次审核流程列表
  1127. await transaction.insert(this.tableName, auditors);
  1128. // 计算缓存
  1129. const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
  1130. // 计算并合同支付最终数据
  1131. const lastAudit = await this.getDataByCondition({
  1132. sid: this.ctx.stage.id,
  1133. times: nowTimes - 1,
  1134. status: auditConst.status.checkNo,
  1135. });
  1136. if (!lastAudit) throw '审批数据错误';
  1137. await this.ctx.service.stagePay.copyStagePays4DeleteTimes(this.ctx.stage, nowTimes, 0, lastAudit.times, lastAudit.order, transaction);
  1138. const stagePay = await this.ctx.service.stagePay.getAuditorStageData(this.ctx.stage.id, lastAudit.times, lastAudit.order);
  1139. const yfPay = stagePay.find(function(x) {
  1140. return x.ptype === payConst.payType.yf;
  1141. });
  1142. const sfPay = stagePay.find(function(x) {
  1143. return x.ptype === payConst.payType.sf;
  1144. });
  1145. // 同步 期信息
  1146. const time = new Date();
  1147. await transaction.update(this.ctx.service.stage.tableName, {
  1148. id: this.ctx.stage.id,
  1149. status: auditConst.status.checkNo,
  1150. contract_tp: tpData.contract_tp,
  1151. qc_tp: tpData.qc_tp,
  1152. times: nowTimes,
  1153. yf_tp: yfPay.tp,
  1154. sf_tp: sfPay.tp,
  1155. tp_history: JSON.stringify(this.ctx.stage.tp_history),
  1156. cache_time_l: time,
  1157. cache_time_r: time,
  1158. });
  1159. await transaction.commit();
  1160. } catch (err) {
  1161. await transaction.rollback();
  1162. throw err;
  1163. }
  1164. }
  1165. }
  1166. return StageAudit;
  1167. };