From a9c33352f7ad32654623e78b7c9b0366cac85287 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Tue, 16 Dec 2025 17:58:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(profit-share):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=88=86=E8=B4=A6=E6=B6=88=E6=81=AF=E5=8F=91=E9=80=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 ProfitShareKafkaProducer 的 sendRefundMessage 方法返回 CompletableFuture - 在 WxMpPayAdapter 中增加 transactionId 和 refundTransactionId 字段解析 - 在 PayResponse 和 RefundResponse 中新增 transactionId 相关字段 - 在 WxPayServiceImpl 中注入 ProfitShareKafkaProducer 并发送分账消息 - 调整退款逻辑以异步方式发送分账退款消息后再执行退款操作 --- .../service/ProfitShareKafkaProducer.java | 7 ++- .../basic/pay/adapter/WxMpPayAdapter.java | 2 + .../ycwl/basic/pay/entity/PayResponse.java | 1 + .../ycwl/basic/pay/entity/RefundResponse.java | 1 + .../service/mobile/impl/WxPayServiceImpl.java | 56 +++++++++++++------ 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/ycwl/basic/integration/profitshare/service/ProfitShareKafkaProducer.java b/src/main/java/com/ycwl/basic/integration/profitshare/service/ProfitShareKafkaProducer.java index c1a509e0..1c080645 100644 --- a/src/main/java/com/ycwl/basic/integration/profitshare/service/ProfitShareKafkaProducer.java +++ b/src/main/java/com/ycwl/basic/integration/profitshare/service/ProfitShareKafkaProducer.java @@ -10,8 +10,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Service; +import java.util.concurrent.CompletableFuture; + /** * 分账Kafka消息生产者 * @@ -58,7 +61,7 @@ public class ProfitShareKafkaProducer { /** * 发送退款消息(订单退款成功后调用) */ - public void sendRefundMessage(RefundMessage message) { + public CompletableFuture> sendRefundMessage(RefundMessage message) { validateRefund(message); String topic = kafkaProps != null && StringUtils.isNotBlank(kafkaProps.getRefundTopic()) ? kafkaProps.getRefundTopic() @@ -69,7 +72,7 @@ public class ProfitShareKafkaProducer { log.info("[REFUND] producing to topic={}, key={}, refundOrderId={}, originalOrderId={}, amount={}, type={}", topic, key, message.getRefundOrderId(), message.getOriginalOrderId(), message.getRefundAmount(), message.getRefundType()); - kafkaTemplate.send(topic, key, payload).whenComplete((metadata, ex) -> { + return kafkaTemplate.send(topic, key, payload).whenComplete((metadata, ex) -> { if (ex != null) { log.error("[REFUND] produce failed: refundOrderId={}, error={}", message.getRefundOrderId(), ex.getMessage(), ex); } else if (metadata != null) { diff --git a/src/main/java/com/ycwl/basic/pay/adapter/WxMpPayAdapter.java b/src/main/java/com/ycwl/basic/pay/adapter/WxMpPayAdapter.java index 01662682..691fbf3e 100644 --- a/src/main/java/com/ycwl/basic/pay/adapter/WxMpPayAdapter.java +++ b/src/main/java/com/ycwl/basic/pay/adapter/WxMpPayAdapter.java @@ -200,6 +200,7 @@ public class WxMpPayAdapter implements IPayAdapter { Transaction parse = parser.parse(requestParam, Transaction.class); resp.setValid(true); resp.setOrderNo(parse.getOutTradeNo()); + resp.setTransactionId(parse.getTransactionId()); if (parse.getAmount() != null) { resp.setOrderPrice(parse.getAmount().getTotal()); resp.setPayPrice(parse.getAmount().getPayerTotal()); @@ -313,6 +314,7 @@ public class WxMpPayAdapter implements IPayAdapter { .build(); RefundNotification parse = parser.parse(requestParam, RefundNotification.class); resp.setValid(true); + resp.setRefundTransactionId(parse.getRefundId()); resp.setOriginalResponse(parse); if (parse.getRefundStatus() == SUCCESS) { //退款成功 diff --git a/src/main/java/com/ycwl/basic/pay/entity/PayResponse.java b/src/main/java/com/ycwl/basic/pay/entity/PayResponse.java index 67fb445b..fef4b624 100644 --- a/src/main/java/com/ycwl/basic/pay/entity/PayResponse.java +++ b/src/main/java/com/ycwl/basic/pay/entity/PayResponse.java @@ -9,6 +9,7 @@ import java.math.BigDecimal; public class PayResponse { private boolean valid; private String orderNo; + private String transactionId; @JsonIgnore private Object originalResponse; private Integer orderPrice; diff --git a/src/main/java/com/ycwl/basic/pay/entity/RefundResponse.java b/src/main/java/com/ycwl/basic/pay/entity/RefundResponse.java index dda2cde7..16890116 100644 --- a/src/main/java/com/ycwl/basic/pay/entity/RefundResponse.java +++ b/src/main/java/com/ycwl/basic/pay/entity/RefundResponse.java @@ -10,6 +10,7 @@ public class RefundResponse { private boolean valid; private String orderNo; private String refundNo; + private String refundTransactionId; @JsonIgnore private Object originalResponse; private Integer orderPrice; diff --git a/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java b/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java index 09d8c332..bddd9742 100644 --- a/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java @@ -7,6 +7,9 @@ import com.ycwl.basic.enums.BizCodeEnum; import com.ycwl.basic.enums.OrderStateEnum; import com.ycwl.basic.enums.StatisticEnum; import com.ycwl.basic.exception.AppException; +import com.ycwl.basic.integration.profitshare.dto.message.OrderMessage; +import com.ycwl.basic.integration.profitshare.dto.message.RefundMessage; +import com.ycwl.basic.integration.profitshare.service.ProfitShareKafkaProducer; import com.ycwl.basic.mapper.OrderMapper; import com.ycwl.basic.mapper.PaymentMapper; import com.ycwl.basic.mapper.StatisticsMapper; @@ -71,6 +74,8 @@ public class WxPayServiceImpl implements WxPayService { @Autowired @Lazy private RedisTemplate redisTemplate; + @Autowired + private ProfitShareKafkaProducer profitShareKafkaProducer; @Override public Map createOrder(Long scenicId, WXPayOrderReqVO req) { @@ -121,6 +126,15 @@ public class WxPayServiceImpl implements WxPayService { invalidateStatisticsCache(scenicId); } }); + profitShareKafkaProducer.sendProfitShareMessage(OrderMessage.builder() + .orderId(callbackResponse.getOrderNo()) + .scenicId(scenicId) + .totalAmount(callbackResponse.getPayPrice() / 100.0) + .paymentSystem("wechat") + .paymentOrderId(callbackResponse.getTransactionId()) + .timestamp(System.currentTimeMillis() / 1000) + .build() + ); } catch (Exception e) { throw new AppException(BizCodeEnum.ADVANCE_PAYMENT_CALLBACK_FAILED, e.toString()); } @@ -143,23 +157,31 @@ public class WxPayServiceImpl implements WxPayService { IPayAdapter scenicPayAdapter = scenicService.getScenicPayAdapter(order.getScenicId()); BigDecimal payPrice = order.getPayPrice(); int priceInCents = payPrice.multiply(new BigDecimal(NumberConstant.HUNDRED)).intValue(); // 转换为分(int) - RefundOrderRequest request = new RefundOrderRequest() - .setOrderNo(orderId) - .setPrice(priceInCents) - .setRefundNo(SnowFlakeUtil.getId()) - .setRefundPrice(priceInCents) - .setNotifyUrl("https://zhentuai.com/api/mobile/wx/pay/v1/"+order.getScenicId()+"/refundNotify"); - RefundOrderResponse response = scenicPayAdapter.refund(request); - if (response.isSuccess()) { - OrderUpdateReq orderUpdateReq = new OrderUpdateReq(); - orderUpdateReq.setId(Long.parseLong(orderId)); - orderUpdateReq.setRefundStatus(OrderStateEnum.REFUNDED.getType()); - orderUpdateReq.setRefundAt(new Date()); - orderMapper.update(orderUpdateReq); - return true; - } else { - return false; - } + profitShareKafkaProducer.sendRefundMessage(RefundMessage.builder() + .refundOrderId(String.valueOf(order.getId())) + .originalOrderId(String.valueOf(order.getId())) + .refundAmount(payPrice.doubleValue()) + .refundType("full") + .paymentSystem("wechat") + .timestamp(System.currentTimeMillis() / 1000) + .build() + ).whenComplete((result, throwable) -> { + RefundOrderRequest request = new RefundOrderRequest() + .setOrderNo(orderId) + .setPrice(priceInCents) + .setRefundNo(SnowFlakeUtil.getId()) + .setRefundPrice(priceInCents) + .setNotifyUrl("https://zhentuai.com/api/mobile/wx/pay/v1/"+order.getScenicId()+"/refundNotify"); + RefundOrderResponse response = scenicPayAdapter.refund(request); + if (response.isSuccess()) { + OrderUpdateReq orderUpdateReq = new OrderUpdateReq(); + orderUpdateReq.setId(Long.parseLong(orderId)); + orderUpdateReq.setRefundStatus(OrderStateEnum.REFUNDED.getType()); + orderUpdateReq.setRefundAt(new Date()); + orderMapper.update(orderUpdateReq); + } + }); + return true; } @Override