轮训机制实现

@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;
        }
    }
}

 

posted @ 2022-06-21 19:28  屠城校尉杜  阅读(45)  评论(0编辑  收藏  举报