Commit a28e3d3b162bba2fa4af17f2bdfb4f9ced97f785
Merge branch 'main' of git3.nong12.com:cashierdesk/dili-cashier
Showing
8 changed files
with
177 additions
and
26 deletions
cashier-mall/src/main/java/com/diligrp/cashier/mall/MallConfiguration.java
| ... | ... | @@ -4,11 +4,18 @@ import com.diligrp.cashier.mall.client.RtMallHttpClient; |
| 4 | 4 | import com.diligrp.cashier.mall.property.RtMallDynamicProperty; |
| 5 | 5 | import com.diligrp.cashier.shared.mybatis.MybatisMapperSupport; |
| 6 | 6 | import org.mybatis.spring.annotation.MapperScan; |
| 7 | +import org.springframework.amqp.core.Binding; | |
| 8 | +import org.springframework.amqp.core.BindingBuilder; | |
| 9 | +import org.springframework.amqp.core.CustomExchange; | |
| 10 | +import org.springframework.amqp.core.Queue; | |
| 7 | 11 | import org.springframework.beans.factory.annotation.Value; |
| 8 | 12 | import org.springframework.context.annotation.Bean; |
| 9 | 13 | import org.springframework.context.annotation.ComponentScan; |
| 10 | 14 | import org.springframework.context.annotation.Configuration; |
| 11 | 15 | |
| 16 | +import java.util.HashMap; | |
| 17 | +import java.util.Map; | |
| 18 | + | |
| 12 | 19 | @Configuration |
| 13 | 20 | @ComponentScan("com.diligrp.cashier.mall") |
| 14 | 21 | @MapperScan(basePackages = {"com.diligrp.cashier.mall.dao"}, markerInterface = MybatisMapperSupport.class) |
| ... | ... | @@ -24,4 +31,24 @@ public class MallConfiguration { |
| 24 | 31 | RtMallDynamicProperty.AppSecretDynamicProperty config = rtMallDynamicProperty.getAppSecrets().getFirst(); |
| 25 | 32 | return new RtMallHttpClient(apiUrl, config.getAppKey(), config.getAppSecret()); |
| 26 | 33 | } |
| 34 | + | |
| 35 | + @Bean | |
| 36 | + public Queue queue() { | |
| 37 | + return new Queue(MallConstants.CASHIER_MALL_CALLBACK_RETRY_QUEUE, true, false, false); | |
| 38 | + } | |
| 39 | + | |
| 40 | + @Bean | |
| 41 | + public CustomExchange customExchange() { | |
| 42 | + Map<String, Object> args = new HashMap<>(); | |
| 43 | + args.put("x-delayed-type", "direct"); | |
| 44 | + return new CustomExchange(MallConstants.CASHIER_MALL_CALLBACK_RETRY_EXCHANGE, MallConstants.EXCHANGE_TYPE, true, false, args); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @Bean | |
| 48 | + public Binding binding(Queue queue, CustomExchange customExchange) { | |
| 49 | + return BindingBuilder.bind(queue) | |
| 50 | + .to(customExchange) | |
| 51 | + .with(MallConstants.CASHIER_MALL_CALLBACK_RETRY_QUEUE) | |
| 52 | + .noargs(); | |
| 53 | + } | |
| 27 | 54 | } | ... | ... |
cashier-mall/src/main/java/com/diligrp/cashier/mall/MallConstants.java
| ... | ... | @@ -11,11 +11,17 @@ public interface MallConstants { |
| 11 | 11 | String RESULT_SUCCESS = "success"; |
| 12 | 12 | String RESULT_FAILURE = "fail"; |
| 13 | 13 | |
| 14 | - Integer RT_MALL_SOURCE = 1; | |
| 15 | - | |
| 16 | 14 | String MALL_TOKEN = Constants.PRODUCT_NAME + "mall_token:"; |
| 17 | 15 | String MALL_USER_INFO = Constants.PRODUCT_NAME + "mall_user_info:"; |
| 18 | 16 | |
| 19 | 17 | String HOURLY_PAYMENT_CALLBACK_METHOD = "hourly.payment.callback"; |
| 20 | 18 | String HOURLY_REFUND_CALLBACK_METHOD = "hourly.refund.callback"; |
| 19 | + | |
| 20 | + // 三方回调重试队列 | |
| 21 | + String CASHIER_MALL_CALLBACK_RETRY_QUEUE = "cashier_mall_callback_retry_queue"; | |
| 22 | + String CASHIER_MALL_CALLBACK_RETRY_EXCHANGE = "cashier_mall_callback_retry_exchange"; | |
| 23 | + String EXCHANGE_TYPE = "x-delayed-message"; | |
| 24 | + | |
| 25 | + // 延时1分钟 | |
| 26 | + Integer WAIT_DELAY_MILLIS = 60_000; | |
| 21 | 27 | } | ... | ... |
cashier-mall/src/main/java/com/diligrp/cashier/mall/api/ManualHandlerApi.java
| 1 | 1 | package com.diligrp.cashier.mall.api; |
| 2 | 2 | |
| 3 | +import com.diligrp.cashier.mall.context.MallInitializeContext; | |
| 4 | +import com.diligrp.cashier.mall.dao.MallBizRefundDao; | |
| 5 | +import com.diligrp.cashier.mall.model.MallBizPayment; | |
| 6 | +import com.diligrp.cashier.mall.model.MallBizRefund; | |
| 7 | +import com.diligrp.cashier.mall.service.biz.MallBizPaymentService; | |
| 3 | 8 | import com.diligrp.cashier.shared.domain.Message; |
| 4 | 9 | import com.diligrp.cashier.shared.spi.IPaymentEventListener; |
| 5 | 10 | import com.diligrp.cashier.shared.spi.domain.PaymentResultBO; |
| 6 | 11 | import com.diligrp.cashier.shared.spi.domain.RefundResultBO; |
| 7 | 12 | import jakarta.annotation.Resource; |
| 8 | 13 | import org.springframework.validation.annotation.Validated; |
| 9 | -import org.springframework.web.bind.annotation.PostMapping; | |
| 10 | -import org.springframework.web.bind.annotation.RequestBody; | |
| 11 | -import org.springframework.web.bind.annotation.RequestMapping; | |
| 12 | -import org.springframework.web.bind.annotation.RestController; | |
| 14 | +import org.springframework.web.bind.annotation.*; | |
| 13 | 15 | |
| 14 | 16 | /** |
| 15 | 17 | * @ClassName ManualHandlerApi.java |
| ... | ... | @@ -23,6 +25,11 @@ import org.springframework.web.bind.annotation.RestController; |
| 23 | 25 | public class ManualHandlerApi { |
| 24 | 26 | @Resource |
| 25 | 27 | private IPaymentEventListener payNotifyServiceImpl; |
| 28 | + @Resource | |
| 29 | + private MallBizPaymentService mallBizPaymentService; | |
| 30 | + @Resource | |
| 31 | + private MallBizRefundDao mallBizRefundDao; | |
| 32 | + | |
| 26 | 33 | |
| 27 | 34 | @PostMapping("/payment/event") |
| 28 | 35 | public Message<?> handlePaymentEvent(@RequestBody PaymentResultBO payment) { |
| ... | ... | @@ -36,4 +43,19 @@ public class ManualHandlerApi { |
| 36 | 43 | return Message.success(); |
| 37 | 44 | } |
| 38 | 45 | |
| 46 | + @PostMapping("/payment/callback/{source}") | |
| 47 | + public Message<?> paymentCallback(@RequestBody PaymentResultBO payment, @PathVariable("source") Integer source) { | |
| 48 | + String tradeId = payment.getTradeId(); | |
| 49 | + MallBizPayment mallBizPayment = mallBizPaymentService.getByPayTradeId(tradeId); | |
| 50 | + MallInitializeContext.getBySource(source).payCallBack(payment, mallBizPayment); | |
| 51 | + return Message.success(); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @PostMapping("/refund/callback/{source}") | |
| 55 | + public Message<?> refundCallback(@RequestBody RefundResultBO refund, @PathVariable("source") Integer source) { | |
| 56 | + MallBizRefund mallBizRefund = mallBizRefundDao.getByRefundTradeId(refund.getRefundId()); | |
| 57 | + MallInitializeContext.getBySource(source).refundCallBack(refund, mallBizRefund); | |
| 58 | + return Message.success(); | |
| 59 | + } | |
| 60 | + | |
| 39 | 61 | } | ... | ... |
cashier-mall/src/main/java/com/diligrp/cashier/mall/listener/CallBackRetryListener.java
0 → 100644
| 1 | +package com.diligrp.cashier.mall.listener; | |
| 2 | + | |
| 3 | +import com.diligrp.cashier.mall.MallConstants; | |
| 4 | +import com.diligrp.cashier.mall.context.MallInitializeContext; | |
| 5 | +import com.diligrp.cashier.mall.model.MallBizPayment; | |
| 6 | +import com.diligrp.cashier.mall.model.MallBizRefund; | |
| 7 | +import com.diligrp.cashier.shared.spi.domain.PaymentResultBO; | |
| 8 | +import com.diligrp.cashier.shared.spi.domain.RefundResultBO; | |
| 9 | +import com.diligrp.cashier.shared.util.JsonUtils; | |
| 10 | +import com.fasterxml.jackson.core.type.TypeReference; | |
| 11 | +import com.rabbitmq.client.Channel; | |
| 12 | +import org.slf4j.Logger; | |
| 13 | +import org.slf4j.LoggerFactory; | |
| 14 | +import org.springframework.amqp.core.Message; | |
| 15 | +import org.springframework.amqp.rabbit.annotation.RabbitListener; | |
| 16 | +import org.springframework.stereotype.Component; | |
| 17 | + | |
| 18 | +import java.io.IOException; | |
| 19 | +import java.util.Map; | |
| 20 | + | |
| 21 | +/** | |
| 22 | + * @ClassName CallBackRetryListener.java | |
| 23 | + * @author dengwei | |
| 24 | + * @version 1.0.0 | |
| 25 | + * @Description CallBackRetryListener | |
| 26 | + */ | |
| 27 | +@Component | |
| 28 | +public class CallBackRetryListener { | |
| 29 | + private static final Logger LOG = LoggerFactory.getLogger(CallBackRetryListener.class); | |
| 30 | + | |
| 31 | + @RabbitListener(queues = MallConstants.CASHIER_MALL_CALLBACK_RETRY_QUEUE, ackMode = "MANUAL") | |
| 32 | + public void callBackRetry(Message message, Channel channel) throws IOException { | |
| 33 | + try { | |
| 34 | + LOG.info("callBackRetry message【{}】", new String(message.getBody())); | |
| 35 | + Map<String, Object> data = JsonUtils.fromJsonString(new String(message.getBody()), new TypeReference<>() { | |
| 36 | + }); | |
| 37 | + | |
| 38 | + switch ((String) data.get("callBackMethod")) { | |
| 39 | + case "pay" -> { | |
| 40 | + Integer source = (Integer) data.get("source"); | |
| 41 | + PaymentResultBO event = JsonUtils.convertValue(data.get("event"), PaymentResultBO.class); | |
| 42 | + MallBizPayment mallBizPayment = JsonUtils.convertValue(data.get("mallBizPayment"), MallBizPayment.class); | |
| 43 | + MallInitializeContext.getBySource(source).payCallBack(event, mallBizPayment); | |
| 44 | + } | |
| 45 | + case "refund" -> { | |
| 46 | + Integer source = (Integer) data.get("source"); | |
| 47 | + RefundResultBO event = JsonUtils.convertValue(data.get("event"), RefundResultBO.class); | |
| 48 | + MallBizRefund mallBizRefund = JsonUtils.convertValue(data.get("mallBizRefund"), MallBizRefund.class); | |
| 49 | + MallInitializeContext.getBySource(source).refundCallBack(event, mallBizRefund); | |
| 50 | + } | |
| 51 | + default -> LOG.warn("未知的回调方法【{}】", data.get("callBackMethod")); | |
| 52 | + } | |
| 53 | + // 消息处理成功,发送确认 | |
| 54 | + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); | |
| 55 | + } catch (Exception e) { | |
| 56 | + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); | |
| 57 | + } | |
| 58 | + } | |
| 59 | +} | ... | ... |
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/biz/impl/MallBizRefundServiceImpl.java
| ... | ... | @@ -65,7 +65,7 @@ public class MallBizRefundServiceImpl implements MallBizRefundService { |
| 65 | 65 | public RefundSuccessVO refund(RefundCO refundCo) { |
| 66 | 66 | MallBizRefund mallBizRefund = mallBizRefundDao.getMallBizRefund(new MallBizRefund(refundCo.getOrderId(), refundCo.getTradeId())); |
| 67 | 67 | if (Objects.nonNull(mallBizRefund)) { |
| 68 | - throw new RtMartMallException(RtMarkErrorCode.E50056); | |
| 68 | + throw new RtMartMallException(RtMarkErrorCode.E5006); | |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | 71 | MallBizOrder mallBizOrder = mallBizOrderDao.getByOrderId(refundCo.getOrderId()); | ... | ... |
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/sourcechannel/AbstractSourceChannel.java
| ... | ... | @@ -20,9 +20,13 @@ import com.diligrp.cashier.shared.util.JsonUtils; |
| 20 | 20 | import jakarta.annotation.Resource; |
| 21 | 21 | import org.slf4j.Logger; |
| 22 | 22 | import org.slf4j.LoggerFactory; |
| 23 | +import org.springframework.amqp.core.Message; | |
| 24 | +import org.springframework.amqp.core.MessageBuilder; | |
| 25 | +import org.springframework.amqp.rabbit.core.RabbitTemplate; | |
| 23 | 26 | import org.springframework.data.redis.core.RedisTemplate; |
| 24 | 27 | import org.springframework.transaction.annotation.Transactional; |
| 25 | 28 | |
| 29 | +import java.nio.charset.StandardCharsets; | |
| 26 | 30 | import java.util.Arrays; |
| 27 | 31 | import java.util.List; |
| 28 | 32 | import java.util.Objects; |
| ... | ... | @@ -49,6 +53,8 @@ public abstract class AbstractSourceChannel { |
| 49 | 53 | protected MallBizPaymentService mallBizPaymentService; |
| 50 | 54 | @Resource |
| 51 | 55 | protected MallBizRefundService mallBizRefundService; |
| 56 | + @Resource | |
| 57 | + protected RabbitTemplate rabbitTemplate; | |
| 52 | 58 | |
| 53 | 59 | /** |
| 54 | 60 | * authLogin |
| ... | ... | @@ -155,4 +161,15 @@ public abstract class AbstractSourceChannel { |
| 155 | 161 | * refundCallBack |
| 156 | 162 | */ |
| 157 | 163 | public abstract void refundCallBack(RefundResultBO event, MallBizRefund mallBizRefund); |
| 164 | + | |
| 165 | + /** | |
| 166 | + * sendDelayMessage | |
| 167 | + */ | |
| 168 | + protected void sendDelayMessage(String load) { | |
| 169 | + LOG.info("sendDelayMessage load: {}", load); | |
| 170 | + Message msg = MessageBuilder.withBody(load.getBytes(StandardCharsets.UTF_8)) | |
| 171 | + .setHeader("x-delay", MallConstants.WAIT_DELAY_MILLIS) | |
| 172 | + .build(); | |
| 173 | + rabbitTemplate.convertAndSend(MallConstants.CASHIER_MALL_CALLBACK_RETRY_EXCHANGE, MallConstants.CASHIER_MALL_CALLBACK_RETRY_QUEUE, msg); | |
| 174 | + } | |
| 158 | 175 | } | ... | ... |
cashier-mall/src/main/java/com/diligrp/cashier/mall/service/sourcechannel/RtMallChannel.java
| ... | ... | @@ -21,6 +21,7 @@ import com.diligrp.cashier.shared.util.DateUtils; |
| 21 | 21 | import com.diligrp.cashier.shared.util.JsonUtils; |
| 22 | 22 | import com.diligrp.cashier.shared.util.UrlParamParserUtils; |
| 23 | 23 | import com.fasterxml.jackson.core.type.TypeReference; |
| 24 | +import com.google.common.collect.Maps; | |
| 24 | 25 | import org.apache.commons.collections4.CollectionUtils; |
| 25 | 26 | import org.slf4j.Logger; |
| 26 | 27 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -84,17 +85,30 @@ public class RtMallChannel extends AbstractSourceChannel { |
| 84 | 85 | if (Objects.equals(mallBizOrder.getOrderType(), OrderType.MINI_PROGRAM.code)) { |
| 85 | 86 | List<MallBizPaymentOrder> mallBizPaymentOrders = mallBizPaymentService.listPaymentOrderByBizPaymentId(mallBizPayment.getId()); |
| 86 | 87 | if (CollectionUtils.isNotEmpty(mallBizPaymentOrders)) { |
| 87 | - payCallBack(mallBizOrder, mallBizPayment, mallBizPaymentOrders.getFirst()); | |
| 88 | + payCallBack(event, mallBizOrder, mallBizPayment, mallBizPaymentOrders.getFirst()); | |
| 88 | 89 | } |
| 89 | 90 | } |
| 90 | 91 | } |
| 91 | 92 | |
| 92 | 93 | /** |
| 94 | + * refundCallBack | |
| 95 | + */ | |
| 96 | + @Override | |
| 97 | + public void refundCallBack(RefundResultBO event, MallBizRefund mallBizRefund) { | |
| 98 | + MallBizOrder mallBizOrder = mallBizOrderDao.selectByPrimaryKey(mallBizRefund.getBizOrderId()); | |
| 99 | + // 目前大润发只对小程序支付存在回调逻辑 | |
| 100 | + if (Objects.equals(mallBizOrder.getOrderType(), OrderType.MINI_PROGRAM.code)) { | |
| 101 | + refundCallBack(event, mallBizOrder, mallBizRefund); | |
| 102 | + } | |
| 103 | + } | |
| 104 | + | |
| 105 | + /** | |
| 93 | 106 | * payCallBack |
| 94 | 107 | * @see https://shopex.yuque.com/hl0rrx/vlp0m4/nkwt5yrhdfzoy78s?singleDoc#RtKpF |
| 95 | 108 | * 支付回调接口 |
| 96 | 109 | */ |
| 97 | - public void payCallBack(final MallBizOrder mallBizOrder, | |
| 110 | + public void payCallBack(final PaymentResultBO event, | |
| 111 | + final MallBizOrder mallBizOrder, | |
| 98 | 112 | final MallBizPayment mallBizPayment, |
| 99 | 113 | final MallBizPaymentOrder mallBizPaymentOrder) { |
| 100 | 114 | LOG.info("orderType:{} payCallBack orderId: {} tradeId: {}", mallBizOrder.getOrderType(), mallBizPaymentOrder.getOrderId(), mallBizPaymentOrder.getTradeId()); |
| ... | ... | @@ -106,37 +120,42 @@ public class RtMallChannel extends AbstractSourceChannel { |
| 106 | 120 | message = HttpClientUtils.postJson(mallBizPayment.getPaymentCallback(), hourlyPaymentCallback, null, new TypeReference<>() { |
| 107 | 121 | }, "rt-mall"); |
| 108 | 122 | } catch (Exception e) { |
| 109 | - // 失败-前期未考虑重试 人工补偿重试!! | |
| 110 | 123 | LOG.warn("payCallBack failed: {}", JsonUtils.toJsonString(hourlyPaymentCallback), e); |
| 124 | + Map<String, Object> param = Maps.newHashMapWithExpectedSize(4); | |
| 125 | + param.put("event", event); | |
| 126 | + param.put("mallBizPayment", mallBizPayment); | |
| 127 | + param.put("callBackMethod", "pay"); | |
| 128 | + param.put("source", OrderSource.RT_MART.getCode()); | |
| 129 | + sendDelayMessage(JsonUtils.toJsonString(param)); | |
| 111 | 130 | } |
| 112 | 131 | LOG.info("payCallBack message: {}", JsonUtils.toJsonString(message)); |
| 113 | 132 | } |
| 114 | 133 | |
| 115 | 134 | /** |
| 116 | 135 | * refundCallBack |
| 117 | - */ | |
| 118 | - @Override | |
| 119 | - public void refundCallBack(RefundResultBO event, MallBizRefund mallBizRefund) { | |
| 120 | - MallBizOrder mallBizOrder = mallBizOrderDao.selectByPrimaryKey(mallBizRefund.getBizOrderId()); | |
| 121 | - // 目前大润发只对小程序支付存在回调逻辑 | |
| 122 | - if (Objects.equals(mallBizOrder.getOrderType(), OrderType.MINI_PROGRAM.code)) { | |
| 123 | - refundCallBack(mallBizOrder, mallBizRefund); | |
| 124 | - } | |
| 125 | - } | |
| 126 | - | |
| 127 | - /** | |
| 128 | - * refundCallBack | |
| 129 | 136 | * @see https://shopex.yuque.com/hl0rrx/vlp0m4/nkwt5yrhdfzoy78s?singleDoc#RtKpF |
| 130 | 137 | * 退款回调接口 |
| 131 | 138 | */ |
| 132 | - public void refundCallBack(final MallBizOrder mallBizOrder, | |
| 139 | + public void refundCallBack(final RefundResultBO event, | |
| 140 | + final MallBizOrder mallBizOrder, | |
| 133 | 141 | final MallBizRefund mallBizRefund) { |
| 134 | 142 | LOG.info("orderType:{} refundCallBack orderId: {} tradeId: {}", mallBizOrder.getOrderType(), mallBizRefund.getOrderId(), mallBizRefund.getTradeId()); |
| 135 | 143 | RtMallDynamicProperty.AppSecretDynamicProperty property = getProperty(mallBizOrder); |
| 136 | 144 | HourlyRefundCallbackDTO hourlyRefundCallback = HourlyRefundCallbackDTO.of(mallBizOrder, mallBizRefund, property); |
| 137 | 145 | hourlyRefundCallback.setSign(sign(hourlyRefundCallback, property)); |
| 138 | - RtMarkMessage<?> message = HttpClientUtils.postJson(mallBizRefund.getRefundCallback(), hourlyRefundCallback, null, new TypeReference<>() { | |
| 139 | - }, "rt-mall"); | |
| 146 | + RtMarkMessage<?> message = null; | |
| 147 | + try { | |
| 148 | + message = HttpClientUtils.postJson(mallBizRefund.getRefundCallback(), hourlyRefundCallback, null, new TypeReference<>() { | |
| 149 | + }, "rt-mall"); | |
| 150 | + } catch (Exception e) { | |
| 151 | + LOG.warn("refundCallBack failed: {}", JsonUtils.toJsonString(hourlyRefundCallback), e); | |
| 152 | + Map<String, Object> param = Maps.newHashMapWithExpectedSize(4); | |
| 153 | + param.put("event", event); | |
| 154 | + param.put("mallBizRefund", mallBizRefund); | |
| 155 | + param.put("callBackMethod", "refund"); | |
| 156 | + param.put("source", OrderSource.RT_MART.getCode()); | |
| 157 | + sendDelayMessage(JsonUtils.toJsonString(param)); | |
| 158 | + } | |
| 140 | 159 | LOG.info("refundCallBack message: {}", JsonUtils.toJsonString(message)); |
| 141 | 160 | } |
| 142 | 161 | ... | ... |
cashier-mall/src/main/java/com/diligrp/cashier/mall/type/RtMarkErrorCode.java
| ... | ... | @@ -20,7 +20,8 @@ public enum RtMarkErrorCode { |
| 20 | 20 | E5003("E5003", "回调状态不匹配"), |
| 21 | 21 | E5004("E5004", "数据异常"), |
| 22 | 22 | E5005("E5005", "支付状态异常"), |
| 23 | - E50056("E5006", "存在相同的申请"), | |
| 23 | + E5006("E5006", "存在相同的申请"), | |
| 24 | + E5007("E5007", "远程调用失败重试"), | |
| 24 | 25 | ; |
| 25 | 26 | |
| 26 | 27 | public final String code; | ... | ... |