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