quality_inspection_audit.js 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. 'use strict';
  2. /**
  3. * 质量管理 - 巡检单
  4. *
  5. * @author Mai
  6. * @date 2024/7/22
  7. * @version
  8. */
  9. const auditConst = require('../const/audit').inspection;
  10. const pushType = require('../const/audit').pushType;
  11. const auditType = require('../const/audit').auditType;
  12. const shenpiConst = require('../const/shenpi');
  13. module.exports = app => {
  14. class QualityInspectionAudit extends app.BaseService {
  15. /**
  16. * 构造函数
  17. *
  18. * @param {Object} ctx - egg全局变量
  19. * @return {void}
  20. */
  21. constructor(ctx) {
  22. super(ctx);
  23. this.tableName = 'quality_inspection_audit';
  24. }
  25. /**
  26. * 获取 审核列表信息
  27. *
  28. * @param {Number} materialId - 材料调差期id
  29. * @param {Number} times - 第几次审批
  30. * @return {Promise<*>}
  31. */
  32. async getAuditors(inspectionId, times = 1, order_sort = 'asc') {
  33. const sql = 'SELECT la.id, la.tid, la.aid, la.times, la.order, la.status, la.opinion, la.is_rectification, la.is_old, la.begin_time, la.end_time, la.audit_type, la.audit_order,' +
  34. ' pa.name, pa.company, pa.role, pa.mobile, pa.telephone, pa.sign_path' +
  35. ` FROM ${this.tableName} la LEFT JOIN ${this.ctx.service.projectAccount.tableName} pa ON la.aid = pa.id` +
  36. ' WHERE la.qiid = ? AND la.times = ?' +
  37. ' ORDER BY la.order ' + order_sort;
  38. const sqlParam = [inspectionId, times];
  39. const result = await this.db.query(sql, sqlParam);
  40. const max_sort = this._.max(result.map(x => { return x.audit_order; }));
  41. for (const i in result) {
  42. result[i].max_sort = max_sort;
  43. }
  44. return result;
  45. }
  46. async getCurAuditors(inspectionId, times = 1) {
  47. const sql =
  48. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, la.audit_type, la.audit_order ' +
  49. ' FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
  50. ' WHERE la.`qiid` = ? and la.`status` = ? and la.`times` = ?';
  51. const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, inspectionId, auditConst.status.checking, times];
  52. return await this.db.query(sql, sqlParam);
  53. }
  54. /**
  55. * 获取 最新审核顺序
  56. *
  57. * @param {Number} materialId - 材料调差期id
  58. * @param {Number} times - 第几次审批
  59. * @return {Promise<number>}
  60. */
  61. async getNewOrder(inspectionId, times = 1) {
  62. const sql = 'SELECT Max(??) As max_order, Max(audit_order) As max_audit_order FROM ?? Where `qiid` = ? and `times` = ?';
  63. const sqlParam = ['order', this.tableName, inspectionId, times];
  64. const result = await this.db.queryOne(sql, sqlParam);
  65. return result && result.max_order ? [result.max_order + 1, result.max_audit_order + 1] : [1, 1];
  66. }
  67. /**
  68. * 新增审核人
  69. *
  70. * @param {Number} materialId - 材料调差期id
  71. * @param {Number} auditorId - 审核人id
  72. * @param {Number} times - 第几次审批
  73. * @return {Promise<number>}
  74. */
  75. async addAuditor(inspectionId, auditorId, times = 1, is_gdzs = 0) {
  76. const transaction = await this.db.beginTransaction();
  77. try {
  78. let [newOrder, newAuditOrder] = await this.getNewOrder(inspectionId, times);
  79. // 判断是否存在固定终审,存在则newOrder - 1并使终审order+1
  80. newOrder = is_gdzs === 1 ? newOrder - 1 : newOrder;
  81. newAuditOrder = is_gdzs === 1 ? newAuditOrder - 1 : newAuditOrder;
  82. if (is_gdzs) await this._syncOrderByDelete(transaction, inspectionId, newOrder, times, '+');
  83. const data = {
  84. tid: this.ctx.tender.id,
  85. qiid: inspectionId,
  86. aid: auditorId,
  87. times,
  88. order: newOrder,
  89. status: auditConst.status.uncheck,
  90. audit_order: newAuditOrder,
  91. };
  92. const result = await transaction.insert(this.tableName, data);
  93. const auditList = await this.ctx.service.tenderPermission.getPartsPermission(this.ctx.tender.id, ['inspection']);
  94. const addAid = this._.includes(this._.map(auditList, 'uid'), auditorId);
  95. if (!addAid) {
  96. const insert_members = [{
  97. uid: auditorId,
  98. inspection: ['1'],
  99. }];
  100. await this.ctx.service.tenderPermission.saveOnePermission(this.ctx.tender.id, [auditorId], insert_members, ['inspection'], transaction);
  101. }
  102. await transaction.commit();
  103. return (result.effectRows = 1);
  104. } catch (err) {
  105. await transaction.rollback();
  106. throw err;
  107. }
  108. return false;
  109. }
  110. /**
  111. * 移除审核人时,同步其后审核人order
  112. * @param transaction - 事务
  113. * @param {Number} materialId - 材料调差期id
  114. * @param {Number} auditorId - 审核人id
  115. * @param {Number} times - 第几次审批
  116. * @return {Promise<*>}
  117. * @private
  118. */
  119. async _syncOrderByDelete(transaction, inspectionId, order, times, selfOperate = '-') {
  120. this.initSqlBuilder();
  121. this.sqlBuilder.setAndWhere('qiid', {
  122. value: inspectionId,
  123. operate: '=',
  124. });
  125. this.sqlBuilder.setAndWhere('order', {
  126. value: order,
  127. operate: '>=',
  128. });
  129. this.sqlBuilder.setAndWhere('times', {
  130. value: times,
  131. operate: '=',
  132. });
  133. this.sqlBuilder.setUpdateData('order', {
  134. value: 1,
  135. selfOperate,
  136. });
  137. this.sqlBuilder.setUpdateData('audit_order', {
  138. value: 1,
  139. selfOperate,
  140. });
  141. const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
  142. const data = await transaction.query(sql, sqlParam);
  143. return data;
  144. }
  145. /**
  146. * 移除审核人
  147. *
  148. * @param {Number} inspectionId - 质量巡检id
  149. * @param {Number} auditorId - 审核人id
  150. * @param {Number} times - 第几次审批
  151. * @return {Promise<boolean>}
  152. */
  153. async deleteAuditor(inspectionId, auditorId, times = 1) {
  154. const transaction = await this.db.beginTransaction();
  155. try {
  156. const condition = { qiid: inspectionId, aid: auditorId, times };
  157. const auditor = await this.getDataByCondition(condition);
  158. if (!auditor) {
  159. throw '该审核人不存在';
  160. }
  161. await transaction.delete(this.tableName, { qiid: inspectionId, order: auditor.order, times });
  162. await this._syncOrderByDelete(transaction, inspectionId, auditor.order, times);
  163. await transaction.commit();
  164. } catch (err) {
  165. await transaction.rollback();
  166. throw err;
  167. }
  168. return true;
  169. }
  170. /**
  171. * 获取审核人需要审核的期列表
  172. *
  173. * @param auditorId
  174. * @return {Promise<*>}
  175. */
  176. async getAuditInspection(auditorId, spid = '') {
  177. const spSql = spid ? ' and t.`spid` = "' + spid + '"' : '';
  178. const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`qiid`,' +
  179. ' m.`code` As `mcode`, m.`status` As `mstatus`,' +
  180. ' t.`name`, t.`project_id`, t.`type`, t.`user_id`, t.`spid` ' +
  181. ' FROM ?? AS ma' +
  182. ' LEFT JOIN ?? AS m On ma.qiid = m.id' +
  183. ' LEFT JOIN ?? As t On m.tid = t.id' +
  184. ' WHERE ((ma.`aid` = ? and (ma.`status` = ? OR ma.`status` = ?)) OR (m.`uid` = ? and ma.`status` = ? and m.`status` = ? and ma.`times` = (m.`times`-1)))' + spSql +
  185. ' ORDER BY ma.`begin_time` DESC';
  186. const sqlParam = [this.tableName, this.ctx.service.qualityInspection.tableName, this.ctx.service.tender.tableName, auditorId, auditConst.status.checking, auditConst.status.rectification, auditorId, auditConst.status.checkNo, auditConst.status.checkNo];
  187. const result = await this.db.query(sql, sqlParam);
  188. // 过滤result中存在重复sid的值, 保留最新的一条
  189. const filterResult = [];
  190. const qiidArr = [];
  191. for (const r of result) {
  192. if (qiidArr.indexOf(r.qiid) === -1) {
  193. filterResult.push(r);
  194. qiidArr.push(r.qiid);
  195. }
  196. }
  197. return filterResult;
  198. }
  199. /**
  200. * 获取审核人审核的次数
  201. *
  202. * @param auditorId
  203. * @return {Promise<*>}
  204. */
  205. async getCountByChecked(auditorId, spid = '') {
  206. if (spid) {
  207. const sql = 'SELECT count(*) AS count FROM ?? AS a LEFT JOIN ?? AS t ON a.tid = t.id WHERE a.`aid` = ? AND a.`status` in (' + this.ctx.helper.getInArrStrSqlFilter([auditConst.status.checked, auditConst.status.checkNo, auditConst.status.checkNoPre, auditConst.status.checkStop]) + ') AND t.`spid` = ?';
  208. const sqlParam = [this.tableName, this.ctx.service.tender.tableName, auditorId, spid];
  209. const result = await this.db.queryOne(sql, sqlParam);
  210. return result.count ? result.count : 0;
  211. }
  212. return await this.db.count(this.tableName, { aid: auditorId, status: [auditConst.status.checked, auditConst.status.checkNo, auditConst.status.checkNoPre, auditConst.status.checkStop] });
  213. }
  214. /**
  215. * 获取最近一次审批结束时间
  216. *
  217. * @param auditorId
  218. * @return {Promise<*>}
  219. */
  220. async getLastEndTimeByChecked(auditorId, spid = '') {
  221. const sqSql = spid ? ' AND t.`spid` = "' + spid + '"' : '';
  222. const sql = 'SELECT a.`end_time` FROM ?? AS a LEFT JOIN ?? AS t ON a.`tid` = t.`id` WHERE a.`aid` = ? ' +
  223. 'AND a.`status` in (' + this.ctx.helper.getInArrStrSqlFilter([auditConst.status.checked, auditConst.status.checkNo, auditConst.status.checkStop]) + ')' + sqSql +
  224. ' ORDER BY a.`end_time` DESC';
  225. const sqlParam = [this.tableName, this.ctx.service.tender.tableName, auditorId];
  226. const result = await this.db.queryOne(sql, sqlParam);
  227. return result ? result.end_time : null;
  228. }
  229. /**
  230. * 获取审核人流程列表
  231. *
  232. * @param auditorId
  233. * @return {Promise<*>}
  234. */
  235. async getAuditGroupByList(inspectionId, times) {
  236. const sql =
  237. 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`qiid`, la.`order`, la.`status`, la.audit_type, la.audit_order ' +
  238. ' FROM (SELECT `aid`, max(`order`) `order` FROM ?? WHERE `qiid` = ? and `times` = ? and `is_old` = ? and `is_rectification` = ? GROUP BY aid) sa' +
  239. ' LEFT JOIN ?? la ON sa.`aid` = la.`aid` AND sa.`order` = la.`order`' +
  240. ' Left JOIN ?? AS pa On la.`aid` = pa.`id` WHERE la.`qiid` = ? and la.`times` = ? and la.`is_old` = ? and la.`is_rectification` = ? order BY la.`order`';
  241. const sqlParam = [this.tableName, inspectionId, times, 0, 0, this.tableName, this.ctx.service.projectAccount.tableName, inspectionId, times, 0, 0];
  242. return await this.db.query(sql, sqlParam);
  243. }
  244. /**
  245. * 获取审核人流程列表(包括原报)
  246. * @param {Number} materialId 调差id
  247. * @param {Number} times 审核次数
  248. * @return {Promise<Array>} 查询结果集(包括原报)
  249. */
  250. async getAuditorsWithOwner(inspectionId, times = 1) {
  251. const result = await this.getAuditGroupByList(inspectionId, times);
  252. const sql =
  253. 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As qiid, 0 As `order`' +
  254. ' FROM ' +
  255. this.ctx.service.qualityInspection.tableName +
  256. ' As s' +
  257. ' LEFT JOIN ' +
  258. this.ctx.service.projectAccount.tableName +
  259. ' As pa' +
  260. ' ON s.uid = pa.id' +
  261. ' WHERE s.id = ?';
  262. const sqlParam = [times, inspectionId, inspectionId];
  263. const user = await this.db.queryOne(sql, sqlParam);
  264. result.unshift(user);
  265. return result;
  266. }
  267. async getAllAuditors(tenderId) {
  268. const sql = 'SELECT ma.aid, ma.tid FROM ' + this.tableName + ' ma' +
  269. ' LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On ma.tid = t.id' +
  270. ' WHERE t.id = ?' +
  271. ' GROUP BY ma.aid';
  272. const sqlParam = [tenderId];
  273. return this.db.query(sql, sqlParam);
  274. }
  275. async updateNewAuditList(inspection, newList) {
  276. const transaction = await this.db.beginTransaction();
  277. try {
  278. // 先删除旧的审批流,再添加新的
  279. await transaction.delete(this.tableName, { qiid: inspection.id, times: inspection.times });
  280. const newAuditors = [];
  281. for (const auditor of newList) {
  282. newAuditors.push({
  283. tid: inspection.tid, qiid: inspection.id, aid: auditor.audit_id,
  284. times: inspection.times, order: auditor.audit_order, status: auditConst.status.uncheck,
  285. audit_type: auditor.audit_type, audit_order: auditor.audit_order,
  286. });
  287. }
  288. if(newAuditors.length > 0) await transaction.insert(this.tableName, newAuditors);
  289. await transaction.commit();
  290. } catch (err) {
  291. await transaction.rollback();
  292. throw err;
  293. }
  294. }
  295. async updateLastAudit(inspection, auditList, lastId) {
  296. const transaction = await this.db.beginTransaction();
  297. try {
  298. // 先判断auditList里的aid是否与lastId相同,相同则删除并重新更新order
  299. const existAudit = auditList.find(x => { return x.aid === lastId });
  300. let order = auditList.length > 0 ? auditList.reduce((rst, a) => { return Math.max(rst, a.order)}, 0) + 1 : 1; // 最大值 + 1
  301. // const idList = this._.map(auditList, 'aid');
  302. // let order = idList.length + 1;
  303. if (existAudit) {
  304. await transaction.delete(this.tableName, {qiid: inspection.id, times: inspection.times, aid: lastId});
  305. const sameOrder = auditList.filter(x => { return x.order === existAudit.order });
  306. if (sameOrder.length === 1) {
  307. const updateData = [];
  308. auditList.forEach(x => {
  309. if (x.order <= existAudit.order) return;
  310. updateData.push({id: x.id, order: x.order - 1, audit_order: x.audit_order - 1});
  311. });
  312. if (updateData.length > 0) {
  313. await transaction.updateRows(updateData);
  314. }
  315. order = order - 1;
  316. }
  317. }
  318. // 添加终审
  319. const newAuditor = {
  320. tid: inspection.tid, qiid: inspection.id, aid: lastId,
  321. times: inspection.times, order, status: auditConst.status.uncheck,
  322. audit_type: auditType.key.common, audit_order: order,
  323. };
  324. await transaction.insert(this.tableName, newAuditor);
  325. await transaction.commit();
  326. } catch (err) {
  327. await transaction.rollback();
  328. throw err;
  329. }
  330. }
  331. async getAuditorGroup(inspectionId, times) {
  332. const auditors = await this.getAuditors(inspectionId, times); // 全部参与的审批人
  333. const newAuditors = auditors.filter(x => { return x.is_old === 0 && x.is_rectification === 0; });
  334. return this.ctx.helper.groupAuditors(newAuditors);
  335. }
  336. async getUserGroup(inspectionId, times) {
  337. const group = await this.getAuditorGroup(inspectionId, times);
  338. const sql =
  339. 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As qiid, 0 As `order`, 1 As audit_type, 0 As audit_order' +
  340. ' FROM ' + this.ctx.service.qualityInspection.tableName + ' As m' +
  341. ' LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' +
  342. ' ON m.uid = pa.id' +
  343. ' WHERE m.id = ?';
  344. const sqlParam = [times, inspectionId, inspectionId];
  345. const user = await this.db.queryOne(sql, sqlParam);
  346. group.unshift([user]);
  347. return group;
  348. }
  349. async getUniqUserGroup(inspectionId, times) {
  350. const group = await this.getAuditorGroup(inspectionId, times);
  351. const sql =
  352. 'SELECT pa.`id` As aid, pa.`name`, pa.`company`, pa.`role`, ? As times, ? As qiid, 0 As `order`, 1 As audit_type, 0 As audit_order' +
  353. ' FROM ' + this.ctx.service.qualityInspection.tableName + ' As m' +
  354. ' LEFT JOIN ' + this.ctx.service.projectAccount.tableName + ' As pa' +
  355. ' ON m.uid = pa.id' +
  356. ' WHERE m.id = ?';
  357. const sqlParam = [times, inspectionId, inspectionId];
  358. const user = await this.db.queryOne(sql, sqlParam);
  359. user.audit_order = 0;
  360. group.unshift([user]);
  361. return this.ctx.helper.groupAuditorsUniq(group);
  362. }
  363. async getAuditorHistory(inspectionId, times, reverse = false) {
  364. const history = [];
  365. if (times >= 1) {
  366. for (let i = 1; i <= times; i++) {
  367. const auditors = await this.getAuditors(inspectionId, i);
  368. const group = this.ctx.helper.groupAuditors(auditors);
  369. const historyGroup = [];
  370. const max_order = group.length > 0 && group[group.length - 1].length > 0 ? (group[group.length - 1][0].is_rectification && group[group.length - 2].length > 0 ? group[group.length - 2][0].audit_order : group[group.length - 1][0].audit_order) : -1;
  371. for (const g of group) {
  372. const his = {
  373. beginYear: '', beginDate: '', beginTime: '', endYear: '', endDate: '', endTime: '', begin_time: null, end_time: null,
  374. audit_type: g[0].audit_type, audit_order: g[0].audit_order,
  375. auditors: g
  376. };
  377. if (his.audit_type === auditType.key.common) {
  378. his.name = g[0].name;
  379. } else {
  380. his.name = this.ctx.helper.transFormToChinese(his.audit_order) + '审';
  381. }
  382. his.is_rectification = !!g[0].is_rectification;
  383. his.is_final = !his.is_rectification ? his.audit_order === max_order : false;
  384. if (g[0].begin_time) {
  385. his.begin_time = g[0].begin_time;
  386. const beginTime = this.ctx.moment(g[0].begin_time);
  387. his.beginYear = beginTime.format('YYYY');
  388. his.beginDate = beginTime.format('MM-DD');
  389. his.beginTime = beginTime.format('HH:mm:ss');
  390. }
  391. let end_time;
  392. g.forEach(x => {
  393. if (x.status === auditConst.status.checkSkip) return;
  394. if (!his.status || x.status === auditConst.status.checking) his.status = x.status;
  395. if (x.end_time && (!end_time || x.end_time > end_time)) {
  396. end_time = x.end_time;
  397. if (his.status !== auditConst.status.checking) his.status = x.status;
  398. }
  399. });
  400. if (end_time) {
  401. his.end_time = end_time;
  402. const endTime = this.ctx.moment(end_time);
  403. his.endYear = endTime.format('YYYY');
  404. his.endDate = endTime.format('MM-DD');
  405. his.endTime = endTime.format('HH:mm:ss');
  406. }
  407. historyGroup.push(his);
  408. }
  409. if (reverse) {
  410. history.push(historyGroup.reverse());
  411. } else {
  412. history.push(historyGroup);
  413. }
  414. }
  415. }
  416. return history;
  417. }
  418. async getUniqAuditor(inspectionId, times) {
  419. const auditors = await this.getAuditors(inspectionId, times); // 全部参与的审批人
  420. const result = [];
  421. auditors.forEach(x => {
  422. if (result.findIndex(r => { return x.aid === r.aid && x.audit_order === r.audit_order; }) < 0) {
  423. result.push(x);
  424. }
  425. });
  426. return result;
  427. }
  428. /**
  429. * 获取审核人已经审核过的审批信息(包括退回,通过,重新审批等)
  430. *
  431. * @param auditorId
  432. * @return {Promise<*>}
  433. */
  434. async getDonesByAudit(auditorId, spid = '') {
  435. const spSql = spid ? ' and t.`spid` = "' + spid + '"' : '';
  436. const status = [auditConst.status.checked, auditConst.status.checkNo, auditConst.status.checkNoPre, auditConst.status.checkStop];
  437. const sql =
  438. 'SELECT la.`status`, la.`end_time` as `shenpi_time`, la.`qiid`, t.`id`, m.`code` As `code`, t.`name`, t.`spid` ' +
  439. ' FROM ?? AS la Left Join ?? AS t ON la.`tid` = t.`id` LEFT JOIN ?? AS m ON la.`qiid` = m.`id`' +
  440. ' WHERE la.`aid` = ? AND la.`status` in (' + this.ctx.helper.getInArrStrSqlFilter(status) + ')' + spSql +
  441. ' ORDER BY la.`end_time` DESC';
  442. const sqlParam = [
  443. this.tableName,
  444. this.ctx.service.tender.tableName,
  445. this.ctx.service.qualityInspection.tableName,
  446. auditorId,
  447. ];
  448. return await this.db.query(sql, sqlParam);
  449. }
  450. /**
  451. * 复制上一期的审批人列表给最新一期
  452. *
  453. * @param transaction - 新增一期的事务
  454. * @param {Object} preMaterial - 上一期
  455. * @param {Object} newaMaterial - 最新一期
  456. * @return {Promise<*>}
  457. */
  458. async copyPreAuditors(transaction, preInspection, newInspection) {
  459. const auditList = await this.getUniqAuditor(preInspection.id, preInspection.times); // 全部参与的审批人
  460. const newAuditors = [];
  461. for (const x of auditList) {
  462. if (x.audit_order !== 0 && x.is_rectification === 0) {
  463. newAuditors.push({
  464. tid: preInspection.tid, qiid: newInspection.id, aid: x.aid,
  465. times: 1, order: x.audit_order, status: auditConst.status.uncheck,
  466. audit_type: x.audit_type, audit_order: x.audit_order,
  467. });
  468. }
  469. }
  470. const result = newAuditors.length > 0 ? await transaction.insert(this.tableName, newAuditors) : null;
  471. return result ? result.affectedRows === newAuditors.length : true;
  472. }
  473. /**
  474. * 开始审批
  475. * @param {Number} cpId - 方案id
  476. * @param {Number} times - 第几次审批
  477. * @return {Promise<boolean>}
  478. */
  479. async start(qiid, times = 1) {
  480. const audits = await this.getAllDataByCondition({ where: { qiid, times, order: 1 } });
  481. if (audits.length === 0) {
  482. if (this.ctx.tender.info.shenpi.inspection === shenpiConst.sp_status.gdspl) {
  483. throw '请联系管理员添加审批人';
  484. } else {
  485. throw '请先选择审批人,再上报数据';
  486. }
  487. }
  488. const transaction = await this.db.beginTransaction();
  489. try {
  490. const begin_time = new Date();
  491. const updateData = audits.map(x => { return { id: x.id, status: auditConst.status.checking, begin_time }; });
  492. await transaction.updateRows(this.tableName, updateData);
  493. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  494. id: qiid, status: auditConst.status.checking,
  495. });
  496. // 微信模板通知
  497. // const shenpiUrl = await this.ctx.helper.urlToShort(
  498. // this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/plan/' + cpId + '/information#shenpi'
  499. // );
  500. // const wechatData = {
  501. // type: 'plan',
  502. // wap_url: shenpiUrl,
  503. // status: wxConst.status.check,
  504. // tips: wxConst.tips.check,
  505. // code: this.ctx.session.sessionProject.code,
  506. // c_name: this.ctx.change.name,
  507. // };
  508. // await this.ctx.helper.sendWechat(this._.map(audits, 'aid'), smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
  509. // for (const audit of audits) {
  510. // await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.BG, {
  511. // pid: this.ctx.session.sessionProject.id,
  512. // tid: this.ctx.tender.id,
  513. // uid: audit.aid,
  514. // sp_type: 'change',
  515. // sp_id: audit.id,
  516. // table_name: this.tableName,
  517. // template: wxConst.template.change,
  518. // wx_data: wechatData,
  519. // });
  520. // }
  521. // todo 更新标段tender状态 ?
  522. await transaction.commit();
  523. } catch (err) {
  524. await transaction.rollback();
  525. throw err;
  526. }
  527. return true;
  528. }
  529. /**
  530. * 审批
  531. * @param {Object} inspection - 质量巡检对象
  532. * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果
  533. * @return {Promise<void>}
  534. */
  535. async check(inspection, checkData) {
  536. if (!this._.includes([auditConst.status.checked, auditConst.status.checkNoPre, auditConst.status.checkNo, auditConst.status.checkStop], checkData.checkType)) {
  537. throw '提交数据错误';
  538. }
  539. const pid = this.ctx.session.sessionProject.id;
  540. switch (checkData.checkType) {
  541. case auditConst.status.checked:
  542. await this._checked(pid, inspection, checkData);
  543. break;
  544. case auditConst.status.checkNoPre:
  545. await this._checkNoPre(pid, inspection, checkData);
  546. break;
  547. case auditConst.status.checkNo:
  548. await this._checkNo(pid, inspection, checkData);
  549. break;
  550. case auditConst.status.checkStop:
  551. await this._checkStop(pid, inspection, checkData);
  552. break;
  553. default:
  554. throw '无效审批操作';
  555. }
  556. }
  557. async _checked(pid, inspection, checkData) {
  558. const accountId = this.ctx.session.sessionUser.accountId;
  559. const time = new Date();
  560. // 整理当前流程审核人状态更新
  561. if (inspection.curAuditorIds.length === 0) throw '审核数据错误';
  562. if (!this._.includes(inspection.curAuditorIds, accountId)) throw '当前标段您无权审批';
  563. const flowAudits = inspection.flowAuditors;
  564. const selfAudit = this._.find(flowAudits, { aid: accountId });
  565. const nextAudits = inspection.nextAuditors;
  566. const transaction = await this.db.beginTransaction();
  567. try {
  568. // 更新本人审批状态
  569. await transaction.update(this.tableName, {
  570. id: selfAudit.id,
  571. status: checkData.checkType,
  572. opinion: checkData.opinion,
  573. end_time: time,
  574. });
  575. // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, selfAudit.id);
  576. // 获取推送必要信息
  577. const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, inspection.id, selfAudit.aid, checkData.opinion);
  578. // 添加推送
  579. // const records = [{ pid, type: pushType.changeProject, uid: this.ctx.change.uid, status: auditConst.status.checked, content: noticeContent }];
  580. const records = [];
  581. const auditors = await this.getAuditorsWithOwner(inspection.id, inspection.times);
  582. auditors.forEach(audit => {
  583. records.push({ pid, tid: selfAudit.tid, type: pushType.inspection, uid: audit.aid, status: auditConst.status.checked, content: noticeContent });
  584. });
  585. await transaction.insert('zh_notice', records);
  586. if (flowAudits.length === 1 || selfAudit.audit_type !== auditType.key.and) {
  587. // 或签更新他人审批状态
  588. if (selfAudit.audit_type === auditType.key.or) {
  589. const updateOther = [];
  590. for (const audit of flowAudits) {
  591. if (audit.aid === selfAudit.aid) continue;
  592. updateOther.push({
  593. id: audit.id,
  594. status: auditConst.status.checkSkip,
  595. opinion: '',
  596. end_time: time,
  597. });
  598. // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, audit.id);
  599. }
  600. if (updateOther.length > 0) transaction.updateRows(this.tableName, updateOther);
  601. }
  602. // 无下一审核人表示,审核结束
  603. if (nextAudits.length > 0) {
  604. // 流程至下一审批人
  605. const updateData = nextAudits.map(x => { return { id: x.id, status: auditConst.status.checking, begin_time: time }; });
  606. await transaction.updateRows(this.tableName, updateData);
  607. // 同步 期信息
  608. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  609. id: inspection.id, status: auditConst.status.checking,
  610. });
  611. // 微信模板通知
  612. // const shenpiUrl = await this.ctx.helper.urlToShort(
  613. // this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/project/' + cpId + '/information#shenpi'
  614. // );
  615. // const wechatData = {
  616. // type: 'project',
  617. // wap_url: shenpiUrl,
  618. // status: wxConst.status.check,
  619. // tips: wxConst.tips.check,
  620. // code: this.ctx.session.sessionProject.code,
  621. // c_name: this.ctx.change.name,
  622. // };
  623. // await this.ctx.helper.sendWechat(this._.map(nextAudits, 'aid'), smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
  624. // for (const nextAudit of nextAudits) {
  625. // await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.BG, {
  626. // pid: this.ctx.session.sessionProject.id,
  627. // tid: this.ctx.tender.id,
  628. // uid: nextAudit.aid,
  629. // sp_type: 'change',
  630. // sp_id: nextAudit.id,
  631. // table_name: this.tableName,
  632. // template: wxConst.template.change,
  633. // wx_data: wechatData,
  634. // });
  635. // }
  636. } else {
  637. // 本期结束
  638. // 生成截止本期数据 final数据
  639. // 同步 期信息
  640. if (!checkData.rectification_uid) throw '请指定整改人';
  641. await transaction.insert(this.tableName, {
  642. tid: inspection.tid, qiid: inspection.id, aid: checkData.rectification_uid,
  643. times: inspection.times, order: selfAudit.order + 1, status: auditConst.status.rectification,
  644. is_rectification: 1, begin_time: time, audit_order: selfAudit.audit_order + 1,
  645. });
  646. const auditList = await this.ctx.service.tenderPermission.getPartsPermission(inspection.tid, ['inspection']);
  647. const addAid = this._.includes(this._.map(auditList, 'uid'), checkData.rectification_uid);
  648. if (!addAid) {
  649. const insert_members = [{
  650. uid: checkData.rectification_uid,
  651. inspection: ['1'],
  652. }];
  653. await this.ctx.service.tenderPermission.saveOnePermission(inspection.tid, [checkData.rectification_uid], insert_members, ['inspection'], transaction);
  654. }
  655. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  656. id: inspection.id, status: auditConst.status.rectification, rectification_uid: checkData.rectification_uid,
  657. });
  658. // 微信模板通知
  659. // const users = this._.uniq(this._.map(auditors, 'aid'));
  660. // const shenpiUrl = await this.ctx.helper.urlToShort(
  661. // this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/project/' + cpId + '/information#shenpi'
  662. // );
  663. // const wechatData = {
  664. // type: 'project',
  665. // wap_url: shenpiUrl,
  666. // status: wxConst.status.success,
  667. // tips: wxConst.tips.success,
  668. // code: this.ctx.session.sessionProject.code,
  669. // c_name: this.ctx.change.name,
  670. // };
  671. // await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
  672. }
  673. } else {
  674. // 同步 期信息
  675. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  676. id: inspection.id,
  677. status: auditConst.status.checking,
  678. rectification_uid: checkData.rectification_uid || null,
  679. });
  680. }
  681. await transaction.commit();
  682. } catch (err) {
  683. await transaction.rollback();
  684. throw err;
  685. }
  686. }
  687. async _checkNo(pid, inspection, checkData) {
  688. const accountId = this.ctx.session.sessionUser.accountId;
  689. const time = new Date();
  690. // 整理当前流程审核人状态更新
  691. const audits = inspection.curAuditors;
  692. if (audits.length === 0) throw '审核数据错误';
  693. const selfAudit = audits.find(x => { return x.aid === accountId; });
  694. if (!selfAudit) throw '当前标段您无权审批';
  695. const auditors = await this.getUniqAuditor(inspection.id, inspection.times); // 全部参与的审批人
  696. const newAuditors = this._.filter(auditors, { is_rectification: 0 }).map(x => {
  697. return {
  698. aid: x.aid, tid: inspection.tid, qiid: inspection.id,
  699. times: inspection.times + 1, order: x.audit_order, status: auditConst.status.uncheck,
  700. audit_type: x.audit_type, audit_order: x.audit_order,
  701. };
  702. });
  703. const transaction = await this.db.beginTransaction();
  704. try {
  705. const updateData = [];
  706. audits.forEach(x => {
  707. updateData.push({
  708. id: x.id,
  709. status: x.aid === selfAudit.aid ? checkData.checkType : auditConst.status.checkSkip,
  710. opinion: x.aid === selfAudit.aid ? checkData.opinion : '',
  711. end_time: x.aid === selfAudit.aid ? time : null,
  712. });
  713. });
  714. await transaction.updateRows(this.tableName, updateData);
  715. // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
  716. // 添加到消息推送表
  717. const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, inspection.id, selfAudit.aid, checkData.opinion);
  718. const records = [{ pid, tid: selfAudit.tid, type: pushType.inspection, uid: inspection.uid, status: auditConst.status.checkNoPre, content: noticeContent }];
  719. auditors.forEach(audit => {
  720. records.push({ pid, tid: selfAudit.tid, type: pushType.inspection, uid: audit.aid, status: auditConst.status.checkNoPre, content: noticeContent });
  721. });
  722. await transaction.insert(this.ctx.service.noticePush.tableName, records);
  723. // 同步期信息
  724. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  725. id: inspection.id, status: checkData.checkType,
  726. times: inspection.times + 1,
  727. rectification_uid: null,
  728. });
  729. // 拷贝新一次审核流程列表
  730. await transaction.insert(this.tableName, newAuditors);
  731. // 微信模板通知
  732. // const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), this.ctx.change.uid));
  733. // const shenpiUrl = await this.ctx.helper.urlToShort(
  734. // this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/project/' + cpId + '/information#shenpi'
  735. // );
  736. // const wechatData = {
  737. // type: 'project',
  738. // wap_url: shenpiUrl,
  739. // status: wxConst.status.back,
  740. // tips: wxConst.tips.back,
  741. // code: this.ctx.session.sessionProject.code,
  742. // c_name: this.ctx.change.name,
  743. // };
  744. // await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
  745. await transaction.commit();
  746. } catch (err) {
  747. await transaction.rollback();
  748. throw err;
  749. }
  750. }
  751. async _checkNoPre(pid, inspection, checkData) {
  752. const accountId = this.ctx.session.sessionUser.accountId;
  753. const time = new Date();
  754. // 整理当前流程审核人状态更新
  755. const audits = inspection.curAuditors;
  756. if (audits.length === 0 || audits[0].order <= 1) throw '审核数据错误';
  757. const selfAudit = audits.find(x => { return x.aid === accountId; });
  758. if (!selfAudit) throw '当前标段您无权审批';
  759. const flowAudits = inspection.flowAuditors;
  760. // 添加重新审批后,不能用order-1,取groupby值里的上一个才对
  761. // const preAuditor = await this.getDataByCondition({sid: stageId, times: times, order: audit.order - 1});
  762. const auditors2 = await this.getAuditGroupByList(inspection.id, inspection.times);
  763. const preAuditors = auditors2.filter(x => { return x.audit_order === selfAudit.audit_order - 1});
  764. const transaction = await this.db.beginTransaction();
  765. try {
  766. // 添加通知
  767. const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, inspection.id, selfAudit.aid, checkData.opinion);
  768. const defaultNoticeRecord = {
  769. pid,
  770. tid: selfAudit.tid,
  771. type: pushType.inspection,
  772. status: auditConst.status.checkNoPre,
  773. content: noticeContent,
  774. };
  775. const records = [
  776. {
  777. uid: inspection.uid,
  778. ...defaultNoticeRecord
  779. },
  780. ];
  781. auditors2.forEach(audit => {
  782. records.push({
  783. uid: audit.aid,
  784. ...defaultNoticeRecord
  785. });
  786. });
  787. await transaction.insert('zh_notice', records);
  788. // 更新同一流程所有审批人状态
  789. const updateData = [];
  790. for (const audit of audits) {
  791. if (audit.aid === selfAudit.aid) {
  792. updateData.push({
  793. id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time,
  794. });
  795. } else {
  796. updateData.push({
  797. id: audit.id, status: auditConst.status.checkSkip, opinion: '', end_time: null,
  798. });
  799. }
  800. }
  801. await transaction.updateRows(this.tableName, updateData);
  802. // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
  803. // 顺移其后审核人流程顺序
  804. const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE qiid = ? AND times = ? AND `order` > ?';
  805. await transaction.query(sql, [inspection.id, selfAudit.times, selfAudit.order]);
  806. // 上一审批人,当前审批人 再次添加至流程
  807. const newAuditors = [];
  808. preAuditors.forEach(x => {
  809. newAuditors.push({
  810. tid: inspection.tid, qiid: inspection.id, aid: x.aid,
  811. times: x.times, order: selfAudit.order + 1,
  812. status: auditConst.status.checking, begin_time: time,
  813. audit_type: x.audit_type, audit_order: x.audit_order,
  814. });
  815. });
  816. // 获取ids值
  817. const newAuditors_result = await transaction.insert(this.tableName, newAuditors);
  818. // 获取刚批量添加的所有list
  819. for (let j = 0; j < preAuditors.length; j++) {
  820. newAuditors[j].id = newAuditors_result.insertId + j;
  821. }
  822. const newFlowAuditors = [];
  823. flowAudits.forEach(x => {
  824. newFlowAuditors.push({
  825. tid: inspection.tid, qiid: inspection.id, aid: x.aid,
  826. times: x.times, order: selfAudit.order + 2,
  827. status: auditConst.status.uncheck,
  828. audit_type: x.audit_type, audit_order: x.audit_order,
  829. });
  830. });
  831. await transaction.insert(this.tableName, newFlowAuditors);
  832. // 同步 期信息
  833. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  834. id: inspection.id,
  835. status: checkData.checkType,
  836. rectification_uid: null,
  837. });
  838. // const preAuditorIds = preAuditors.map(x => { return x.aid; });
  839. // const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/stage/' + stageInfo.order);
  840. // const users = this._.map(this.ctx.stage.auditAssists.filter(x => {return preAuditorIds.indexOf(x.user_id) >= 0 }), 'ass_user_id');
  841. // users.push(...preAuditorIds);
  842. // await this.ctx.helper.sendAliSms(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  843. // qi: stageInfo.order,
  844. // code: shenpiUrl,
  845. // });
  846. // // 微信模板通知
  847. // const wechatData = {
  848. // wap_url: shenpiUrl,
  849. // qi: stageInfo.order,
  850. // status: wxConst.status.check,
  851. // tips: wxConst.tips.check,
  852. // code: this.ctx.session.sessionProject.code,
  853. // };
  854. // await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  855. // 重新发送配置
  856. // for (const a of newAuditors) {
  857. // await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JL, {
  858. // pid: this.ctx.session.sessionProject.id,
  859. // tid: this.ctx.tender.id,
  860. // uid: a.aid,
  861. // sp_type: 'stage',
  862. // sp_id: a.id,
  863. // table_name: this.tableName,
  864. // template: wxConst.template.stage,
  865. // wx_data: wechatData,
  866. // });
  867. // }
  868. await transaction.commit();
  869. } catch (err) {
  870. await transaction.rollback();
  871. throw err;
  872. }
  873. }
  874. async _checkStop(pid, inspection, checkData) {
  875. const accountId = this.ctx.session.sessionUser.accountId;
  876. const time = new Date();
  877. // 整理当前流程审核人状态更新
  878. const audits = inspection.curAuditors;
  879. if (!audits) throw '审核数据错误';
  880. const selfAudit = audits.find(x => { return x.aid === accountId; });
  881. if (!selfAudit) throw '当前标段您无权终止';
  882. const auditors = await this.getUniqAuditor(inspection.id, inspection.times); // 全部参与的审批人
  883. const transaction = await this.db.beginTransaction();
  884. try {
  885. const updateData = [];
  886. audits.forEach(x => {
  887. updateData.push({
  888. id: x.id,
  889. status: x.aid === selfAudit.aid ? checkData.checkType : auditConst.status.checkSkip,
  890. opinion: x.aid === selfAudit.aid ? checkData.opinion : '',
  891. end_time: x.aid === selfAudit.aid ? time : null,
  892. });
  893. });
  894. await transaction.updateRows(this.tableName, updateData);
  895. // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
  896. // 获取推送必要信息
  897. const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, inspection.id, selfAudit.aid, checkData.opinion);
  898. // 添加推送
  899. const records = [{ pid, tid: selfAudit.tid, type: pushType.inspection, uid: inspection.uid, status: auditConst.status.checkStop, content: noticeContent }];
  900. auditors.forEach(audit => {
  901. records.push({ pid, tid: selfAudit.tid, type: pushType.inspection, uid: audit.aid, status: auditConst.status.checkStop, content: noticeContent });
  902. });
  903. await transaction.insert('zh_notice', records);
  904. // 本期结束
  905. // 生成截止本期数据 final数据
  906. // 同步 期信息
  907. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  908. id: inspection.id, status: checkData.checkType,
  909. });
  910. // 微信模板通知
  911. // const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), this.ctx.change.uid));
  912. // const shenpiUrl = await this.ctx.helper.urlToShort(
  913. // this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/project/' + cpId + '/information#shenpi'
  914. // );
  915. // const wechatData = {
  916. // type: 'project',
  917. // wap_url: shenpiUrl,
  918. // status: wxConst.status.stop,
  919. // tips: wxConst.tips.stop,
  920. // code: this.ctx.session.sessionProject.code,
  921. // c_name: this.ctx.change.name,
  922. // };
  923. // await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
  924. await transaction.commit();
  925. } catch (err) {
  926. await transaction.rollback();
  927. throw err;
  928. }
  929. }
  930. /**
  931. * 审批
  932. * @param {Object} inspection - 质量巡检对象
  933. * @param {auditConst.status.checked|auditConst.status.checkNo} checkType - 审批结果
  934. * @return {Promise<void>}
  935. */
  936. async rectification(inspection, checkData) {
  937. if (!this._.includes([auditConst.status.checked, auditConst.status.checkNoPre], checkData.checkType)) {
  938. throw '提交数据错误';
  939. }
  940. const pid = this.ctx.session.sessionProject.id;
  941. switch (checkData.checkType) {
  942. case auditConst.status.checked:
  943. await this._rectificationChecked(pid, inspection, checkData);
  944. break;
  945. case auditConst.status.checkNoPre:
  946. await this._rectificationCheckNoPre(pid, inspection, checkData);
  947. break;
  948. default:
  949. throw '无效审批操作';
  950. }
  951. }
  952. async _rectificationChecked(pid, inspection, checkData) {
  953. const accountId = this.ctx.session.sessionUser.accountId;
  954. const time = new Date();
  955. // 整理当前流程审核人状态更新
  956. if (inspection.curAuditorIds.length === 0) throw '审核数据错误';
  957. if (!this._.includes(inspection.curAuditorIds, accountId)) throw '当前标段您无权审批';
  958. const flowAudits = inspection.flowAuditors;
  959. const selfAudit = this._.find(flowAudits, { aid: accountId });
  960. const transaction = await this.db.beginTransaction();
  961. try {
  962. // 更新本人审批状态
  963. await transaction.update(this.tableName, {
  964. id: selfAudit.id,
  965. status: checkData.checkType,
  966. opinion: checkData.opinion,
  967. end_time: time,
  968. });
  969. // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, selfAudit.id);
  970. // 获取推送必要信息
  971. const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, inspection.id, selfAudit.aid, checkData.opinion);
  972. // 添加推送
  973. // const records = [{ pid, type: pushType.changeProject, uid: this.ctx.change.uid, status: auditConst.status.checked, content: noticeContent }];
  974. const records = [];
  975. const auditors = await this.getAuditorsWithOwner(inspection.id, inspection.times);
  976. auditors.forEach(audit => {
  977. records.push({ pid, tid: selfAudit.tid, type: pushType.inspection, uid: audit.aid, status: auditConst.status.checked, content: noticeContent });
  978. });
  979. await transaction.insert('zh_notice', records);
  980. // 本期结束
  981. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  982. id: inspection.id, status: auditConst.status.checked,
  983. });
  984. // 微信模板通知
  985. // const users = this._.uniq(this._.map(auditors, 'aid'));
  986. // const shenpiUrl = await this.ctx.helper.urlToShort(
  987. // this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/change/project/' + cpId + '/information#shenpi'
  988. // );
  989. // const wechatData = {
  990. // type: 'project',
  991. // wap_url: shenpiUrl,
  992. // status: wxConst.status.success,
  993. // tips: wxConst.tips.success,
  994. // code: this.ctx.session.sessionProject.code,
  995. // c_name: this.ctx.change.name,
  996. // };
  997. // await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
  998. await transaction.commit();
  999. } catch (err) {
  1000. await transaction.rollback();
  1001. throw err;
  1002. }
  1003. }
  1004. async _rectificationCheckNoPre(pid, inspection, checkData) {
  1005. const accountId = this.ctx.session.sessionUser.accountId;
  1006. const time = new Date();
  1007. // 整理当前流程审核人状态更新
  1008. const audits = inspection.curAuditors;
  1009. if (audits.length === 0 || audits[0].order <= 1) throw '审核数据错误';
  1010. const selfAudit = audits.find(x => { return x.aid === accountId; });
  1011. if (!selfAudit) throw '当前标段您无权审批';
  1012. const auditors2 = await this.getAuditGroupByList(inspection.id, inspection.times);
  1013. const preAuditors = auditors2.filter(x => { return x.audit_order === selfAudit.audit_order - 1});
  1014. const transaction = await this.db.beginTransaction();
  1015. try {
  1016. // 添加通知
  1017. const noticeContent = await this.getNoticeContent(pid, selfAudit.tid, inspection.id, selfAudit.aid, checkData.opinion);
  1018. const defaultNoticeRecord = {
  1019. pid,
  1020. tid: selfAudit.tid,
  1021. type: pushType.inspection,
  1022. status: auditConst.status.checkNoPre,
  1023. content: noticeContent,
  1024. };
  1025. const records = [
  1026. {
  1027. uid: inspection.uid,
  1028. ...defaultNoticeRecord
  1029. },
  1030. ];
  1031. auditors2.forEach(audit => {
  1032. records.push({
  1033. uid: audit.aid,
  1034. ...defaultNoticeRecord
  1035. });
  1036. });
  1037. await transaction.insert('zh_notice', records);
  1038. // 更新同一流程所有审批人状态
  1039. const updateData = [];
  1040. for (const audit of audits) {
  1041. if (audit.aid === selfAudit.aid) {
  1042. updateData.push({
  1043. id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time,
  1044. });
  1045. }
  1046. }
  1047. await transaction.updateRows(this.tableName, updateData);
  1048. // await this.ctx.service.noticeAgain.stopNoticeAgain(transaction, this.tableName, this._.map(updateData, 'id'));
  1049. // 上一审批人,当前审批人 再次添加至流程
  1050. const newAuditors = [];
  1051. preAuditors.forEach(x => {
  1052. newAuditors.push({
  1053. tid: inspection.tid, qiid: inspection.id, aid: x.aid,
  1054. times: x.times, order: selfAudit.order + 1,
  1055. status: auditConst.status.checking, begin_time: time,
  1056. audit_type: x.audit_type, audit_order: x.audit_order,
  1057. });
  1058. });
  1059. // 获取ids值
  1060. const newAuditors_result = await transaction.insert(this.tableName, newAuditors);
  1061. // 同步 期信息
  1062. await transaction.update(this.ctx.service.qualityInspection.tableName, {
  1063. id: inspection.id,
  1064. status: checkData.checkType,
  1065. rectification_item: '',
  1066. rectification_date: null,
  1067. });
  1068. // const preAuditorIds = preAuditors.map(x => { return x.aid; });
  1069. // const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/stage/' + stageInfo.order);
  1070. // const users = this._.map(this.ctx.stage.auditAssists.filter(x => {return preAuditorIds.indexOf(x.user_id) >= 0 }), 'ass_user_id');
  1071. // users.push(...preAuditorIds);
  1072. // await this.ctx.helper.sendAliSms(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), SmsAliConst.template.stage_check, {
  1073. // qi: stageInfo.order,
  1074. // code: shenpiUrl,
  1075. // });
  1076. // // 微信模板通知
  1077. // const wechatData = {
  1078. // wap_url: shenpiUrl,
  1079. // qi: stageInfo.order,
  1080. // status: wxConst.status.check,
  1081. // tips: wxConst.tips.check,
  1082. // code: this.ctx.session.sessionProject.code,
  1083. // };
  1084. // await this.ctx.helper.sendWechat(users, smsTypeConst.const.JL, smsTypeConst.judge.approval.toString(), wxConst.template.stage, wechatData);
  1085. // 重新发送配置
  1086. // for (const a of newAuditors) {
  1087. // await this.ctx.service.noticeAgain.addNoticeAgain(transaction, smsTypeConst.const.JL, {
  1088. // pid: this.ctx.session.sessionProject.id,
  1089. // tid: this.ctx.tender.id,
  1090. // uid: a.aid,
  1091. // sp_type: 'stage',
  1092. // sp_id: a.id,
  1093. // table_name: this.tableName,
  1094. // template: wxConst.template.stage,
  1095. // wx_data: wechatData,
  1096. // });
  1097. // }
  1098. await transaction.commit();
  1099. } catch (err) {
  1100. await transaction.rollback();
  1101. throw err;
  1102. }
  1103. }
  1104. /**
  1105. * 用于添加推送所需的content内容
  1106. * @param {Number} pid 项目id
  1107. * @param {Number} tid 台账id
  1108. * @param {Number} cpId 方案id
  1109. * @param {Number} uid 审批人id
  1110. */
  1111. async getNoticeContent(pid, tid, qiid, uid, opinion = '') {
  1112. const noticeSql = 'SELECT * FROM (SELECT ' +
  1113. ' t.`id` As `tid`, t.`spid` As `spid`, ma.`qiid`, m.`code` as `c_code`, t.`name`, pa.`name` As `su_name`, pa.role As `su_role`' +
  1114. ' FROM (SELECT * FROM ?? WHERE `id` = ? ) As t' +
  1115. ' LEFT JOIN ?? As m On t.`id` = m.`tid` AND m.`id` = ?' +
  1116. ' LEFT JOIN ?? As ma ON m.`id` = ma.`qiid`' +
  1117. ' LEFT JOIN ?? As pa ON pa.`id` = ?' +
  1118. ' WHERE t.`project_id` = ? ) as new_t GROUP BY new_t.`tid`';
  1119. const noticeSqlParam = [this.ctx.service.tender.tableName, tid, this.ctx.service.qualityInspection.tableName, qiid, this.tableName, this.ctx.service.projectAccount.tableName, uid, pid];
  1120. const content = await this.db.query(noticeSql, noticeSqlParam);
  1121. if (content.length) {
  1122. content[0].opinion = opinion;
  1123. }
  1124. return content.length ? JSON.stringify(content[0]) : '';
  1125. }
  1126. }
  1127. return QualityInspectionAudit;
  1128. };