轮训机制实现
@Service public class BscanCPayPollingHandler { private static final Logger LOGGER = LoggerFactory.getLogger(BscanCPayPollingHandler.class); /** * 线程池 */ private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 30, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.DiscardPolicy()); @Resource(name = "channelPaymentQueryServiceImpl") private IChannelPaymentQueryService channelPaymentQueryService; @Inject private IRefundForTradeService refundForTradeService; /** * 查询订单状态,每5s一次 * * @param requestContext 请求上下文 * @param times 时间 */ public void doQuery(RequestContext<ChannelPosPaymentRequest> requestContext, int times) { // 1.动态调整线程池情况 checkThreadPool(); // 2.执行轮询业务 threadPoolExecutor.execute(new BscanCPayQueryThread(times, requestContext, channelPaymentQueryService, refundForTradeService)); // 3.动态调整线程池情况 checkThreadPool(); } /** * 动态调整线程池大小 */ public void checkThreadPool() { int activeCount = threadPoolExecutor.getActiveCount(); int corePoolSize = threadPoolExecutor.getCorePoolSize(); int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize(); int usedSize = threadPoolExecutor.getQueue().size(); LOGGER.info("Number of thread pool active threads responsible for polling", activeCount, "number of core threads", corePoolSize, "maximum number of threads", maximumPoolSize, "Number of queue tasks", usedSize); if ((activeCount > corePoolSize - 5) && (corePoolSize + 5 < maximumPoolSize)) { threadPoolExecutor.setCorePoolSize(corePoolSize + 5); LOGGER.info("The number of core threads in the thread pool responsible for polling is expanded by 10"); } if (activeCount < corePoolSize - 10) { threadPoolExecutor.setCorePoolSize(corePoolSize - 5); LOGGER.info("The number of core threads responsible for polling in the thread pool is reduced by 10"); } } }
/** * 轮询支付结果异步线程:无异步通知 * * @author DDD * @Since 2022/5/13 */ public class BscanCPayQueryThread implements Runnable { /** * 日志 */ private static final Logger LOGGER = LoggerFactory.getLogger(BscanCPayQueryThread.class); /** * 退款服务端 */ private static final String REFUND_SOURCE_IPS = "DDD"; /** * 异步调用工作查询支付结果线程 */ private static final String LOG_PREFIX = "【异步调用查询工作线程】"; private final int times; private final RequestContext<ChannelPosPaymentRequest> requestContext; private final IChannelPaymentQueryService channelPaymentQueryService; private final IRefundForTradeService refundForTradeService; public BscanCPayQueryThread(int times, RequestContext<ChannelPosPaymentRequest> requestContext, IChannelPaymentQueryService channelPaymentQueryService, IRefundForTradeService refundForTradeService) { this.times = times; this.requestContext = requestContext; this.channelPaymentQueryService = channelPaymentQueryService; this.refundForTradeService = refundForTradeService; } @Override public void run() { String tempMsg = "**************启动工作支付结果查询线程******************"; try { LOGGER.info(LOG_PREFIX, tempMsg); pollingOrder(requestContext, times); tempMsg = " ****************工作支付结果查询线程结束*******************"; LOGGER.info(LOG_PREFIX, tempMsg); } catch (Exception exception) { tempMsg = LOG_PREFIX + " 运行出现异常:" + exception.getLocalizedMessage(); LOGGER.error(tempMsg, exception); } } private void pollingOrder(RequestContext<ChannelPosPaymentRequest> requestContext, int times) { String tempMsg = ""; // 1.参数校验 if (times <= 0) { times = 1; } DelayQueue<OrderDelayed> orderDelayedDelayQueue = new DelayQueue<>(); if (null == requestContext || StringUtils.isEmpty(requestContext.getChannelNo())) { tempMsg = "渠道号为空,工作支付结果异步查询触发失败,错误码:"; LOGGER.error(tempMsg, PayErrorCodeEnum.CHANNEL_QUERY_FAIL.getCode()); throw new ChannelException(PayErrorCodeEnum.CHANNEL_QUERY_FAIL.getCode(), "渠道号为空,工作支付结果异步查询触发失败"); } // 2.初始化延时队列 for (int i = 0; i < times; i++) { orderDelayedDelayQueue.put(new OrderDelayed(5 * (i + 1), requestContext.getChannelNo(), TimeUnit.SECONDS)); } // 3. 触发查询 int queueSize = orderDelayedDelayQueue.size(); try { for (int i = 0; i < queueSize; i++) { LOGGER.info("The execution channel serial number is {} the time is {}", requestContext.getChannelNo(), String.valueOf(i + 1)); OrderDelayed orderDelayed = orderDelayedDelayQueue.take(); ChannelPaymentQueryRequest channelPaymentQueryRequest = new ChannelPaymentQueryRequest(); channelPaymentQueryRequest.setRequestModule(SYSTEM_CODE.getValue()); channelPaymentQueryRequest.setChannelNo(orderDelayed.getOrderId()); channelPaymentQueryRequest.setPayQueryType(PayQueryType.TRADE_QUERY.getCode()); MessageResponse<ChannelPaymentQueryResponse> channelResponse = channelPaymentQueryService.queryTrade(channelPaymentQueryRequest); LOGGER.info("queryChannel response ", channelResponse); if (channelResponse.isSuccessful()) { BankTransStatusEnum status = BankTransStatusEnum.getEnum(channelResponse.getResultData().getPayStatus()); // 判断工作支付,是否是最终状态,支付失败或支付成功 if (anyPayStatusMatch(status.getCode(), SUCCESS, FAIL, REFUND, FINISH, CLOSE)) { LOGGER.info("Order payment status is", status.getCode()); break; } } // 4.最后一次查询结束后,仍未获得订单结果,撤销订单 if (i == (queueSize - 1)) { LOGGER.info("To cancel the payment order, the channelNo is ", requestContext.getChannelNo()); cancelOrder(requestContext); } } } catch (InterruptedException e) { LOGGER.info("The thread of getting the payment order result is abnormal, and the order is cancelled ", requestContext.getChannelNo()); cancelOrder(requestContext); tempMsg = "获取订单支付结果线程异常中断.已撤销订单:"; LOGGER.error(tempMsg, e); throw new ChannelException(PayErrorCodeEnum.CHANNEL_QUERY_FAIL.getCode(), tempMsg + e.getLocalizedMessage(), e); } } private boolean cancelOrder(RequestContext<ChannelPosPaymentRequest> requestContext) { String tempMsg = "**************cancel start*****************"; LOGGER.info(LOG_PREFIX, tempMsg); RefundTradeRequest refundTradeRequest = new RefundTradeRequest(); ChannelPaymentTradeVo tradeVo = requestContext.getDataPo(); refundTradeRequest.setMerchantNo(tradeVo.getMerchantNo()); refundTradeRequest.setOutTradeNo(tradeVo.getOutTradeNo()); refundTradeRequest.setOriOutTradeNo(tradeVo.getOutTradeNo()); refundTradeRequest.setRequestModule(SYSTEM_CODE.getValue()); refundTradeRequest.setChannelCode(requestContext.getChannelCode()); refundTradeRequest.setBankTradeNo(tradeVo.getBankTradeNo()); refundTradeRequest.setBankCode(requestContext.getBankCode()); refundTradeRequest.setOriTradeNo(requestContext.getTradeNo()); refundTradeRequest.setRefundSource(REFUND_SOURCE_IPS); refundTradeRequest.setPosCancel(true); String currencyCode = tradeVo.getCurrency(); CurrencyEnum currencyEnum = CurrencyEnum.getEnum(currencyCode); if (null == currencyEnum) { throw new ChannelRefundException(PayErrorCodeEnum.CHANNEL_REFUND_FAIL.getCode(), "获取币种(currencyEnum)为空"); } String amount = null; if (null != tradeVo.getAmount()) { amount = String.valueOf(tradeVo.getAmount()); } BigDecimal transformData = BigDecimal.valueOf(parseDouble(amount + "")) .divide(new BigDecimal(10).pow(currencyEnum.getSeparator())) .setScale(currencyEnum.getSeparator()); Money refundMoney = new Money(currencyEnum.getCode(), transformData.toString()); refundTradeRequest.setRefundMoney(refundMoney); tempMsg = "user did not pay,cancel the order start! "; LOGGER.info(tempMsg, JSON.toJSONString(refundTradeRequest)); MessageResponse<CancelTradeResponseData> response = refundForTradeService.cancel(refundTradeRequest); tempMsg = "user did not pay,cancel the order end!"; LOGGER.info(tempMsg, JSON.toJSONString(response)); tempMsg = "**************cancel end*****************"; LOGGER.info(LOG_PREFIX, tempMsg); return response.isSuccessful(); } /** * 判断渠道成功状态 * * @param payStatus 支付状态 * @param bankTransStatusEnum 银行支付状态枚举 * @return 成功状态 */ public static boolean anyPayStatusMatch(String payStatus, BankTransStatusEnum... bankTransStatusEnum) { return Arrays.stream(bankTransStatusEnum).anyMatch(status -> status.getCode().equals(payStatus)); } class OrderDelayed implements Delayed { private long time; private String orderId; public OrderDelayed(long time, String orderNo, TimeUnit unit) { this.time = System.currentTimeMillis() + (time > 0 ? unit.toMillis(time) : 0); this.orderId = orderNo; } @Override public long getDelay(TimeUnit unit) { return unit.convert(time - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed delayed) { OrderDelayed orderDelayed = (OrderDelayed) delayed; long diff = this.time - orderDelayed.time; if (diff <= 0) { return -1; } else { return 1; } } public String getOrderId() { return orderId; } } }