feat(profit-share): 实现分账消息发送功能

- 修改 ProfitShareKafkaProducer 的 sendRefundMessage 方法返回 CompletableFuture
- 在 WxMpPayAdapter 中增加 transactionId 和 refundTransactionId 字段解析
- 在 PayResponse 和 RefundResponse 中新增 transactionId 相关字段
- 在 WxPayServiceImpl 中注入 ProfitShareKafkaProducer 并发送分账消息
- 调整退款逻辑以异步方式发送分账退款消息后再执行退款操作
This commit is contained in:
2025-12-16 17:58:20 +08:00
parent a9555d612a
commit a9c33352f7
5 changed files with 48 additions and 19 deletions

View File

@@ -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<SendResult<String, String>> 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) {

View File

@@ -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) {
//退款成功

View File

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

View File

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

View File

@@ -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<String, String> redisTemplate;
@Autowired
private ProfitShareKafkaProducer profitShareKafkaProducer;
@Override
public Map<String, Object> 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