safe_inspection_audit.js 64 KB

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