ScheduleController.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. package com.repair.controller;
  2. import com.repair.common.utils.*;
  3. import com.repair.config.WxOpenidConfig;
  4. import com.repair.model.pojo.*;
  5. import com.repair.model.vo.*;
  6. import com.repair.services.*;
  7. import org.apache.commons.lang3.StringUtils;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.scheduling.annotation.Async;
  10. import org.springframework.scheduling.annotation.Scheduled;
  11. import org.springframework.stereotype.Component;
  12. import org.springframework.transaction.annotation.Transactional;
  13. import java.math.BigDecimal;
  14. import java.text.SimpleDateFormat;
  15. import java.util.*;
  16. import java.util.stream.Collectors;
  17. @Component
  18. //@EnableAsync
  19. public class ScheduleController {
  20. /**
  21. * @Scheduled注解会在默认情况下以单线程的方式执行定时任务。 这个“单线程”指两个方面:
  22. * 如果一个定时任务执行时间大于其任务间隔时间,那么下一次将会等待上一次执行结束后再继续执行。
  23. * 如果多个定时任务在同一时刻执行,任务会依次执行。
  24. * @Async:对某个方法进行异步执行
  25. * @EnableAsync:开启异步支持
  26. */
  27. @Autowired
  28. private WxOpenidConfig wxOpenidConfig;
  29. @Autowired
  30. private RepairUserService repairUserService;
  31. @Autowired
  32. private RepairRecordService repairRecordService;
  33. @Autowired
  34. private RepairEvaluateService repairEvaluateService;
  35. @Autowired
  36. private RepairTrackRecordService repairTrackRecordService;
  37. @Autowired
  38. private RepairArticleBuildService repairArticleBuildService;
  39. @Autowired
  40. private RepairRefundRecordService repairRefundRecordService;
  41. @Autowired
  42. private RepairShiftSettingsService repairShiftSettingsService;
  43. @Autowired
  44. private RepairSystemSettingService repairSystemSettingService;
  45. @Autowired
  46. private RepairSystemMessagesService repairSystemMessagesService;
  47. @Autowired
  48. private RepairDispatchRecordService repairDispatchRecordService;
  49. //定时格式参考:https://blog.csdn.net/java13992394428/article/details/108740453
  50. /**
  51. * 每天八点自动派单
  52. * 周六周日定时器不生效
  53. * 其余时间都是早上八点前捞一遍当前时间以前的单子
  54. * 有合适的维修师傅就把单子派给维修师傅
  55. * 周一到周五早上七点执行:0 0 7 ? * MON-FRI
  56. * 周一到周五1、2、3、4、5、6、7、18、19、20、21、22点执行:0 0 1,2,3,4,5,6,7,18,19,20,21,22 ? * MON-FRI
  57. */
  58. //每小时执行一次:0 0 */1 * * ?
  59. //每分钟执行一次:0 */1 * * * ?
  60. @Async
  61. @Scheduled(cron = "0 0 7 ? * MON-FRI ")
  62. //@Scheduled(cron = "0 */1 * * * ? ")
  63. @Transactional(rollbackFor = {Exception.class})
  64. public void autoDispatch() throws Exception {
  65. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":自动派单进来执行了一次!");
  66. /**
  67. * 读取未被派单的数据
  68. * 拿到当前时间为止的所有未被派单的数据
  69. */
  70. List<pendingOrderVo> records = repairRecordService.queryPendingOrder();
  71. //有待接单数据
  72. if (records.size() >= 0) {
  73. Date nowDate = new Date();
  74. String dateHour = new SimpleDateFormat("HH:mm").format(nowDate);
  75. String dateNow = new SimpleDateFormat("yyyy-MM-dd").format(nowDate);
  76. /**
  77. * 只获取当天排班和能接单的
  78. * 获取维修师傅和郭班长的单子
  79. */
  80. List<AutoDispatchUserVo> users = repairUserService.autoDispatchUser(dateNow);
  81. String shiftStr = StringUtils.join(users.stream().map(AutoDispatchUserVo::getShiftId).collect(Collectors.toList()), ",");
  82. List<String> shifts = Arrays.asList(shiftStr.split(",")).stream().distinct().collect(Collectors.toList());
  83. //只获取不是值班 或下班后的排班数据
  84. List<RepairShiftSettings> shiftDatas = repairShiftSettingsService.getRepairsShiftIdByHour(shifts, dateHour);
  85. //2023-11-03 A-jax 添加获取报修关联楼栋
  86. List<ArticleBuildVo> articleBuilds = new ArrayList<>();
  87. if(users != null && users.size() > 0){
  88. List<Integer> userIds = users.stream().map(AutoDispatchUserVo::getId).collect(Collectors.toList());
  89. articleBuilds = repairArticleBuildService.queryArticleBuild(StringUtils.join(userIds,","));
  90. }
  91. List<RepairDispatchRecord> rdrs = new ArrayList<>();
  92. List<RepairSystemMessages> rsms = new ArrayList<>();
  93. List<RepairTrackRecord> rtrs = new ArrayList<>();
  94. for (pendingOrderVo record : records) {
  95. //跟张总确认一下是不是到时候维修师傅会把所有楼栋都勾选上
  96. List<AutoDispatchUserVo> schoolUsers = new ArrayList<>();
  97. for (AutoDispatchUserVo user:users) {
  98. long owner = articleBuilds != null && articleBuilds.size() > 0 ? articleBuilds.stream().filter(e -> e.getUserId() == user.getId() && ("," + e.getArticleId() + ",").contains("," + record.getArticleId() + ",") && ("," + e.getBuildId() + ",").contains("," + record.getBuildId() + ",")).count() : 0;
  99. if(owner > 0){
  100. schoolUsers.add(user);
  101. }
  102. }
  103. schoolUsers.sort(Comparator.comparing(AutoDispatchUserVo::getRdrCount));
  104. for (AutoDispatchUserVo user : schoolUsers) {
  105. List<RepairShiftSettings> datas = shiftDatas.stream().filter(e -> ("," + user.getShiftId() + ",").contains("," + e.getId() + ",")).collect(Collectors.toList());
  106. if (datas.size() <= 0) {
  107. continue;
  108. }
  109. Integer isDuty = 0;
  110. int acceptanceAssessTime = user.getAcceptanceTime();
  111. try {
  112. //2023-10-08 看是否是值班时间接单
  113. if (shiftDatas.size() > 0) {
  114. Optional<RepairShiftSettings> shiftData = datas.stream().filter(e -> nowDate.before(TimeExchange.StringToDate(e.getEndTime(), "HH:mm"))).sorted(Comparator.comparing(RepairShiftSettings::getStartTime)).findFirst();
  115. if (shiftData != null && shiftData.isPresent()) {
  116. if (shiftData.get().getIsDuty().intValue() == 1) {
  117. isDuty = 1;
  118. }
  119. }
  120. }
  121. //下班之后或者值班接的单不考核 将报修工单中的is_duty改为1
  122. Date workTime = TimeExchange.StringToDate(dateNow + " " + datas.get(0).getEndTime() + ":00", "yyyy-MM-dd HH:mm:ss");
  123. if (workTime.before(nowDate)) {
  124. isDuty = 1;
  125. }
  126. if (isDuty == 0) {
  127. //在工作时间才去派单
  128. List<timeVo> tvs = DelayedUtils.timeVos(datas);
  129. //工作的时间是否够修完 临下班接的单不做延长
  130. for (int i = 0; i < tvs.size(); i++) {
  131. Date startWorkTime = TimeExchange.StringToDate(dateNow + " " + tvs.get(i).getStart() + ":00", "yyyy-MM-dd HH:mm:ss");
  132. Date endWorkTime = TimeExchange.StringToDate(dateNow + " " + tvs.get(i).getEnd() + ":00", "yyyy-MM-dd HH:mm:ss");
  133. //小于工作时间段的开始时间
  134. if (nowDate.before(startWorkTime) || nowDate.equals(startWorkTime) && i == 0) {
  135. //早上开始上班之前的单子
  136. int minute = TimeExchange.getOffsetMinutes(nowDate, startWorkTime);
  137. int workMinte = TimeExchange.getOffsetMinutes(nowDate, endWorkTime);
  138. acceptanceAssessTime = acceptanceAssessTime + minute;
  139. acceptanceAssessTime = DelayedUtils.addMinuteStart(tvs, endWorkTime, acceptanceAssessTime, nowDate, dateNow, workMinte, i, 1);
  140. break;
  141. } else if ((startWorkTime.before(nowDate) || startWorkTime.equals(nowDate)) && (nowDate.before(endWorkTime) || nowDate.equals(endWorkTime))) {
  142. //工作时间段内的单子
  143. int workMinte = TimeExchange.getOffsetMinutes(nowDate, endWorkTime);
  144. acceptanceAssessTime = DelayedUtils.addMinuteStart(tvs, endWorkTime, acceptanceAssessTime, nowDate, dateNow, workMinte, i, 1);
  145. break;
  146. } else {
  147. //非工作时间段的单子
  148. Date nextStartWorkTime = TimeExchange.StringToDate(dateNow + " " + tvs.get(i + 1).getStart() + ":00", "yyyy-MM-dd HH:mm:ss");
  149. if ((endWorkTime.before(nowDate) || endWorkTime.equals(nowDate)) && (nowDate.before(nextStartWorkTime) || nowDate.equals(nextStartWorkTime))) {
  150. Date nextEndWorkTime = TimeExchange.StringToDate(dateNow + " " + tvs.get(i + 1).getEnd() + ":00", "yyyy-MM-dd HH:mm:ss");
  151. int minute = TimeExchange.getOffsetMinutes(nowDate, nextStartWorkTime);
  152. int workMinte = TimeExchange.getOffsetMinutes(nowDate, nextEndWorkTime);
  153. acceptanceAssessTime = acceptanceAssessTime + minute;
  154. acceptanceAssessTime = DelayedUtils.addMinuteStart(tvs, nextEndWorkTime, acceptanceAssessTime, nowDate, dateNow, workMinte, i, 2);
  155. break;
  156. }
  157. }
  158. }
  159. RepairDispatchRecord rdr = new RepairDispatchRecord();
  160. //创建派单记录
  161. rdr.setAssignedTime(new Date());
  162. rdr.setAcceptanceAssessTime(acceptanceAssessTime);
  163. rdr.setOrderType(0);
  164. rdr.setIsLoseEfficacy(0);
  165. rdr.setUsersId(user.getId());
  166. rdr.setRecordId(record.getId());
  167. rdr.setCreateTime(new Date());
  168. rdr.setUpdateTime(new Date());
  169. rdr.setCreateUser("定时器自动派单");
  170. rdr.setUpdateUser("定时器自动派单");
  171. rdr.setDeleted(0);
  172. rdrs.add(rdr);
  173. user.setRdrCount(user.getRdrCount() + 1);
  174. //给用户的消息中心数据
  175. RepairSystemMessages rsmU = new RepairSystemMessages();
  176. rsmU.setRecordId(record.getId());
  177. rsmU.setRecipientId(record.getUserId());
  178. rsmU.setContent("工单已交给系统,系统将催促师傅师尽快接单!");
  179. rsmU.setIsRead(0);
  180. rsmU.setCreateTime(new Date());
  181. rsmU.setUpdateTime(new Date());
  182. rsmU.setCreateUser("定时器自动派单");
  183. rsmU.setUpdateUser("定时器自动派单");
  184. rsmU.setDeleted(0);
  185. rsms.add(rsmU);
  186. //给师傅的消息中心数据
  187. RepairSystemMessages rsmS = new RepairSystemMessages();
  188. rsmS.setRecordId(record.getId());
  189. rsmS.setRecipientId(user.getId());
  190. rsmS.setContent("系统自动分配工单,请尽快处理!");
  191. rsmS.setIsRead(0);
  192. rsmS.setCreateTime(new Date());
  193. rsmS.setUpdateTime(new Date());
  194. rsmS.setCreateUser("定时器自动派单");
  195. rsmS.setUpdateUser("定时器自动派单");
  196. rsmS.setDeleted(0);
  197. rsms.add(rsmS);
  198. //添加跟踪记录
  199. RepairTrackRecord rtr = new RepairTrackRecord();
  200. rtr.setRecordId(record.getId());
  201. rtr.setMaintenanceState(1);
  202. rtr.setContent("已派单");
  203. rtr.setUserId(0);
  204. rtr.setUserZzstr("系统自动派单");
  205. rtr.setCreateTime(new Date());
  206. rtr.setUpdateTime(new Date());
  207. rtr.setCreateUser("定时器自动派单");
  208. rtr.setUpdateUser("定时器自动派单");
  209. rtr.setDeleted(0);
  210. rtrs.add(rtr);
  211. break;
  212. }
  213. } catch (Exception e) {
  214. System.out.println("自动派单失败:" + e.getMessage());
  215. throw new Exception("自动派单失败!");
  216. }
  217. }
  218. }
  219. try {
  220. boolean insertDis = repairDispatchRecordService.insertDispatchBatch(rdrs);
  221. if (!insertDis) {
  222. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":存储派单数据异常!");
  223. throw new Exception("自动派单失败!");
  224. }
  225. boolean insertRsm = repairSystemMessagesService.inserBatchSystemMessage(rsms);
  226. if (!insertRsm) {
  227. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":消息中心数据存储异常!");
  228. throw new Exception("自动派单失败!");
  229. }
  230. boolean insertRtr = repairTrackRecordService.insertTrackBatch(rtrs);
  231. if (!insertRtr) {
  232. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":根据记录数据存储异常!");
  233. throw new Exception("自动派单失败!");
  234. }
  235. } catch (Exception e) {
  236. System.out.println("新增自动派单失败:" + e.getMessage());
  237. throw new Exception("自动派单失败!");
  238. }
  239. }
  240. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":自动派单执行成功了一次!");
  241. }
  242. /**
  243. * 每小时执行一次
  244. * 自动好评定时器
  245. * XX小时后未评价的订单
  246. * 获取已完成没有好评数据的维修单数据,并且自动好评
  247. * 好评完成之后在消息中心添加自动好评提示信息
  248. * 发给维修师傅和用户
  249. */
  250. //每小时执行一次:0 0 */1 * * ?
  251. //每分钟执行一次:0 */1 * * * ?
  252. @Async
  253. @Scheduled(cron = "0 0 */1 * * ? ")
  254. @Transactional(rollbackFor = {Exception.class})
  255. public void autoEvaluate() throws Exception {
  256. //读取系统设置表数据 没设置就不好评
  257. RepairSystemSetting setting = repairSystemSettingService.queryRepairSystemSetting();
  258. if (setting != null && setting.getHour() != null) {
  259. String dateStr = TimeExchange.TimeRangeHour(new Date(), -setting.getHour(), "yyyy-MM-dd HH:mm:ss");
  260. List<RecordEvaluateVo> evaluates = repairEvaluateService.queryEvaluateList(dateStr);
  261. List<RepairEvaluate> res = new ArrayList<>();
  262. List<RepairSystemMessages> rsms = new ArrayList<>();
  263. for (RecordEvaluateVo evaluate : evaluates) {
  264. RepairEvaluate re = new RepairEvaluate();
  265. re.setRecordId(evaluate.getId());
  266. re.setStar(5);
  267. re.setContent("自动好评");
  268. re.setCreateTime(new Date());
  269. re.setUpdateTime(new Date());
  270. re.setCreateUser("定时器自动评价");
  271. re.setUpdateUser("定时器自动评价");
  272. re.setDeleted(0);
  273. res.add(re);
  274. RepairSystemMessages rsm = new RepairSystemMessages();
  275. rsm.setRecordId(evaluate.getId());
  276. rsm.setRecipientId(evaluate.getUserId());
  277. rsm.setContent("维修单" + setting.getHour() + "小时内未评价,系统自动5星好评");
  278. rsm.setIsRead(0);
  279. rsm.setCreateTime(new Date());
  280. rsm.setUpdateTime(new Date());
  281. rsm.setCreateUser("定时器自动评价");
  282. rsm.setUpdateUser("定时器自动评价");
  283. rsm.setDeleted(0);
  284. rsms.add(rsm);
  285. if (evaluate.getMaintenancerId() != null) {
  286. String[] userIds = evaluate.getMaintenancerId().split(",");
  287. for (int i = 0; i < userIds.length; i++) {
  288. RepairSystemMessages rsmSf = new RepairSystemMessages();
  289. rsmSf.setRecordId(evaluate.getId());
  290. rsmSf.setRecipientId(Integer.valueOf(userIds[i]));
  291. rsmSf.setContent("维修单二十四小时内未评价,系统自动5星好评");
  292. rsmSf.setIsRead(0);
  293. rsmSf.setCreateTime(new Date());
  294. rsmSf.setUpdateTime(new Date());
  295. rsmSf.setCreateUser("定时器自动评价");
  296. rsmSf.setUpdateUser("定时器自动评价");
  297. rsmSf.setDeleted(0);
  298. rsms.add(rsmSf);
  299. }
  300. }
  301. }
  302. try {
  303. boolean insertRes = repairEvaluateService.inserBatchEvaluate(res);
  304. if (!insertRes) {
  305. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":存储评价数据异常!");
  306. throw new Exception("自动评价失败!");
  307. }
  308. boolean insertRsm = repairSystemMessagesService.inserBatchSystemMessage(rsms);
  309. if (!insertRsm) {
  310. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":消息中心数据存储异常!");
  311. throw new Exception("自动评价失败!");
  312. }
  313. } catch (Exception e) {
  314. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":好评异常,异常信息:" + e.getMessage());
  315. throw new Exception("自动评价失败!");
  316. }
  317. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":好评执行成功了一次!");
  318. }
  319. }
  320. /**
  321. * 2023-10-26 A-jax 添加退款订单查询
  322. * 凌晨2点执行退款查询操作
  323. */
  324. //凌晨两点:0 0 2 * * ?
  325. //每分钟执行一次:0 */1 * * * ?
  326. @Async
  327. @Scheduled(cron = "0 0 2 * * ?")
  328. @Transactional(rollbackFor = {Exception.class})
  329. public void queryRefund() throws Exception {
  330. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":开始查询退款进度!");
  331. //获取退款中或部分退款的订单
  332. List<repairRefundVo> dataList = repairRefundRecordService.queryRefundRecord();
  333. List<String> datas = dataList == null ? new ArrayList<>() : dataList.stream().map(repairRefundVo::getRecordNo).distinct().collect(Collectors.toList());
  334. List<RepairRefundRecord> rrrs = new ArrayList<>();
  335. for (String data : datas) {
  336. Map<String, String> params = new HashMap<String, String>();
  337. params.put("appid", wxOpenidConfig.getXappid());//微信分配的小程序ID
  338. params.put("mch_id", wxOpenidConfig.getShappid());//微信支付分配的商户号
  339. params.put("out_trade_no", data);//"086339330913483");//商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。详见商户订单号
  340. String nonceStr = WxUtil.getWxNonceStr();
  341. params.put("nonce_str", nonceStr);//随机字符串,长度要求在32位以内。推荐随机数生成算法
  342. String Sign = WxUtil.getSign(params, wxOpenidConfig.getShsecret());//参数 + 商户密钥
  343. params.put("sign", Sign);
  344. String retXml = WxUtil.getRequestXml(params);
  345. String msg = HttpUtils.post("https://api.mch.weixin.qq.com/pay/refundquery", retXml);
  346. System.out.println(msg);
  347. try {
  348. Map<String, Object> dateSre = XmlUtil.fromXML(msg);
  349. if (dateSre.get("return_code").toString().equals("SUCCESS") && dateSre.get("result_code").toString().equals("SUCCESS")) {
  350. List<repairRefundVo> repairRefunds = dataList.stream().filter(e -> e.getRecordNo().equals(data)).collect(Collectors.toList());
  351. if(repairRefunds != null && repairRefunds.size() > 0){
  352. for (repairRefundVo repairRefund : repairRefunds) {
  353. String price = repairRefund.getPayPrice().multiply(new BigDecimal(100)).toString();
  354. String outRefundNo =null;
  355. String refundId = null;
  356. for (int i = 0;i<repairRefunds.size();i++) {
  357. if(dateSre.get("refund_id_"+i).toString().equals(price)){
  358. if(dateSre.get("refund_status_"+i).toString().equals("SUCCESS")){
  359. outRefundNo = dateSre.get("out_refund_no_"+i).toString();//商户退款单号
  360. refundId = dateSre.get("refund_id_"+i).toString();//微信退款单号
  361. RepairRefundRecord rrr = new RepairRefundRecord();
  362. rrr.setId(repairRefund.getId());
  363. rrr.setIsSuccess(1);
  364. rrr.setWxNo(outRefundNo);//商户退款单号
  365. rrr.setRefundNo(refundId);//微信退款单号
  366. rrrs.add(rrr);
  367. }
  368. }
  369. }
  370. }
  371. }
  372. }
  373. } catch (Exception e) {
  374. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":退款进度,异常信息:" + e.getMessage());
  375. throw new Exception("退款进度查询失败!");
  376. }
  377. }
  378. try {
  379. if(rrrs.size() > 0){
  380. boolean result = repairRefundRecordService.updateRdfundBatch(rrrs);
  381. if(!result){
  382. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":退款进度更新失败!");
  383. throw new Exception("退款进度查询失败!");
  384. }
  385. }
  386. } catch (Exception e) {
  387. System.out.println(TimeExchange.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss") + ":退款查询,异常信息:" + e.getMessage());
  388. throw new Exception("退款进度查询失败!");
  389. }
  390. }
  391. }