From 27f009b375807bef5f3785686e3fb4d0a4ebb3b9 Mon Sep 17 00:00:00 2001
From: zhangsan <646228430@qq.com>
Date: Tue, 5 Aug 2025 22:29:33 +0800
Subject: [PATCH] =?UTF-8?q?8.4=20=E5=B0=8F=E5=9E=8B=E5=95=86=E5=9F=8E?=
=?UTF-8?q?=E9=80=80=E5=8D=95=E5=AF=B9=E6=8E=A5=E6=8B=BC=E5=9B=A2=E7=B3=BB?=
=?UTF-8?q?=E7=BB=9F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../api/dto/TeamRefundSuccessRequestDTO.java | 47 +++++++++++
.../mybatis/mapper/pay_order_mapper.xml | 6 ++
.../order/adapter/port/IProductPort.java | 2 +
.../adapter/repository/IOrderRepository.java | 2 +
.../order/model/valobj/OrderStatusVO.java | 1 +
.../domain/order/service/IOrderService.java | 11 ++-
.../domain/order/service/OrderService.java | 47 +++++++++--
.../adapter/port/ProductPort.java | 30 ++++++-
.../adapter/repository/OrderRepository.java | 5 ++
.../whut/infrastructure/dao/IOrderDao.java | 2 +
.../gateway/IGroupBuyMarketService.java | 14 +++-
.../whut/trigger/http/AliPayController.java | 83 ++++++++++++++++++-
.../listener/RefundSuccessTopicListener.java | 52 ++++++++++++
13 files changed, 285 insertions(+), 17 deletions(-)
create mode 100644 pay-mall-api/src/main/java/edu/whut/api/dto/TeamRefundSuccessRequestDTO.java
create mode 100644 pay-mall-trigger/src/main/java/edu/whut/trigger/listener/RefundSuccessTopicListener.java
diff --git a/pay-mall-api/src/main/java/edu/whut/api/dto/TeamRefundSuccessRequestDTO.java b/pay-mall-api/src/main/java/edu/whut/api/dto/TeamRefundSuccessRequestDTO.java
new file mode 100644
index 0000000..1951f43
--- /dev/null
+++ b/pay-mall-api/src/main/java/edu/whut/api/dto/TeamRefundSuccessRequestDTO.java
@@ -0,0 +1,47 @@
+package edu.whut.api.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 拼团退单消息对象
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class TeamRefundSuccessRequestDTO {
+
+ /**
+ * 退单类型
+ */
+ private String type;
+
+ /**
+ * 用户ID
+ */
+ private String userId;
+
+ /**
+ * 拼单组队ID
+ */
+ private String teamId;
+
+ /**
+ * 活动ID
+ */
+ private Long activityId;
+
+ /**
+ * 预购订单ID
+ */
+ private String orderId;
+
+ /**
+ * 外部交易单号
+ */
+ private String outTradeNo;
+
+}
diff --git a/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml b/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml
index cdc3a59..2ace9b5 100644
--- a/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml
+++ b/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml
@@ -108,4 +108,10 @@
where user_id = #{userId} and order_id = #{orderId}
+
+ update pay_order
+ set status = 'WAIT_REFUND', update_time = now()
+ where user_id = #{userId} and order_id = #{orderId}
+
+
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java
index c546186..6fe62c5 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java
@@ -13,4 +13,6 @@ public interface IProductPort {
void settlementMarketPayOrder(String userId, String orderId, Date orderTime);
+
+ void refundMarketPayOrder(String userId, String orderId);
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java
index bbf104c..71151b2 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java
@@ -34,4 +34,6 @@ public interface IOrderRepository {
OrderEntity queryOrderByUserIdAndOrderId(String userId, String orderId);
boolean refundOrder(String userId, String orderId);
+
+ boolean refundMarketOrder(String userId, String orderId);
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/valobj/OrderStatusVO.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/valobj/OrderStatusVO.java
index a9a402d..869556c 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/valobj/OrderStatusVO.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/valobj/OrderStatusVO.java
@@ -13,6 +13,7 @@ public enum OrderStatusVO {
DEAL_DONE("DEAL_DONE", "交易完成 - 商品发货完成"),
CLOSE("CLOSE", "超时关单 - 超市未支付"),
MARKET("MARKET", "营销结算 - 拼团组队完成"),
+ WAIT_REFUND("WAIT_REFUND", "营销退单待退款"),
;
private final String code;
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/IOrderService.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/IOrderService.java
index 56714ef..2a797d9 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/IOrderService.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/IOrderService.java
@@ -1,6 +1,7 @@
package edu.whut.domain.order.service;
+import com.alipay.api.AlipayApiException;
import edu.whut.domain.order.model.entity.OrderEntity;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
@@ -24,6 +25,14 @@ public interface IOrderService {
List queryUserOrderList(String userId, Long lastId, Integer pageSize);
- boolean refundOrder(String userId, String orderId);
+ /**
+ * 营销退单
+ */
+ boolean refundMarketOrder(String userId, String orderId);
+
+ /**
+ * 接收拼团退单消息
+ */
+ boolean refundPayOrder(String userId, String orderId) throws AlipayApiException;
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java
index a53eeee..4fb2060 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java
@@ -2,7 +2,10 @@ package edu.whut.domain.order.service;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
+import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradePagePayRequest;
+import com.alipay.api.request.AlipayTradeRefundRequest;
+import com.alipay.api.response.AlipayTradeRefundResponse;
import edu.whut.domain.order.adapter.port.IProductPort;
import edu.whut.domain.order.adapter.repository.IOrderRepository;
import edu.whut.domain.order.model.aggregate.CreateOrderAggregate;
@@ -143,7 +146,7 @@ public class OrderService extends AbstractOrderService{
}
@Override
- public boolean refundOrder(String userId, String orderId) {
+ public boolean refundMarketOrder(String userId, String orderId) {
// 1. 查询订单信息,验证订单是否存在且属于该用户
OrderEntity orderEntity = repository.queryOrderByUserIdAndOrderId(userId, orderId);
if (null == orderEntity) {
@@ -158,16 +161,46 @@ public class OrderService extends AbstractOrderService{
return false;
}
- // 3. 对于营销类型的单子,调用拼团执行组队退单 todo
+ // 3. 对于营销类型的单子,调用拼团执行组队退单
+ port.refundMarketPayOrder(userId, orderId);
// 4. 执行退单操作
- boolean result = repository.refundOrder(userId, orderId);
- if (result) {
- log.info("退单成功 userId:{} orderId:{}", userId, orderId);
+ if (OrderStatusVO.CREATE.getCode().equals(status) || OrderStatusVO.PAY_WAIT.getCode().equals(status)) {
+ return repository.refundOrder(userId, orderId);
} else {
- log.warn("退单失败 userId:{} orderId:{}", userId, orderId);
+ boolean result = repository.refundMarketOrder(userId, orderId);
+ if (result) {
+ log.info("退单成功 userId:{} orderId:{}", userId, orderId);
+ } else {
+ log.warn("退单失败 userId:{} orderId:{}", userId, orderId);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public boolean refundPayOrder(String userId, String orderId) throws AlipayApiException {
+ // 1. 查询订单信息,验证订单是否存在且属于该用户
+ OrderEntity orderEntity = repository.queryOrderByUserIdAndOrderId(userId, orderId);
+ if (null == orderEntity) {
+ log.warn("退款失败,订单不存在或不属于该用户 userId:{} orderId:{}", userId, orderId);
+ return false;
}
- return result;
+ AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+ AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
+ refundModel.setOutTradeNo(orderEntity.getOrderId());
+ refundModel.setRefundAmount(orderEntity.getPayAmount().toString());
+ refundModel.setRefundReason("交易退单");
+ request.setBizModel(refundModel);
+
+ // 交易退款
+ AlipayTradeRefundResponse execute = alipayClient.execute(request);
+ if (!execute.isSuccess()) return false;
+
+ // 状态变更
+ repository.refundOrder(userId, orderId);
+
+ return true;
}
}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java
index dc60a97..b871f66 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java
@@ -1,10 +1,7 @@
package edu.whut.infrastructure.adapter.port;
import com.alibaba.fastjson.JSON;
-import edu.whut.api.dto.LockMarketPayOrderRequestDTO;
-import edu.whut.api.dto.LockMarketPayOrderResponseDTO;
-import edu.whut.api.dto.SettlementMarketPayOrderRequestDTO;
-import edu.whut.api.dto.SettlementMarketPayOrderResponseDTO;
+import edu.whut.api.dto.*;
import edu.whut.api.response.Response;
import edu.whut.domain.order.adapter.port.IProductPort;
import edu.whut.domain.order.model.entity.MarketPayDiscountEntity;
@@ -137,5 +134,30 @@ public class ProductPort implements IProductPort {
}
}
+ @Override
+ public void refundMarketPayOrder(String userId, String orderId) {
+ RefundMarketPayOrderRequestDTO requestDTO = new RefundMarketPayOrderRequestDTO();
+ requestDTO.setSource(source);
+ requestDTO.setChannel(chanel);
+ requestDTO.setUserId(userId);
+ requestDTO.setOutTradeNo(orderId);
+
+ try {
+ Call> call = groupBuyMarketService.refundMarketPayOrder(requestDTO);
+
+ // 获取结果
+ Response response = call.execute().body();
+ log.info("营销退单{} requestDTO:{} responseDTO:{}", userId, JSON.toJSONString(requestDTO), JSON.toJSONString(response));
+ if (null == response) return;
+
+ // 异常判断
+ if (!"0000".equals(response.getCode())) {
+ throw new AppException(response.getCode(), response.getInfo());
+ }
+
+ } catch (Exception e) {
+ log.error("营销退单失败{}", userId, e);
+ }
+ }
}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java
index 34d9b4c..408ecc4 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java
@@ -275,4 +275,9 @@ public class OrderRepository implements IOrderRepository {
public boolean refundOrder(String userId, String orderId) {
return orderDao.refundOrder(userId, orderId);
}
+
+ @Override
+ public boolean refundMarketOrder(String userId, String orderId) {
+ return orderDao.refundMarketOrder(userId, orderId);
+ }
}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java
index 29406c8..8813766 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java
@@ -34,5 +34,7 @@ public interface IOrderDao {
boolean refundOrder(@Param("userId") String userId, @Param("orderId") String orderId);
+ boolean refundMarketOrder(@Param("userId") String userId, @Param("orderId") String orderId);
+
}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/IGroupBuyMarketService.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/IGroupBuyMarketService.java
index 27928c9..0e28a14 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/IGroupBuyMarketService.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/IGroupBuyMarketService.java
@@ -1,9 +1,6 @@
package edu.whut.infrastructure.gateway;
-import edu.whut.api.dto.LockMarketPayOrderRequestDTO;
-import edu.whut.api.dto.LockMarketPayOrderResponseDTO;
-import edu.whut.api.dto.SettlementMarketPayOrderRequestDTO;
-import edu.whut.api.dto.SettlementMarketPayOrderResponseDTO;
+import edu.whut.api.dto.*;
import edu.whut.api.response.Response;
import retrofit2.Call;
import retrofit2.http.Body;
@@ -32,5 +29,14 @@ public interface IGroupBuyMarketService {
@POST("api/v1/gbm/trade/settlement_market_pay_order")
Call> settlementMarketPayOrder(@Body SettlementMarketPayOrderRequestDTO requestDTO);
+ /**
+ * 营销拼团退单
+ *
+ * @param requestDTO 退单请求信息
+ * @return 退单结果信息
+ */
+ @POST("api/v1/gbm/trade/refund_market_pay_order")
+ Call> refundMarketPayOrder(@Body RefundMarketPayOrderRequestDTO requestDTO);
+
}
diff --git a/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java
index dd08253..7b09643 100644
--- a/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java
+++ b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java
@@ -1,7 +1,11 @@
package edu.whut.trigger.http;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.internal.util.AlipaySignature;
+import com.alipay.api.request.AlipayTradeQueryRequest;
import edu.whut.api.IPayService;
import edu.whut.api.dto.*;
import edu.whut.api.response.Response;
@@ -35,6 +39,8 @@ public class AliPayController implements IPayService {
private final IOrderService orderService;
+ private final AlipayClient alipayClient;
+
/**
* {
* "userId": "10001",
@@ -213,7 +219,7 @@ public class AliPayController implements IPayService {
String orderId = requestDTO.getOrderId();
// 执行退单操作
- boolean success = orderService.refundOrder(userId, orderId);
+ boolean success = orderService.refundMarketOrder(userId, orderId);
RefundOrderResponseDTO responseDTO = new RefundOrderResponseDTO();
responseDTO.setSuccess(success);
@@ -242,4 +248,79 @@ public class AliPayController implements IPayService {
}
}
+ /**
+ * 测试回调接口 - 主动查询支付宝交易状态
+ */
+ @RequestMapping(value = "active_pay_notify", method = RequestMethod.POST)
+ public Response activePayNotify(@RequestParam String outTradeNo) {
+ try {
+ log.info("测试回调接口,开始查询订单: {}", outTradeNo);
+
+ // 构建支付宝交易查询请求
+ AlipayTradeQueryModel bizModel = new AlipayTradeQueryModel();
+ bizModel.setOutTradeNo(outTradeNo);
+
+ AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
+ queryRequest.setBizModel(bizModel);
+
+ // 调用支付宝API查询交易状态
+ String body = alipayClient.execute(queryRequest).getBody();
+ log.info("支付宝查询结果: {}", body);
+
+ // 解析查询结果
+ JSONObject responseJson = JSON.parseObject(body);
+ JSONObject queryResponse = responseJson.getJSONObject("alipay_trade_query_response");
+
+ if (queryResponse != null && "10000".equals(queryResponse.getString("code"))) {
+ String tradeStatus = queryResponse.getString("trade_status");
+ String tradeNo = queryResponse.getString("trade_no");
+ String totalAmount = queryResponse.getString("total_amount");
+ String gmtPayment = queryResponse.getString("send_pay_date");
+
+ log.info("查询成功 - 交易状态: {}, 支付宝交易号: {}, 金额: {}, 支付时间: {}",
+ tradeStatus, tradeNo, totalAmount, gmtPayment);
+
+ // 如果交易成功,执行后续流程处理
+ if ("TRADE_SUCCESS".equals(tradeStatus)) {
+ log.info("交易成功,开始处理后续流程,订单号: {}", outTradeNo);
+
+ // 调用订单服务更新订单状态
+ orderService.changeOrderPaySuccess(outTradeNo,
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(gmtPayment));
+
+ log.info("订单状态更新成功,订单号: {}", outTradeNo);
+
+ return Response.builder()
+ .code(Constants.ResponseCode.SUCCESS.getCode())
+ .info(Constants.ResponseCode.SUCCESS.getInfo())
+ .data("交易成功,订单状态已更新")
+ .build();
+ } else {
+ log.info("交易状态非成功状态: {}, 订单号: {}", tradeStatus, outTradeNo);
+ return Response.builder()
+ .code(Constants.ResponseCode.SUCCESS.getCode())
+ .info(Constants.ResponseCode.SUCCESS.getInfo())
+ .data("交易状态: " + tradeStatus)
+ .build();
+ }
+ } else {
+ String errorMsg = queryResponse != null ? queryResponse.getString("msg") : "查询失败";
+ log.error("支付宝查询失败: {}, 订单号: {}", errorMsg, outTradeNo);
+ return Response.builder()
+ .code(Constants.ResponseCode.UN_ERROR.getCode())
+ .info(Constants.ResponseCode.UN_ERROR.getInfo())
+ .data("查询失败: " + errorMsg)
+ .build();
+ }
+
+ } catch (Exception e) {
+ log.error("测试回调接口异常,订单号: {}", outTradeNo, e);
+ return Response.builder()
+ .code(Constants.ResponseCode.UN_ERROR.getCode())
+ .info(Constants.ResponseCode.UN_ERROR.getInfo())
+ .data("系统异常: " + e.getMessage())
+ .build();
+ }
+ }
+
}
diff --git a/pay-mall-trigger/src/main/java/edu/whut/trigger/listener/RefundSuccessTopicListener.java b/pay-mall-trigger/src/main/java/edu/whut/trigger/listener/RefundSuccessTopicListener.java
new file mode 100644
index 0000000..8d8729d
--- /dev/null
+++ b/pay-mall-trigger/src/main/java/edu/whut/trigger/listener/RefundSuccessTopicListener.java
@@ -0,0 +1,52 @@
+package edu.whut.trigger.listener;
+import com.alibaba.fastjson.JSON;
+import com.alipay.api.AlipayApiException;
+import edu.whut.api.dto.TeamRefundSuccessRequestDTO;
+import edu.whut.domain.order.service.IOrderService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.ExchangeTypes;
+import org.springframework.amqp.rabbit.annotation.Exchange;
+import org.springframework.amqp.rabbit.annotation.Queue;
+import org.springframework.amqp.rabbit.annotation.QueueBinding;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 营销退单成功消息
+ */
+@Slf4j
+@Component
+public class RefundSuccessTopicListener {
+
+ @Resource
+ private IOrderService orderService;
+
+ /**
+ * 指定消费队列
+ */
+ @RabbitListener(
+ bindings = @QueueBinding(
+ value = @Queue(value = "${spring.rabbitmq.config.consumer.topic_team_refund.queue}"),
+ exchange = @Exchange(value = "${spring.rabbitmq.config.consumer.topic_team_refund.exchange}", type = ExchangeTypes.TOPIC),
+ key = "${spring.rabbitmq.config.consumer.topic_team_refund.routing_key}"
+ )
+ )
+ public void listener(String message) {
+ try {
+ log.info("退单回调,发起退款 {}", message);
+ TeamRefundSuccessRequestDTO requestDTO = JSON.parseObject(message, TeamRefundSuccessRequestDTO.class);
+ String type = requestDTO.getType();
+ if ("paid_unformed".equals(type) || "paid_formed".equals(type)) {
+ orderService.refundPayOrder(requestDTO.getUserId(), requestDTO.getOutTradeNo());
+ }
+ } catch (AlipayApiException ex) {
+ throw new RuntimeException(ex);
+ } catch (Exception e) {
+ log.error("拼团回调,退单完成,退款失败 {}", message, e);
+ throw e;
+ }
+ }
+
+}