8.4 小型商城退单对接拼团系统

This commit is contained in:
zhangsan 2025-08-05 22:29:33 +08:00
parent 636742e53d
commit 27f009b375
13 changed files with 285 additions and 17 deletions

View File

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

View File

@ -108,4 +108,10 @@
where user_id = #{userId} and order_id = #{orderId} where user_id = #{userId} and order_id = #{orderId}
</update> </update>
<update id="refundMarketOrder">
update pay_order
set status = 'WAIT_REFUND', update_time = now()
where user_id = #{userId} and order_id = #{orderId}
</update>
</mapper> </mapper>

View File

@ -13,4 +13,6 @@ public interface IProductPort {
void settlementMarketPayOrder(String userId, String orderId, Date orderTime); void settlementMarketPayOrder(String userId, String orderId, Date orderTime);
void refundMarketPayOrder(String userId, String orderId);
} }

View File

@ -34,4 +34,6 @@ public interface IOrderRepository {
OrderEntity queryOrderByUserIdAndOrderId(String userId, String orderId); OrderEntity queryOrderByUserIdAndOrderId(String userId, String orderId);
boolean refundOrder(String userId, String orderId); boolean refundOrder(String userId, String orderId);
boolean refundMarketOrder(String userId, String orderId);
} }

View File

@ -13,6 +13,7 @@ public enum OrderStatusVO {
DEAL_DONE("DEAL_DONE", "交易完成 - 商品发货完成"), DEAL_DONE("DEAL_DONE", "交易完成 - 商品发货完成"),
CLOSE("CLOSE", "超时关单 - 超市未支付"), CLOSE("CLOSE", "超时关单 - 超市未支付"),
MARKET("MARKET", "营销结算 - 拼团组队完成"), MARKET("MARKET", "营销结算 - 拼团组队完成"),
WAIT_REFUND("WAIT_REFUND", "营销退单待退款"),
; ;
private final String code; private final String code;

View File

@ -1,6 +1,7 @@
package edu.whut.domain.order.service; 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.OrderEntity;
import edu.whut.domain.order.model.entity.PayOrderEntity; import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity; import edu.whut.domain.order.model.entity.ShopCartEntity;
@ -24,6 +25,14 @@ public interface IOrderService {
List<OrderEntity> queryUserOrderList(String userId, Long lastId, Integer pageSize); List<OrderEntity> 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;
} }

View File

@ -2,7 +2,10 @@ package edu.whut.domain.order.service;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient; import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradePagePayRequest; 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.port.IProductPort;
import edu.whut.domain.order.adapter.repository.IOrderRepository; import edu.whut.domain.order.adapter.repository.IOrderRepository;
import edu.whut.domain.order.model.aggregate.CreateOrderAggregate; import edu.whut.domain.order.model.aggregate.CreateOrderAggregate;
@ -143,7 +146,7 @@ public class OrderService extends AbstractOrderService{
} }
@Override @Override
public boolean refundOrder(String userId, String orderId) { public boolean refundMarketOrder(String userId, String orderId) {
// 1. 查询订单信息验证订单是否存在且属于该用户 // 1. 查询订单信息验证订单是否存在且属于该用户
OrderEntity orderEntity = repository.queryOrderByUserIdAndOrderId(userId, orderId); OrderEntity orderEntity = repository.queryOrderByUserIdAndOrderId(userId, orderId);
if (null == orderEntity) { if (null == orderEntity) {
@ -158,16 +161,46 @@ public class OrderService extends AbstractOrderService{
return false; return false;
} }
// 3. 对于营销类型的单子调用拼团执行组队退单 todo // 3. 对于营销类型的单子调用拼团执行组队退单
port.refundMarketPayOrder(userId, orderId);
// 4. 执行退单操作 // 4. 执行退单操作
boolean result = repository.refundOrder(userId, orderId); if (OrderStatusVO.CREATE.getCode().equals(status) || OrderStatusVO.PAY_WAIT.getCode().equals(status)) {
return repository.refundOrder(userId, orderId);
} else {
boolean result = repository.refundMarketOrder(userId, orderId);
if (result) { if (result) {
log.info("退单成功 userId:{} orderId:{}", userId, orderId); log.info("退单成功 userId:{} orderId:{}", userId, orderId);
} else { } else {
log.warn("退单失败 userId:{} orderId:{}", userId, orderId); log.warn("退单失败 userId:{} orderId:{}", userId, orderId);
} }
return result; 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;
}
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;
}
}

View File

@ -1,10 +1,7 @@
package edu.whut.infrastructure.adapter.port; package edu.whut.infrastructure.adapter.port;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import edu.whut.api.dto.LockMarketPayOrderRequestDTO; import edu.whut.api.dto.*;
import edu.whut.api.dto.LockMarketPayOrderResponseDTO;
import edu.whut.api.dto.SettlementMarketPayOrderRequestDTO;
import edu.whut.api.dto.SettlementMarketPayOrderResponseDTO;
import edu.whut.api.response.Response; import edu.whut.api.response.Response;
import edu.whut.domain.order.adapter.port.IProductPort; import edu.whut.domain.order.adapter.port.IProductPort;
import edu.whut.domain.order.model.entity.MarketPayDiscountEntity; 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<Response<RefundMarketPayOrderResponseDTO>> call = groupBuyMarketService.refundMarketPayOrder(requestDTO);
// 获取结果
Response<RefundMarketPayOrderResponseDTO> 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);
}
}
} }

