feat(shop): 添加订单支付功能支持
- 新增 OrderPrepayRequest DTO 用于处理支付请求参数 - 实现 prepay 接口支持 /pay、/prepay、/repay 多路径兼容 - 添加用户登录验证和租户权限校验机制 - 集成微信支付创建订单功能并返回支付信息 - 实现订单状态验证包括已支付、已删除、已过期等状态检查 - 支持通过订单ID或订单号查询并处理支付请求 - 添加支付类型参数处理和默认值设置逻辑
This commit is contained in:
@@ -19,6 +19,7 @@ import com.gxwebsoft.shop.task.OrderAutoCancelTask;
|
||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||
import com.gxwebsoft.shop.param.ShopOrderParam;
|
||||
import com.gxwebsoft.shop.dto.OrderCreateRequest;
|
||||
import com.gxwebsoft.shop.dto.OrderPrepayRequest;
|
||||
import com.gxwebsoft.shop.dto.UpdatePaymentStatusRequest;
|
||||
import com.gxwebsoft.payment.service.PaymentService;
|
||||
import com.gxwebsoft.payment.dto.PaymentResponse;
|
||||
@@ -170,6 +171,92 @@ public class ShopOrderController extends BaseController {
|
||||
return fail("添加失败");
|
||||
}
|
||||
|
||||
@Operation(summary = "发起支付/重新支付(兼容 pay/prepay/repay)")
|
||||
@PostMapping({"/pay", "/prepay", "/repay"})
|
||||
public ApiResult<?> prepay(@RequestBody OrderPrepayRequest request) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) {
|
||||
return fail("用户未登录");
|
||||
}
|
||||
|
||||
// 允许从请求显式传 tenantId(兼容一些历史调用),否则优先从请求头/登录态推断
|
||||
Integer tenantId = request != null ? request.getTenantId() : null;
|
||||
if (tenantId == null) {
|
||||
tenantId = ObjectUtil.defaultIfNull(getTenantId(), loginUser.getTenantId());
|
||||
}
|
||||
|
||||
if (request == null || (request.getOrderId() == null && StrUtil.isBlank(request.getOrderNo()))) {
|
||||
return fail("orderId 或 orderNo 不能为空");
|
||||
}
|
||||
|
||||
ShopOrder order;
|
||||
if (request.getOrderId() != null) {
|
||||
order = shopOrderService.getById(request.getOrderId());
|
||||
} else {
|
||||
if (tenantId == null) {
|
||||
return fail("tenantId 不能为空");
|
||||
}
|
||||
order = shopOrderService.getByOrderNo(request.getOrderNo(), tenantId);
|
||||
}
|
||||
if (order == null) {
|
||||
return fail("订单不存在");
|
||||
}
|
||||
|
||||
// 校验租户(避免用别的租户订单号撞库)
|
||||
if (tenantId != null && order.getTenantId() != null && !tenantId.equals(order.getTenantId())) {
|
||||
return fail("订单不存在");
|
||||
}
|
||||
|
||||
// 普通用户只能操作自己的订单;管理员可越权(复用取消权限判断即可)
|
||||
if (!loginUser.getUserId().equals(order.getUserId()) && !hasOrderCancelAuthority()) {
|
||||
return fail("无权限操作此订单");
|
||||
}
|
||||
|
||||
// 业务状态校验:这些错误需要明确返回(code!=0),避免前端误判为接口不支持而降级走创建订单
|
||||
if (Boolean.TRUE.equals(order.getPayStatus())) {
|
||||
return fail("订单已支付");
|
||||
}
|
||||
if (order.getDeleted() != null && order.getDeleted() == 1) {
|
||||
return fail("订单已删除");
|
||||
}
|
||||
if (order.getOrderStatus() != null) {
|
||||
// 2=已取消;6=退款成功;7=客户端申请退款;其他非0状态也视为不可再次发起支付
|
||||
if (!Objects.equals(order.getOrderStatus(), 0)) {
|
||||
return fail("订单状态不允许发起支付");
|
||||
}
|
||||
}
|
||||
if (order.getExpirationTime() != null && order.getExpirationTime().isBefore(LocalDateTime.now())) {
|
||||
return fail("订单已过期");
|
||||
}
|
||||
|
||||
// 补齐 createWxOrder 所需字段(注意:openid 在 ShopOrder 上是非持久化字段,查询出来为空)
|
||||
Integer payType = request.getPayType() != null ? request.getPayType() : order.getPayType();
|
||||
if (payType == null) {
|
||||
payType = 1;
|
||||
}
|
||||
if (!Objects.equals(payType, 1) && !Objects.equals(payType, 102)) {
|
||||
return fail("该订单不支持发起微信支付");
|
||||
}
|
||||
order.setPayType(payType);
|
||||
order.setPayUserId(loginUser.getUserId());
|
||||
order.setOpenid(loginUser.getOpenid());
|
||||
if (order.getPayPrice() == null) {
|
||||
order.setPayPrice(ObjectUtil.defaultIfNull(order.getTotalPrice(), BigDecimal.ZERO));
|
||||
}
|
||||
if (StrUtil.isBlank(order.getComments())) {
|
||||
order.setComments("订单支付");
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> wxOrderInfo = shopOrderService.createWxOrder(order);
|
||||
return success(wxOrderInfo);
|
||||
} catch (Exception e) {
|
||||
logger.error("发起支付失败 - userId={}, orderId={}, orderNo={}",
|
||||
loginUser.getUserId(), order.getOrderId(), order.getOrderNo(), e);
|
||||
return fail("发起支付失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('shop:shopOrder:update')")
|
||||
@Operation(summary = "修改订单")
|
||||
@PutMapping()
|
||||
|
||||
32
src/main/java/com/gxwebsoft/shop/dto/OrderPrepayRequest.java
Normal file
32
src/main/java/com/gxwebsoft/shop/dto/OrderPrepayRequest.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.gxwebsoft.shop.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* 订单重新发起支付请求DTO
|
||||
*
|
||||
* 前端会按 /shop/shop-order/pay -> /prepay -> /repay 依次尝试。
|
||||
* 后端可统一实现为同一套逻辑(多个URL别名),避免出现 404 导致前端误判为不支持接口。
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "OrderPrepayRequest", description = "订单重新发起支付请求")
|
||||
public class OrderPrepayRequest {
|
||||
|
||||
@Schema(description = "订单ID(二选一:orderId 或 orderNo)")
|
||||
@Positive(message = "订单ID必须为正数")
|
||||
private Integer orderId;
|
||||
|
||||
@Schema(description = "订单号(二选一:orderId 或 orderNo)")
|
||||
private String orderNo;
|
||||
|
||||
@Schema(description = "支付方式:1=微信支付,102=微信Native(兼容旧类型)。不传则使用订单原支付方式/默认微信支付")
|
||||
private Integer payType;
|
||||
|
||||
@Schema(description = "租户ID(可选;不传则从当前登录用户/请求头推断)")
|
||||
@Positive(message = "租户ID必须为正数")
|
||||
private Integer tenantId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user