package com.sqx.scheduler.order; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.ekyong.www.pay.pay.qrcode.api.RhtQrcodePayApi; import com.ekyong.www.pay.pay.qrcode.bean.QrcodeQueryRequestBean; import com.ekyong.www.pay.pay.qrcode.bean.QrcodeQueryResponseBean; import com.github.wxpay.sdk.WXPay; import com.sqx.modules.common.entity.CommonInfo; import com.sqx.modules.common.service.CommonInfoService; import com.sqx.modules.order.entity.TbOrder; import com.sqx.modules.order.service.AppOrderService; import com.sqx.modules.pay.config.WXConfig; import com.sqx.modules.pay.config.WechatPayConfig; import com.sqx.modules.pay.entity.PayDetails; import com.sqx.modules.pay.enums.PayStateEnums; import com.sqx.modules.pay.service.PayDetailsService; import com.sqx.scheduler.config.SchedulerLock; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; /** * 订单定时任务 * * @author : codingliang * @date : 2024-08-19 20:42 */ @Slf4j @Component @RequiredArgsConstructor public class OrderScheduler { private final RedissonClient redissonClient; private final AppOrderService orderService; private final CommonInfoService commonInfoService; private final PayDetailsService payDetailsService; private final static String RHT_PAY_BASE_URL = "https://api.ekbuyclub.com"; /** * 预约订单自动接单 * 5分钟运行一次 */ @Async @Scheduled(cron = "0 */5 * * * ?") public void reservationAutoReceivingOrder() { RLock lock = redissonClient.getLock(SchedulerLock.ORDER_OF_RESERVATION_AUTO_RECEIVING_LOCK); lock.lock(); try { log.info("预约订单自动接单任务开始运行"); List orderIds = orderService.getCurWaitReceivingOrderIds(); if (CollUtil.isEmpty(orderIds)) { log.info("没有符合条件的预约订单,预约订单自动接单任务运行...."); return; } List waitOrders = orderIds.stream().map(orderId -> { TbOrder waitOrder = new TbOrder(); waitOrder.setOrderId(orderId); waitOrder.setStatus(6); return waitOrder; }).collect(Collectors.toList()); for (TbOrder waitOrder : waitOrders) { log.info("预约订单id:{},正在自动接单", waitOrder.getOrderId()); try { orderService.updateOrder(waitOrder); log.info("预约订单id:{},自动接单成功", waitOrder.getOrderId()); } catch (Exception e) { e.printStackTrace(); log.error("预约单id:{},自动接单失败,失败原因:{}", waitOrder.getOrderId(), e.getMessage()); } } log.info("预约订单自动接单任务运行成功"); } catch (Exception e) { log.error("预约订单自动接单任务运行失败失败原因:{}", e.getMessage()); } finally { lock.unlock(); } } /** * 制作中订单自动制作完成 * 每分钟30秒时运行一次 */ @Async @Scheduled(cron = "30 */2 * * * ?") public void prodIngOrderAutoCompleted(){ try { log.info("自动制作完成制作中订单任务开始运行"); // 获取配置:是否开启制作中订单自动完成 String value = commonInfoService.findOne(418).getValue(); if("是".equals(value)){ String minute = commonInfoService.findOne(419).getValue(); LocalDateTime minusMinutes = LocalDateTime.now().minusMinutes(Long.valueOf(minute)); // 获取制作中状态的订单列表 List orders = orderService.getProdIngOrders(minusMinutes); if (CollUtil.isEmpty(orders)) { log.info("当前时间段没有需要自动制作完成的订单"); } else { // 订单随机排序 Collections.shuffle(orders); for (TbOrder order: orders){ try { log.info("订单:{},开始自动制作完成", order.getOrderId()); order.setStatus(3); orderService.updateOrder(order); log.info("订单:{},自动制作完成", order.getOrderId()); } catch (Exception e){ e.printStackTrace(); log.error("订单:{},自动完成异常,异常原因:【{}】", order.getOrderId(), e); } } } } log.info("自动制作完成制作中订单任务运行成功"); } catch (Exception e) { log.error("制作中订单自动制作完成任务运行失败失败原因:【{}】", e); } } /** * 自动完成订单 * 每30s运行一次 */ @Async @Scheduled(cron = "45 */1 * * * ?") public void autoFinishOrder() { RLock lock = redissonClient.getLock(SchedulerLock.ORDER_OF_WAIT_DELIVERY_AUTO_FINISH_LOCK); lock.lock(); try { log.info("自动完成订单任务开始运行"); CommonInfo one = commonInfoService.findOne(268); DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 查询所有 待取餐/派送中 状态的订单 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("status", 3); List orders = orderService.list(queryWrapper); for (TbOrder order: orders) { try { LocalDateTime updateTime = LocalDateTime.parse(order.getUpdateTime(), df); // 计算当前订单什么时候超时完成 LocalDateTime overDateTime = updateTime.plusHours(Integer.parseInt(one.getValue())); if (LocalDateTime.now().isAfter(overDateTime)) { log.info("订单id:{},开始自动完成", order.getOrderId()); orderService.accomplishOrders(order.getOrderId(),2); log.info("订单id:{},完成自动完成", order.getOrderId()); } } catch (Exception e) { log.error("订单id:{},开始自动完成失败,失败原因【{}】", order.getOrderId(), e); } } log.info("自动完成订单任务运行成功"); } catch (Exception e) { log.info("自动完成订单任务运行异常,异常", e); } finally { lock.unlock(); } } /** * 自动更改超时订单 * 每3分钟运行一次 */ @Async @Scheduled(cron = "0 */3 * * * ?") public void changeTimeOutOrder() { RLock lock = redissonClient.getLock(SchedulerLock.TIME_OUT_LOCK); lock.lock(); try { log.info("超时订单自动取消任务开始运行"); //加5分钟在前端强制取消前执行 long time = new Date().getTime()+5*60*1000; List orderList = orderService.changeTimeOutOrder(time); if (orderList.size() == 0) { log.info("没有符合条件的超时订单,超时订单自动取消任务运行...."); return; } WXConfig config = new WXConfig(); // 当前都是小程序支付 config.setAppId(commonInfoService.findOne(74).getValue()); //微信商户key config.setKey(commonInfoService.findOne(250).getValue()); //微信商户号mchId config.setMchId(commonInfoService.findOne(251).getValue()); WXPay wxpay = new WXPay(config); WechatPayConfig wechatMchConfig = WechatPayConfig.builder() .appId(commonInfoService.findOne(45).getValue()) .mchId(commonInfoService.findOne(434).getValue()) .mchKey(commonInfoService.findOne(435).getValue()) .h5Url(commonInfoService.findOne(19).getValue()) .build(); RhtQrcodePayApi qrcodePay = new RhtQrcodePayApi(wechatMchConfig.getMchId(), wechatMchConfig.getMchKey(), RHT_PAY_BASE_URL); ArrayList orders = new ArrayList<>(); for (TbOrder tbOrder : orderList) { Integer state = tbOrder.getState(); if (0 == state) { // 状态为待支付需要调用支付系统订单查询接口判断支付系统对应的订单状态 Map data = new HashMap<>(); String orderNumber = tbOrder.getOrderNumber(); //商户订单号 data.put("out_trade_no", orderNumber); Map response = wxpay.orderQuery(data); String trade_state = response.get("trade_state"); //SUCCESS—支付成功,REFUND—转入退款,NOTPAY—未支付,CLOSED—已关闭,REVOKED—已撤销(刷卡支付),USERPAYING--用户支付中,PAYERROR--支付失败(其他原因,如银行返回失败) if (ObjectUtils.isNotEmpty(trade_state)) { if ("NOTPAY".equals(trade_state)) { tbOrder.setStatus(5); orders.add(tbOrder); }else if ("SUCCESS".equals(trade_state)){ tbOrder.setPayType(1); orderService.updateOrderAfterPaySuccess(tbOrder); } }else { QrcodeQueryRequestBean qqrb = new QrcodeQueryRequestBean(); qqrb.setTraceno(orderNumber);// 商户流水号 QrcodeQueryResponseBean response2 = qrcodePay.query(qqrb); String respCode = response2.getRespCode(); log.info("超时任务处理订单【{}】查询支付结果,支付通知信息:{}", orderNumber, response2); // 0未支付,1支付成功,2支付失败,4退款中,5退款成功,6退款失败 if ("0".equals(respCode)) { tbOrder.setStatus(5); orders.add(tbOrder); }else if ("1".equals(respCode)){ PayDetails payDetails = payDetailsService.getByOrderNo(orderNumber); if (!ObjectUtil.isNull(payDetails)) { payDetails.setState(PayStateEnums.PAY_SUCCESS.getStateCode()); payDetails.setPayTime(response2.getTransDate()+ " " + response2.getTransTime()); payDetails.setTradeNo(response2.getChannelTraceno()); payDetails.setRemark("Scheduled RHT PAY SUCCESS"); payDetailsService.updateDetail(payDetails); }else { log.error("超时任务处理订单【{}】支付成功,但未获取到支付订单!支付通知信息:{}", orderNumber, response2); } tbOrder.setPayType(1); orderService.updateOrderAfterPaySuccess(tbOrder); } } } else { tbOrder.setStatus(5); orders.add(tbOrder); } } //list为空不要执行 if(!orders.isEmpty()){ orderService.updateBatchById(orders); } log.info("超时订单自动取消运行成功"); } catch (Exception e) { log.error("超时订单自动取消任务运行失败失败原因:{}", e.getMessage()); } finally { lock.unlock(); } } }