View File

@ -275,4 +275,9 @@ public class OrderRepository implements IOrderRepository {
public boolean refundOrder(String userId, String orderId) { public boolean refundOrder(String userId, String orderId) {
return orderDao.refundOrder(userId, orderId); return orderDao.refundOrder(userId, orderId);
} }
@Override
public boolean refundMarketOrder(String userId, String orderId) {
return orderDao.refundMarketOrder(userId, orderId);
}
} }

View File

@ -34,5 +34,7 @@ public interface IOrderDao {
boolean refundOrder(@Param("userId") String userId, @Param("orderId") String orderId); boolean refundOrder(@Param("userId") String userId, @Param("orderId") String orderId);
boolean refundMarketOrder(@Param("userId") String userId, @Param("orderId") String orderId);
} }

View File

@ -1,9 +1,6 @@
package edu.whut.infrastructure.gateway; package edu.whut.infrastructure.gateway;
import edu.whut.api.dto.LockMarketPayOrderRequestDTO; import edu.whut.api.dto.*;
import edu.whut.api.dto.LockMarketPayOrderResponseDTO;
import edu.whut.api.dto.SettlementMarketPayOrderRequestDTO;
import edu.whut.api.dto.SettlementMarketPayOrderResponseDTO;
import edu.whut.api.response.Response; import edu.whut.api.response.Response;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
@ -32,5 +29,14 @@ public interface IGroupBuyMarketService {
@POST("api/v1/gbm/trade/settlement_market_pay_order") @POST("api/v1/gbm/trade/settlement_market_pay_order")
Call<Response<SettlementMarketPayOrderResponseDTO>> settlementMarketPayOrder(@Body SettlementMarketPayOrderRequestDTO requestDTO); Call<Response<SettlementMarketPayOrderResponseDTO>> settlementMarketPayOrder(@Body SettlementMarketPayOrderRequestDTO requestDTO);
/**
* 营销拼团退单
*
* @param requestDTO 退单请求信息
* @return 退单结果信息
*/
@POST("api/v1/gbm/trade/refund_market_pay_order")
Call<Response<RefundMarketPayOrderResponseDTO>> refundMarketPayOrder(@Body RefundMarketPayOrderRequestDTO requestDTO);
} }

View File

@ -1,7 +1,11 @@
package edu.whut.trigger.http; package edu.whut.trigger.http;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException; 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.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import edu.whut.api.IPayService; import edu.whut.api.IPayService;
import edu.whut.api.dto.*; import edu.whut.api.dto.*;
import edu.whut.api.response.Response; import edu.whut.api.response.Response;
@ -35,6 +39,8 @@ public class AliPayController implements IPayService {
private final IOrderService orderService; private final IOrderService orderService;
private final AlipayClient alipayClient;
/** /**
* { * {
* "userId": "10001", * "userId": "10001",
@ -213,7 +219,7 @@ public class AliPayController implements IPayService {
String orderId = requestDTO.getOrderId(); String orderId = requestDTO.getOrderId();
// 执行退单操作 // 执行退单操作
boolean success = orderService.refundOrder(userId, orderId); boolean success = orderService.refundMarketOrder(userId, orderId);
RefundOrderResponseDTO responseDTO = new RefundOrderResponseDTO(); RefundOrderResponseDTO responseDTO = new RefundOrderResponseDTO();
responseDTO.setSuccess(success); responseDTO.setSuccess(success);
@ -242,4 +248,79 @@ public class AliPayController implements IPayService {
} }
} }
/**
* 测试回调接口 - 主动查询支付宝交易状态
*/
@RequestMapping(value = "active_pay_notify", method = RequestMethod.POST)
public Response<String> 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.<String>builder()
.code(Constants.ResponseCode.SUCCESS.getCode())
.info(Constants.ResponseCode.SUCCESS.getInfo())
.data("交易成功,订单状态已更新")
.build();
} else {
log.info("交易状态非成功状态: {}, 订单号: {}", tradeStatus, outTradeNo);
return Response.<String>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.<String>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.<String>builder()
.code(Constants.ResponseCode.UN_ERROR.getCode())
.info(Constants.ResponseCode.UN_ERROR.getInfo())
.data("系统异常: " + e.getMessage())
.build();
}
}
} }

View File

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