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