change_project_audit.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date 2018/8/14
  7. * @version
  8. */
  9. const auditConst = require('../const/audit').changeProject;
  10. const pushType = require('../const/audit').pushType;
  11. const shenpiConst = require('../const/shenpi');
  12. const smsTypeConst = require('../const/sms_type');
  13. const SMS = require('../lib/sms');
  14. const SmsAliConst = require('../const/sms_alitemplate');
  15. const wxConst = require('../const/wechat_template');
  16. module.exports = app => {
  17. class ChangeProjectAudit extends app.BaseService {
  18. /**
  19. * 构造函数
  20. *
  21. * @param {Object} ctx - egg全局变量
  22. * @return {void}
  23. */
  24. constructor(ctx) {
  25. super(ctx);
  26. this.tableName = 'change_project_audit';
  27. }
  28. /**
  29. * 获取 审核列表信息
  30. *
  31. * @param {Number} cpId - 变更立项id
  32. * @param {Number} times - 第几次审批
  33. * @return {Promise<*>}
  34. */
  35. async getAuditors(cpId, times = 1) {
  36. const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' +
  37. 'FROM ?? AS la, ?? AS pa, (SELECT t1.`aid`,(@i:=@i+1) as `sort` FROM (SELECT t.`aid`, t.`order` FROM (select `aid`, `order` from ?? WHERE `cpid` = ? AND `times` = ? ORDER BY `order` LIMIT 200) t GROUP BY t.`aid` ORDER BY t.`order`) t1, (select @i:=0) as it) as g ' +
  38. 'WHERE la.`cpid` = ? and la.`times` = ? and la.`aid` = pa.`id` and g.`aid` = la.`aid` order by la.`order`';
  39. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.tableName, cpId, times, cpId, times];
  40. const result = await this.db.query(sql, sqlParam);
  41. const sql2 = 'SELECT COUNT(a.`aid`) as num FROM (SELECT `aid` FROM ?? WHERE `cpid` = ? AND `times` = ? GROUP BY `aid`) as a';
  42. const sqlParam2 = [this.tableName, cpId, times];
  43. const count = await this.db.queryOne(sql2, sqlParam2);
  44. for (const i in result) {
  45. result[i].max_sort = count.num;
  46. }
  47. return result;
  48. }
  49. /**
  50. * 获取 当前审核人
  51. *
  52. * @param {Number} cpId - 变更立项id
  53. * @param {Number} times - 第几次审批
  54. * @return {Promise<*>}
  55. */
  56. async getCurAuditor(cpId, times = 1) {
  57. const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
  58. ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
  59. ' WHERE la.`cpid` = ? and la.`status` = ? and la.`times` = ?';
  60. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cpId, auditConst.status.checking, times];
  61. return await this.db.queryOne(sql, sqlParam);
  62. }
  63. /**
  64. * 获取审核人流程列表
  65. *
  66. * @param auditorId
  67. * @return {Promise<*>}
  68. */
  69. async getAuditGroupByList(changeId, times) {
  70. const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`aid`, la.`order` ' +
  71. ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
  72. ' WHERE la.`cpid` = ? and la.`times` = ? and la.`status` != ? and la.`status` != ? GROUP BY la.`aid` ORDER BY la.`order`';
  73. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times, auditConst.status.revise, auditConst.status.cancelRevise];
  74. return await this.db.query(sql, sqlParam);
  75. }
  76. /**
  77. * 移除审核人
  78. *
  79. * @param {Number} materialId - 材料调差期id
  80. * @param {Number} status - 期状态
  81. * @param {Number} status - 期次数
  82. * @return {Promise<boolean>}
  83. */
  84. async getAuditorByStatus(cpId, status, times = 1) {
  85. let auditor = null;
  86. let sql = '';
  87. let sqlParam = '';
  88. switch (status) {
  89. case auditConst.status.checking :
  90. case auditConst.status.checked :
  91. case auditConst.status.checkNo :
  92. case auditConst.status.revise :
  93. case auditConst.status.cancelRevise :
  94. sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`aid`, la.`order` ' +
  95. ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
  96. ' WHERE la.`cpid` = ? and la.`status` = ? ' +
  97. ' ORDER BY la.`times` desc, la.`order` desc';
  98. sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cpId, status];
  99. auditor = await this.db.queryOne(sql, sqlParam);
  100. break;
  101. case auditConst.status.back :
  102. sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`aid`, la.`order` ' +
  103. ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
  104. ' WHERE la.`cpid` = ? and la.`status` = ? and la.`times` = ?' +
  105. ' ORDER BY la.`times` desc, la.`order` desc';
  106. sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cpId, auditConst.status.back, parseInt(times) - 1];
  107. auditor = await this.db.queryOne(sql, sqlParam);
  108. break;
  109. case auditConst.status.uncheck :
  110. break;
  111. default:break;
  112. }
  113. return auditor;
  114. }
  115. async getLastAudit(cpid, times, transaction = null) {
  116. const sql = 'SELECT * FROM ?? WHERE `cpid` = ? AND `times` = ? ORDER BY `order` DESC';
  117. const sqlParam = [this.tableName, cpid, times];
  118. return transaction ? await transaction.queryOne(sql, sqlParam) : await this.db.queryOne(sql, sqlParam);
  119. }
  120. /**
  121. * 获取审核人流程列表(包括原报)
  122. * @param {Number} materialId 调差id
  123. * @param {Number} times 审核次数
  124. * @return {Promise<Array>} 查询结果集(包括原报)
  125. */
  126. async getAuditorsWithOwner(cpId, times = 1) {
  127. const result = await this.getAuditGroupByList(cpId, times);
  128. const sql =
  129. 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As cpid, 0 As `order`' +
  130. ' FROM ' +
  131. this.ctx.service.changeProject.tableName +
  132. ' As s' +
  133. ' LEFT JOIN ' +
  134. this.ctx.service.projectAccount.tableName +
  135. ' As pa' +
  136. ' ON s.uid = pa.id' +
  137. ' WHERE s.id = ?';
  138. const sqlParam = [times, cpId, cpId];
  139. const user = await this.db.queryOne(sql, sqlParam);
  140. result.unshift(user);
  141. return result;
  142. }
  143. /**
  144. * 新增审核人
  145. *
  146. * @param {Number} cpId - 立项书id
  147. * @param {Number} auditorId - 审核人id
  148. * @param {Number} times - 第几次审批
  149. * @return {Promise<number>}
  150. */
  151. async addAuditor(cpId, auditorId, times = 1, is_gdzs = 0) {
  152. const transaction = await this.db.beginTransaction();
  153. let flag = false;
  154. try {
  155. let newOrder = await this.getNewOrder(cpId, times);
  156. // 判断是否存在固定终审,存在则newOrder - 1并使终审order+1
  157. newOrder = is_gdzs === 1 ? newOrder - 1 : newOrder;
  158. if (is_gdzs) await this._syncOrderByDelete(transaction, cpId, newOrder, times, '+');
  159. const data = {
  160. tid: this.ctx.tender.id,
  161. cpid: cpId,
  162. aid: auditorId,
  163. times,
  164. order: newOrder,
  165. status: auditConst.status.uncheck,
  166. };
  167. const result = await transaction.insert(this.tableName, data);
  168. await transaction.commit();
  169. flag = result.effectRows = 1;
  170. } catch (err) {
  171. await transaction.rollback();
  172. throw err;
  173. }
  174. return flag;
  175. }
  176. /**
  177. * 获取 最新审核顺序
  178. *
  179. * @param {Number} cpId - 立项书id
  180. * @param {Number} times - 第几次审批
  181. * @return {Promise<number>}
  182. */
  183. async getNewOrder(cpId, times = 1) {
  184. const sql = 'SELECT Max(??) As max_order FROM ?? Where `cpid` = ? and `times` = ?';
  185. const sqlParam = ['order', this.tableName, cpId, times];
  186. const result = await this.db.queryOne(sql, sqlParam);
  187. return result && result.max_order ? result.max_order + 1 : 1;
  188. }
  189. /**
  190. * 移除审核人
  191. *
  192. * @param {Number} cpId - 变更立项书id
  193. * @param {Number} auditorId - 审核人id
  194. * @param {Number} times - 第几次审批
  195. * @return {Promise<boolean>}
  196. */
  197. async deleteAuditor(cpId, auditorId, times = 1) {
  198. const transaction = await this.db.beginTransaction();
  199. try {
  200. const condition = { cpid: cpId, aid: auditorId, times };
  201. const auditor = await this.getDataByCondition(condition);
  202. if (!auditor) {
  203. throw '该审核人不存在';
  204. }
  205. await this._syncOrderByDelete(transaction, cpId, auditor.order, times);
  206. await transaction.delete(this.tableName, condition);
  207. await transaction.commit();
  208. } catch (err) {
  209. await transaction.rollback();
  210. throw err;
  211. }
  212. return true;
  213. }
  214. /**
  215. * 移除审核人时,同步其后审核人order
  216. * @param transaction - 事务
  217. * @param {Number} cpId - 变更立项书id
  218. * @param {Number} auditorId - 审核人id
  219. * @param {Number} times - 第几次审批
  220. * @return {Promise<*>}
  221. * @private
  222. */
  223. async _syncOrderByDelete(transaction, cpId, order, times, selfOperate = '-') {
  224. this.initSqlBuilder();
  225. this.sqlBuilder.setAndWhere('cpid', {
  226. value: cpId,
  227. operate: '=',
  228. });
  229. this.sqlBuilder.setAndWhere('order', {
  230. value: order,
  231. operate: '>=',
  232. });
  233. this.sqlBuilder.setAndWhere('times', {
  234. value: times,
  235. operate: '=',
  236. });
  237. this.sqlBuilder.setUpdateData('order', {
  238. value: 1,
  239. selfOperate,
  240. });
  241. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  242. const data = await transaction.query(sql, sqlParam);
  243. return data;
  244. }
  245. /**
  246. * 开始审批
  247. * @param {Number} cpId - 立项书id
  248. * @param {Number} times - 第几次审批
  249. * @return {Promise<boolean>}
  250. */
  251. async start(cpId, times = 1) {
  252. const audit = await this.getDataByCondition({ cpid: cpId, times, order: 1 });
  253. if (!audit) {
  254. // if (this.ctx.tender.info.shenpi.material === shenpiConst.sp_status.gdspl) {
  255. // throw '请联系管理员添加审批人';
  256. // } else {
  257. throw '请先选择审批人,再上报数据';
  258. // }
  259. }
  260. const transaction = await this.db.beginTransaction();
  261. try {
  262. await transaction.update(this.tableName, { id: audit.id, status: auditConst.status.checking, begin_time: new Date() });
  263. await transaction.update(this.ctx.service.changeProject.tableName, {
  264. id: cpId, status: auditConst.status.checking,
  265. });
  266. // 微信模板通知
  267. const wechatData = {
  268. type: 'project',
  269. status: wxConst.status.check,
  270. tips: wxConst.tips.check,
  271. code: this.ctx.session.sessionProject.code,
  272. c_name: this.ctx.change.name,
  273. };
  274. await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
  275. await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: cpId });
  276. // todo 更新标段tender状态 ?
  277. await transaction.commit();
  278. } catch (err) {
  279. await transaction.rollback();
  280. throw err;
  281. }
  282. return true;
  283. }
  284. /**
  285. * 获取审核人需要审核的期列表
  286. *
  287. * @param auditorId
  288. * @return {Promise<*>}
  289. */
  290. async getAuditChangeProject(auditorId) {
  291. const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`cpid`,' +
  292. ' m.`status` As `mstatus`, m.`code` As `mcode`,' +
  293. ' t.`name`, t.`project_id`, t.`type`, t.`user_id` ' +
  294. ' FROM ?? AS ma, ?? AS m, ?? As t ' +
  295. ' WHERE ((ma.`aid` = ? and ma.`status` = ?) OR (m.`uid` = ? and ma.`status` = ? and m.`status` = ? and ma.`times` = (m.`times`-1)))' +
  296. ' and ma.`cpid` = m.`id` and ma.`tid` = t.`id` ORDER BY ma.`begin_time` DESC';
  297. const sqlParam = [this.tableName, this.ctx.service.changeProject.tableName, this.ctx.service.tender.tableName, auditorId, auditConst.status.checking, auditorId, auditConst.status.back, auditConst.status.back];
  298. return await this.db.query(sql, sqlParam);
  299. }
  300. /**
  301. * 获取审核人审核的次数
  302. *
  303. * @param auditorId
  304. * @return {Promise<*>}
  305. */
  306. async getCountByChecked(auditorId) {
  307. return await this.db.count(this.tableName, { aid: auditorId, status: [auditConst.status.checked, auditConst.status.back, auditConst.status.checkNo] });
  308. }
  309. /**
  310. * 获取最近一次审批结束时间
  311. *
  312. * @param auditorId
  313. * @return {Promise<*>}
  314. */
  315. async getLastEndTimeByChecked(auditorId) {
  316. const sql = 'SELECT `end_time` FROM ?? WHERE `aid` = ? ' +
  317. 'AND `status` in (' + this.ctx.helper.getInArrStrSqlFilter([auditConst.status.checked, auditConst.status.back, auditConst.status.checkNo]) + ') ORDER BY `end_time` DESC';
  318. const sqlParam = [this.tableName, auditorId];
  319. const result = await this.db.queryOne(sql, sqlParam);
  320. return result ? result.end_time : null;
  321. }
  322. /**
  323. * 用于添加推送所需的content内容
  324. * @param {Number} pid 项目id
  325. * @param {Number} tid 台账id
  326. * @param {Number} cpId 立项书id
  327. * @param {Number} uid 审批人id
  328. */
  329. async getNoticeContent(pid, tid, cpId, uid, opinion = '') {
  330. const noticeSql = 'SELECT * FROM (SELECT ' +
  331. ' t.`id` As `tid`, ma.`cpid`, m.`code` as `c_code`, t.`name`, pa.`name` As `su_name`, pa.role As `su_role`' +
  332. ' FROM (SELECT * FROM ?? WHERE `id` = ? ) As t' +
  333. ' LEFT JOIN ?? As m On t.`id` = m.`tid` AND m.`id` = ?' +
  334. ' LEFT JOIN ?? As ma ON m.`id` = ma.`cpid`' +
  335. ' LEFT JOIN ?? As pa ON pa.`id` = ?' +
  336. ' WHERE t.`project_id` = ? ) as new_t GROUP BY new_t.`tid`';
  337. const noticeSqlParam = [this.ctx.service.tender.tableName, tid, this.ctx.service.changeProject.tableName, cpId, this.tableName, this.ctx.service.projectAccount.tableName, uid, pid];
  338. const content = await this.db.query(noticeSql, noticeSqlParam);
  339. if (content.length) {
  340. content[0].opinion = opinion;
  341. }
  342. return content.length ? JSON.stringify(content[0]) : '';
  343. }
  344. /**
  345. * 审批
  346. * @param {Number} cpId - 立项书id
  347. * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果
  348. * @param {Number} times - 第几次审批
  349. * @return {Promise<void>}
  350. */
  351. async check(cpId, checkData, times = 1) {
  352. if (checkData.checkType !== auditConst.status.checked && checkData.checkType !== auditConst.status.checkNo && checkData.checkType !== auditConst.status.back) {
  353. throw '提交数据错误';
  354. }
  355. const pid = this.ctx.session.sessionProject.id;
  356. switch (checkData.checkType) {
  357. case auditConst.status.checked:
  358. await this._checked(pid, cpId, checkData, times);
  359. break;
  360. case auditConst.status.back:
  361. await this._back(pid, cpId, checkData, times);
  362. break;
  363. case auditConst.status.checkNo:
  364. await this._checkNo(pid, cpId, checkData, times);
  365. break;
  366. default:
  367. throw '无效审批操作';
  368. }
  369. }
  370. async _checked(pid, cpId, checkData, times) {
  371. const time = new Date();
  372. // 整理当前流程审核人状态更新
  373. const audit = await this.getDataByCondition({ cpid: cpId, times, status: auditConst.status.checking });
  374. if (!audit) {
  375. throw '审核数据错误';
  376. }
  377. // 获取审核人列表
  378. const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  379. const sqlParam = [this.tableName, cpId, times];
  380. const auditors = await this.db.query(sql, sqlParam);
  381. const nextAudit = await this.getDataByCondition({ cpid: cpId, times, order: audit.order + 1 });
  382. const transaction = await this.db.beginTransaction();
  383. try {
  384. await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
  385. // 获取推送必要信息
  386. const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
  387. // 添加推送
  388. const records = [{ pid, type: pushType.changeProject, uid: this.ctx.change.uid, status: auditConst.status.checked, content: noticeContent }];
  389. auditors.forEach(audit => {
  390. records.push({ pid, type: pushType.changeProject, uid: audit.aid, status: auditConst.status.checked, content: noticeContent });
  391. });
  392. await transaction.insert('zh_notice', records);
  393. // 无下一审核人表示,审核结束
  394. if (nextAudit) {
  395. // 流程至下一审批人
  396. await transaction.update(this.tableName, { id: nextAudit.id, status: auditConst.status.checking, begin_time: time });
  397. // 同步 期信息
  398. await transaction.update(this.ctx.service.changeProject.tableName, {
  399. id: cpId, status: auditConst.status.checking,
  400. });
  401. // 微信模板通知
  402. const wechatData = {
  403. type: 'project',
  404. status: wxConst.status.check,
  405. tips: wxConst.tips.check,
  406. code: this.ctx.session.sessionProject.code,
  407. c_name: this.ctx.change.name,
  408. };
  409. await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
  410. } else {
  411. // 本期结束
  412. // 生成截止本期数据 final数据
  413. // 同步 期信息
  414. await transaction.update(this.ctx.service.changeProject.tableName, {
  415. id: cpId, status: checkData.checkType,
  416. });
  417. // 微信模板通知
  418. const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), this.ctx.change.uid));
  419. const wechatData = {
  420. type: 'project',
  421. status: wxConst.status.success,
  422. tips: wxConst.tips.success,
  423. code: this.ctx.session.sessionProject.code,
  424. c_name: this.ctx.change.name,
  425. };
  426. await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
  427. }
  428. await transaction.commit();
  429. } catch (err) {
  430. await transaction.rollback();
  431. throw err;
  432. }
  433. }
  434. async _back(pid, cpId, checkData, times) {
  435. const time = new Date();
  436. const changeData = await this.ctx.service.changeProject.getDataById(cpId);
  437. // 整理当前流程审核人状态更新
  438. const audit = await this.getDataByCondition({ cpid: cpId, times, status: auditConst.status.checking });
  439. if (!audit) {
  440. throw '审核数据错误';
  441. }
  442. const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  443. const sqlParam = [this.tableName, cpId, times];
  444. const auditors = await this.db.query(sql, sqlParam);
  445. let order = 1;
  446. for (const a of auditors) {
  447. a.times = times + 1;
  448. a.order = order;
  449. a.status = auditConst.status.uncheck;
  450. order++;
  451. }
  452. const transaction = await this.db.beginTransaction();
  453. try {
  454. await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
  455. // 添加到消息推送表
  456. const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
  457. const records = [{ pid, type: pushType.changeProject, uid: this.ctx.change.uid, status: auditConst.status.back, content: noticeContent }];
  458. auditors.forEach(audit => {
  459. records.push({ pid, type: pushType.changeProject, uid: audit.aid, status: auditConst.status.back, content: noticeContent });
  460. });
  461. await transaction.insert(this.ctx.service.noticePush.tableName, records);
  462. // 同步期信息
  463. await transaction.update(this.ctx.service.changeProject.tableName, {
  464. id: cpId, status: checkData.checkType,
  465. times: times + 1,
  466. });
  467. // 拷贝新一次审核流程列表
  468. await transaction.insert(this.tableName, auditors);
  469. // 微信模板通知
  470. const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), this.ctx.change.uid));
  471. const wechatData = {
  472. type: 'project',
  473. status: wxConst.status.back,
  474. tips: wxConst.tips.back,
  475. code: this.ctx.session.sessionProject.code,
  476. c_name: this.ctx.change.name,
  477. };
  478. await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
  479. // 生成内容保存表至zh_change_project_history中,用于撤回
  480. await this.ctx.service.changeProjectHistory.saveHistory(transaction, changeData);
  481. await transaction.commit();
  482. } catch (err) {
  483. await transaction.rollback();
  484. throw err;
  485. }
  486. }
  487. async _checkNo(pid, cpId, checkData, times) {
  488. const time = new Date();
  489. // 整理当前流程审核人状态更新
  490. const audit = await this.getDataByCondition({ cpid: cpId, times, status: auditConst.status.checking });
  491. if (!audit) {
  492. throw '审核数据错误';
  493. }
  494. // 获取审核人列表
  495. const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  496. const sqlParam = [this.tableName, cpId, times];
  497. const auditors = await this.db.query(sql, sqlParam);
  498. const transaction = await this.db.beginTransaction();
  499. try {
  500. await transaction.update(this.tableName, { id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time });
  501. // 获取推送必要信息
  502. const noticeContent = await this.getNoticeContent(pid, audit.tid, cpId, audit.aid, checkData.opinion);
  503. // 添加推送
  504. const records = [{ pid, type: pushType.changeProject, uid: this.ctx.change.uid, status: auditConst.status.checkNo, content: noticeContent }];
  505. auditors.forEach(audit => {
  506. records.push({ pid, type: pushType.changeProject, uid: audit.aid, status: auditConst.status.checkNo, content: noticeContent });
  507. });
  508. await transaction.insert('zh_notice', records);
  509. // 本期结束
  510. // 生成截止本期数据 final数据
  511. // 同步 期信息
  512. await transaction.update(this.ctx.service.changeProject.tableName, {
  513. id: cpId, status: checkData.checkType,
  514. });
  515. // 微信模板通知
  516. const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), this.ctx.change.uid));
  517. const wechatData = {
  518. type: 'project',
  519. status: wxConst.status.stop,
  520. tips: wxConst.tips.stop,
  521. code: this.ctx.session.sessionProject.code,
  522. c_name: this.ctx.change.name,
  523. };
  524. await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
  525. await transaction.commit();
  526. } catch (err) {
  527. await transaction.rollback();
  528. throw err;
  529. }
  530. }
  531. /**
  532. * 复制上一期的审批人列表给最新一期
  533. *
  534. * @param transaction - 新增一期的事务
  535. * @param {Object} preMaterial - 上一期
  536. * @param {Object} newaMaterial - 最新一期
  537. * @return {Promise<*>}
  538. */
  539. async copyPreChangeProjectAuditors(transaction, preChange, newChange) {
  540. const auditors = await this.getAuditGroupByList(preChange.id, preChange.times);
  541. const newAuditors = [];
  542. for (const a of auditors) {
  543. const na = {
  544. tid: preChange.tid,
  545. cpid: newChange.id,
  546. aid: a.aid,
  547. times: newChange.times,
  548. order: newAuditors.length + 1,
  549. status: auditConst.status.uncheck,
  550. };
  551. newAuditors.push(na);
  552. }
  553. const result = newAuditors.length > 0 ? await transaction.insert(this.tableName, newAuditors) : null;
  554. return result ? result.affectedRows === auditors.length : true;
  555. }
  556. async getAllAuditors(tenderId) {
  557. const sql = 'SELECT ma.aid, ma.tid FROM ' + this.tableName + ' ma' +
  558. ' LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On ma.tid = t.id' +
  559. ' WHERE t.id = ?' +
  560. ' GROUP BY ma.aid';
  561. const sqlParam = [tenderId];
  562. return this.db.query(sql, sqlParam);
  563. }
  564. /**
  565. * 审批撤回
  566. * @param {Number} stageId - 标段id
  567. * @param {Number} times - 第几次审批
  568. * @return {Promise<void>}
  569. */
  570. async checkCancel(change) {
  571. // 分4种情况,根据ctx.cancancel值判断:
  572. // 1.原报发起撤回,当前流程删除,并回到待上报
  573. // 2.审批人撤回审批通过,增加流程,并回到它审批中
  574. // 3.审批人撤回审批终止,增加流程,并回到它审批中,并更新立项状态为审批中
  575. // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中
  576. switch (change.cancancel) {
  577. case 1: await this._userCheckCancel(change); break;
  578. case 2: await this._auditCheckCancel(change); break;
  579. case 3: await this._auditCheckCancelStop(change); break;
  580. case 4: await this._auditCheckCancelNo(change); break;
  581. default: throw '不可撤回,请刷新页面重试';
  582. }
  583. }
  584. /**
  585. * 原报撤回,直接改动审批人状态
  586. * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人
  587. *
  588. * 一审 1 A checking -> A uncheck status改 pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1
  589. * ...
  590. *
  591. * @param stage
  592. * @returns {Promise<void>}
  593. * @private
  594. */
  595. async _userCheckCancel(change) {
  596. const transaction = await this.db.beginTransaction();
  597. try {
  598. // 整理当前流程审核人状态更新
  599. const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checking });
  600. // 审批人变成待审批状态
  601. await transaction.update(this.tableName, {
  602. id: curAudit.id,
  603. status: auditConst.status.uncheck,
  604. begin_time: null,
  605. opinion: null,
  606. });
  607. // 变成待上报状态
  608. await transaction.update(this.ctx.service.changeProject.tableName, {
  609. id: change.id,
  610. status: change.times === 1 ? auditConst.status.uncheck : auditConst.status.back,
  611. });
  612. await transaction.commit();
  613. } catch (err) {
  614. await transaction.rollback();
  615. throw err;
  616. }
  617. }
  618. /**
  619. * 审批人撤回审批通过,插入两条数据
  620. *
  621. * 一审 1 A checked 一审 1 A checked
  622. * 二审 2 B checked pre -> 二审 2 B checked
  623. * 三审 3 C checking cur 二审 3 B checkCancel 增 增extra_his 增tp_his
  624. * 四审 4 D uncheck 二审 4 B checking 增 增pay_cur
  625. * 三审 5 C uncheck order、status改
  626. * 四审 6 D uncheck order改
  627. *
  628. * @param stage
  629. * @returns {Promise<void>}
  630. * @private
  631. */
  632. async _auditCheckCancel(change) {
  633. const time = new Date();
  634. const transaction = await this.db.beginTransaction();
  635. try {
  636. // 整理当前流程审核人状态更新
  637. const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checking });
  638. const preAudit = change.preAudit;
  639. if (!curAudit || curAudit.order <= 1 || !preAudit) {
  640. throw '撤回用户数据错误';
  641. }
  642. // 顺移其后审核人流程顺序
  643. const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
  644. await transaction.query(sql, [change.id, change.times, curAudit.order]);
  645. // 当前审批人2次添加至流程中
  646. const newAuditors = [];
  647. // 先入撤回记录
  648. newAuditors.push({
  649. tid: change.tid,
  650. cpid: change.id,
  651. aid: preAudit.aid,
  652. times: change.times,
  653. order: curAudit.order,
  654. status: auditConst.status.checkCancel,
  655. begin_time: time,
  656. end_time: time,
  657. opinion: '',
  658. });
  659. newAuditors.push({
  660. tid: change.tid,
  661. cpid: change.id,
  662. aid: preAudit.aid,
  663. times: change.times,
  664. order: curAudit.order + 1,
  665. status: auditConst.status.checking,
  666. begin_time: time,
  667. });
  668. await transaction.insert(this.tableName, newAuditors);
  669. // 当前审批人变成待审批
  670. await transaction.update(this.tableName, { id: curAudit.id, order: curAudit.order + 2, begin_time: null, status: auditConst.status.uncheck });
  671. await transaction.commit();
  672. } catch (err) {
  673. await transaction.rollback();
  674. throw err;
  675. }
  676. }
  677. /**
  678. * 审批人撤回审批终止,插入两条数据
  679. *
  680. * @param stage
  681. * @returns {Promise<void>}
  682. * @private
  683. */
  684. async _auditCheckCancelStop(change) {
  685. const time = new Date();
  686. const transaction = await this.db.beginTransaction();
  687. try {
  688. // const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checkNo });
  689. const curAudit = await this.getAuditorByStatus(change.id, auditConst.status.checkNo);
  690. if (!curAudit) {
  691. throw '撤回用户数据错误';
  692. }
  693. // 顺移其后审核人流程顺序
  694. const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
  695. await transaction.query(sql, [change.id, change.times, curAudit.order]);
  696. // 当前审批人2次添加至流程中
  697. const newAuditors = [];
  698. // 先入撤回记录
  699. newAuditors.push({
  700. tid: change.tid,
  701. cpid: change.id,
  702. aid: curAudit.aid,
  703. times: change.times,
  704. order: curAudit.order + 1,
  705. status: auditConst.status.checkCancel,
  706. begin_time: time,
  707. end_time: time,
  708. opinion: '',
  709. });
  710. newAuditors.push({
  711. tid: change.tid,
  712. cpid: change.id,
  713. aid: curAudit.aid,
  714. times: change.times,
  715. order: curAudit.order + 2,
  716. status: auditConst.status.checking,
  717. begin_time: time,
  718. });
  719. await transaction.insert(this.tableName, newAuditors);
  720. await transaction.update(this.ctx.service.changeProject.tableName, {
  721. id: change.id,
  722. status: auditConst.status.checking,
  723. });
  724. await transaction.commit();
  725. } catch (err) {
  726. await transaction.rollback();
  727. throw err;
  728. }
  729. }
  730. /**
  731. * 审批人撤回审批退回原报
  732. *
  733. * 1# 一审 1 A checked 1# 一审 1 A checked
  734. * 二审 2 B checkNo pre -> 二审 2 B checkNo
  735. * 三审 3 C uncheck 二审 3 B checkCancel 增 pay: 2#0 -> 1#3 jl: 2#0 -> 1#3 增tp_his 增extra_his
  736. * 二审 4 B checking 增 pay: 2#0 -> 1#4
  737. * 三审 5 C uncheck order改
  738. *
  739. * 2# 一审 1 A uncheck 2# 删 pay: 2#0删 jl: 2#0删
  740. * 二审 2 B uncheck
  741. * 三审 3 C uncheck
  742. *
  743. * @param stage
  744. * @returns {Promise<void>}
  745. * @private
  746. */
  747. async _auditCheckCancelNo(change) {
  748. const time = new Date();
  749. const transaction = await this.db.beginTransaction();
  750. try {
  751. // const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times - 1, status: auditConst.status.back });
  752. const curAudit = await this.getAuditorByStatus(change.id, auditConst.status.back, change.times);
  753. // 整理上一个流程审核人状态更新
  754. // 顺移其后审核人流程顺序
  755. const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
  756. await transaction.query(sql, [change.id, change.times - 1, curAudit.order]);
  757. // 当前审批人2次添加至流程中
  758. const newAuditors = [];
  759. newAuditors.push({
  760. tid: change.tid,
  761. cpid: change.id,
  762. aid: curAudit.aid,
  763. times: curAudit.times,
  764. order: curAudit.order + 1,
  765. status: auditConst.status.checkCancel,
  766. begin_time: time,
  767. end_time: time,
  768. opinion: '',
  769. });
  770. newAuditors.push({
  771. tid: change.tid,
  772. cpid: change.id,
  773. aid: curAudit.aid,
  774. times: curAudit.times,
  775. order: curAudit.order + 2,
  776. status: auditConst.status.checking,
  777. begin_time: time,
  778. });
  779. await transaction.insert(this.tableName, newAuditors);
  780. // 删除当前次审批流
  781. await transaction.delete(this.tableName, { cpid: change.id, times: change.times });
  782. // 回退数据
  783. await this.ctx.service.changeProjectHistory.returnHistory(transaction, change.id);
  784. await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: change.id });
  785. // // 设置变更立项为审批中
  786. // await transaction.update(this.ctx.service.changeProject.tableName, {
  787. // id: change.id,
  788. // time: change.times - 1,
  789. // status: auditConst.status.checking,
  790. // });
  791. await transaction.commit();
  792. } catch (err) {
  793. await transaction.rollback();
  794. throw err;
  795. }
  796. }
  797. /**
  798. * 重新审批变更令
  799. * @param { string } cid - 查询的清单
  800. * @return {Promise<*>} - 可用的变更令列表
  801. */
  802. async checkRevise(change) {
  803. const time = new Date();
  804. // 初始化事务
  805. const transaction = await this.db.beginTransaction();
  806. let result = false;
  807. try {
  808. const pid = this.ctx.session.sessionProject.id;
  809. // 获取审核人列表
  810. const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  811. const sqlParam = [this.tableName, change.id, change.times];
  812. const auditors = await this.db.query(sql, sqlParam);
  813. // 添加到消息推送表
  814. const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '发起修订');
  815. const records = [];
  816. auditors.forEach(auditor => {
  817. records.push({
  818. pid,
  819. type: pushType.changeProject,
  820. uid: auditor.aid,
  821. status: auditConst.status.revise,
  822. content: noticeContent,
  823. });
  824. });
  825. await transaction.insert('zh_notice', records);
  826. // 获取当前次数审批人列表
  827. const auditList = await this.getAuditGroupByList(change.id, change.times);
  828. const lastAudit = await this.getLastAudit(change.id, change.times);
  829. const insert_audit_array = [];
  830. // 新增一个发起修订状态到审批流程中
  831. const revise_audit = {
  832. tid: change.tid,
  833. cpid: change.id,
  834. aid: change.uid,
  835. times: change.times,
  836. order: lastAudit.order + 1,
  837. status: auditConst.status.revise,
  838. begin_time: time,
  839. end_time: time,
  840. opinion: '',
  841. };
  842. insert_audit_array.push(revise_audit);
  843. // 新增新一次的审批人列表
  844. let order = 1;
  845. for (const al of auditList) {
  846. const insert_audit = {
  847. tid: change.tid,
  848. cpid: change.id,
  849. aid: al.aid,
  850. times: change.times + 1,
  851. order,
  852. status: auditConst.status.uncheck,
  853. };
  854. insert_audit_array.push(insert_audit);
  855. order++;
  856. }
  857. await transaction.insert(this.tableName, insert_audit_array);
  858. // 生成内容保存表至zh_change_history中,用于撤销修订回退
  859. const changeData = await transaction.get(this.ctx.service.changeProject.tableName, { id: change.id });
  860. await this.ctx.service.changeProjectHistory.saveHistory(transaction, changeData);
  861. // 设置变更立项修订状态
  862. await transaction.update(this.ctx.service.changeProject.tableName, {
  863. id: change.id,
  864. status: auditConst.status.revise,
  865. times: change.times + 1,
  866. });
  867. await transaction.commit();
  868. result = true;
  869. } catch (error) {
  870. console.log(error);
  871. await transaction.rollback();
  872. result = false;
  873. }
  874. return result;
  875. }
  876. /**
  877. * 撤销修订变更令
  878. * @param { string } cid - 查询的清单
  879. * @return {Promise<*>} - 可用的变更令列表
  880. */
  881. async cancelRevise(change) {
  882. const time = new Date();
  883. // 初始化事务
  884. const transaction = await this.db.beginTransaction();
  885. let result = false;
  886. try {
  887. const pid = this.ctx.session.sessionProject.id;
  888. // 获取审核人列表
  889. const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
  890. const sqlParam = [this.tableName, change.id, change.times - 1];
  891. const auditors = await this.db.query(sql, sqlParam);
  892. // 添加到消息推送表
  893. const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '撤销修订');
  894. const records = [];
  895. auditors.forEach(auditor => {
  896. records.push({
  897. pid,
  898. type: pushType.changeProject,
  899. uid: auditor.aid,
  900. status: auditConst.status.cancelRevise,
  901. content: noticeContent,
  902. });
  903. });
  904. await transaction.insert('zh_notice', records);
  905. const lastAudit = await this.getLastAudit(change.id, change.times - 1);
  906. // 新增一个撤销修订状态到审批流程中
  907. const revise_audit = {
  908. tid: change.tid,
  909. cpid: change.id,
  910. aid: this.ctx.session.sessionUser.accountId,
  911. times: change.times - 1,
  912. order: lastAudit.order + 1,
  913. status: auditConst.status.cancelRevise,
  914. begin_time: time,
  915. end_time: time,
  916. opinion: '',
  917. };
  918. await transaction.insert(this.ctx.service.changeProjectAudit.tableName, revise_audit);
  919. await transaction.delete(this.ctx.service.changeProjectAudit.tableName, { cpid: change.id, times: change.times });
  920. await this.ctx.service.changeProjectHistory.returnHistory(transaction, change.id);
  921. await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: change.id });
  922. await transaction.commit();
  923. result = true;
  924. } catch (error) {
  925. console.log(error);
  926. await transaction.rollback();
  927. result = false;
  928. }
  929. return result;
  930. }
  931. /**
  932. * 重新审批变更立项
  933. * @param { string } cid - 查询的清单
  934. * @return {Promise<*>} - 可用的变更令列表
  935. */
  936. async checkAgain(change) {
  937. // 初始化事务
  938. const time = new Date();
  939. const transaction = await this.db.beginTransaction();
  940. let result = false;
  941. try {
  942. // 获取终审
  943. const zsAudit = await this.getAuditorByStatus(change.id, auditConst.status.checked);
  944. const lastAudit = await this.getLastAudit(change.id, change.times);
  945. const insert_audit_array = [];
  946. // 新增2个审批状态到审批列表中
  947. insert_audit_array.push({
  948. tid: change.tid,
  949. cpid: change.id,
  950. aid: zsAudit.aid,
  951. times: zsAudit.times,
  952. order: lastAudit.order + 1,
  953. status: auditConst.status.checkAgain,
  954. begin_time: time,
  955. end_time: time,
  956. opinion: '',
  957. });
  958. // 新增2个审批人到审批列表中
  959. insert_audit_array.push({
  960. tid: change.tid,
  961. cpid: change.id,
  962. aid: zsAudit.aid,
  963. times: zsAudit.times,
  964. order: lastAudit.order + 2,
  965. status: auditConst.status.checking,
  966. begin_time: time,
  967. });
  968. await transaction.insert(this.tableName, insert_audit_array);
  969. // 设置变更令审批中
  970. await transaction.update(this.ctx.service.changeProject.tableName, {
  971. id: change.id,
  972. status: auditConst.status.checking,
  973. });
  974. await transaction.commit();
  975. result = true;
  976. } catch (error) {
  977. await transaction.rollback();
  978. result = false;
  979. }
  980. return result;
  981. }
  982. }
  983. return ChangeProjectAudit;
  984. };