From c5da6f371b0c7e35a28972523791373bc04cc808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Wed, 4 Feb 2026 17:38:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(order):=20=E5=88=86=E7=A6=BB=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E9=80=80=E6=AC=BE=E5=8A=9F=E8=83=BD=E5=88=B0=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E6=8E=A5=E5=8F=A3=E5=B9=B6=E4=BC=98=E5=8C=96=E6=B0=B4?= =?UTF-8?q?=E7=A5=A8=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将订单退款逻辑从update方法中分离到独立的refund接口 - 添加退款相关操作权限控制和参数验证 - 实现申请退款和同意退款两种状态的分别处理 - 新增水票总数统计功能,包括service、mapper和controller层实现 - 修改佣金注释文本从"第3级佣金"为"分润收入" - 优化订单更新逻辑,禁止通过普通更新接口进行退款操作 --- .../controller/GltUserTicketController.java | 18 +- .../glt/mapper/GltUserTicketMapper.java | 8 + .../glt/mapper/xml/GltUserTicketMapper.xml | 8 + .../glt/service/GltUserTicketService.java | 8 + .../impl/GltUserTicketServiceImpl.java | 6 + .../shop/controller/ShopOrderController.java | 225 +++++++++--------- .../task/DealerOrderSettlement10584Task.java | 2 +- 7 files changed, 165 insertions(+), 110 deletions(-) diff --git a/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketController.java b/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketController.java index 8452e72..15848a1 100644 --- a/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketController.java +++ b/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketController.java @@ -1,13 +1,11 @@ package com.gxwebsoft.glt.controller; import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.glt.service.GltUserTicketService; import com.gxwebsoft.glt.entity.GltUserTicket; import com.gxwebsoft.glt.param.GltUserTicketParam; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.core.annotation.OperationLog; import io.swagger.v3.oas.annotations.Operation; @@ -16,7 +14,9 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 我的水票控制器 @@ -38,6 +38,20 @@ public class GltUserTicketController extends BaseController { return success(gltUserTicketService.pageRel(param)); } + @Operation(summary = "我的水票总数") + @GetMapping("/my-total") + public ApiResult myTotal() { + Integer userId = getLoginUserId(); + if (userId == null) { + return fail("未登录"); + } + Integer totalQty = gltUserTicketService.sumTotalQtyByUserId(userId); + Map data = new HashMap<>(); + data.put("userId", userId); + data.put("totalQty", totalQty); + return success(data); + } + @PreAuthorize("hasAuthority('glt:gltUserTicket:list')") @Operation(summary = "查询全部我的水票") @GetMapping() diff --git a/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketMapper.java b/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketMapper.java index b521b57..1ec8167 100644 --- a/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketMapper.java +++ b/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketMapper.java @@ -34,4 +34,12 @@ public interface GltUserTicketMapper extends BaseMapper { */ List selectListRel(@Param("param") GltUserTicketParam param); + /** + * 统计用户水票总数量(sum(total_qty)) + * + * @param userId 用户ID + * @return 总数量 + */ + Integer sumTotalQtyByUserId(@Param("userId") Integer userId); + } diff --git a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketMapper.xml b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketMapper.xml index 13b32f6..90ffe7c 100644 --- a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketMapper.xml +++ b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketMapper.xml @@ -86,4 +86,12 @@ + + + diff --git a/src/main/java/com/gxwebsoft/glt/service/GltUserTicketService.java b/src/main/java/com/gxwebsoft/glt/service/GltUserTicketService.java index 46245ea..1c292e9 100644 --- a/src/main/java/com/gxwebsoft/glt/service/GltUserTicketService.java +++ b/src/main/java/com/gxwebsoft/glt/service/GltUserTicketService.java @@ -39,4 +39,12 @@ public interface GltUserTicketService extends IService { */ GltUserTicket getByIdRel(Integer id); + /** + * 统计指定用户水票总数量(sum(total_qty)) + * + * @param userId 用户ID + * @return 总数量(无记录返回0) + */ + Integer sumTotalQtyByUserId(Integer userId); + } diff --git a/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketServiceImpl.java b/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketServiceImpl.java index 8c4ee06..06b171f 100644 --- a/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketServiceImpl.java +++ b/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketServiceImpl.java @@ -44,4 +44,10 @@ public class GltUserTicketServiceImpl extends ServiceImpl update(@RequestBody ShopOrder shopOrder) throws Exception { - // 1. 验证订单是否可以退款 if (shopOrder == null) { - return fail("订单不存在"); + return fail("订单不存在"); + } + // 退款相关操作单独走退款接口,便于做财务权限隔离 + if (Objects.equals(shopOrder.getOrderStatus(), 4) || Objects.equals(shopOrder.getOrderStatus(), 6)) { + return fail("退款相关操作请使用退款接口: PUT /api/shop/shop-order/refund"); } ShopOrder shopOrderNow = shopOrderService.getById(shopOrder.getOrderId()); if (shopOrderNow == null) { return fail("订单不存在"); } - // 申请退款 - if (Objects.equals(shopOrder.getOrderStatus(), 4)) { - shopOrder.setRefundApplyTime(LocalDateTime.now()); - } // 发货状态从“未发货(10)”变更为“已发货(20)”时,记录发货信息 if (Objects.equals(shopOrderNow.getDeliveryStatus(), 10) && Objects.equals(shopOrder.getDeliveryStatus(), 20)) { ShopOrderDelivery shopOrderDelivery = new ShopOrderDelivery(); @@ -287,113 +286,118 @@ public class ShopOrderController extends BaseController { shopOrderDeliveryService.setExpress(getLoginUser(), shopOrderDelivery, shopOrder); } - // 退款操作 - if (Objects.equals(shopOrder.getOrderStatus(), 6)) { - // 当订单状态更改为6(已退款)时,执行退款操作 - try { - - // 检查订单是否已支付 - if (!Boolean.TRUE.equals(shopOrderNow.getPayStatus())) { - return fail("订单未支付,无法退款"); - } - - // 检查是否已经退款过了 - if (StrUtil.isNotBlank(shopOrderNow.getRefundOrder())) { - logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), shopOrderNow.getRefundOrder()); - return fail("订单已退款,请勿重复操作"); - } - - // 2. 生成退款单号 - String refundNo = "RF" + IdUtil.getSnowflakeNextId(); - - // 3. 确定退款金额(默认全额退款) - BigDecimal refundAmount = shopOrder.getRefundMoney(); - if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { - // 如果没有指定退款金额,使用订单实付金额 - refundAmount = shopOrderNow.getTotalPrice(); - } - - // 验证退款金额不能大于订单金额 - if (refundAmount.compareTo(shopOrderNow.getTotalPrice()) > 0) { - return fail("退款金额不能大于订单金额"); - } - - // 4. 确定支付类型(默认为微信Native支付) - PaymentType paymentType = PaymentType.WECHAT_NATIVE; - if (shopOrderNow.getPayType() != null) { - // 根据订单的支付类型确定 - // 支付方式:0余额支付,1微信支付,2支付宝支付,3银联支付,4现金支付,5POS机支付,6免费,7积分支付 - paymentType = PaymentType.getByCode(shopOrderNow.getPayType()); - - // 如果是微信支付,需要根据微信支付子类型确定具体的支付方式 - if (paymentType == PaymentType.WECHAT) { - // 目前统一使用WECHAT_NATIVE进行退款 - paymentType = PaymentType.WECHAT_NATIVE; - } - } - - // 5. 调用统一支付服务的退款接口 - logger.info("开始处理订单退款 - 订单号: {}, 退款单号: {}, 退款金额: {}, 支付方式: {}", - shopOrderNow.getOrderNo(), refundNo, refundAmount, paymentType); - - PaymentResponse refundResponse = paymentService.refund( - shopOrderNow.getOrderNo(), // 原订单号 - refundNo, // 退款单号 - paymentType, // 支付方式 - shopOrderNow.getTotalPrice(), // 订单总金额 - refundAmount, // 退款金额 - shopOrder.getRefundReason() != null ? shopOrder.getRefundReason() : "用户申请退款", // 退款原因 - shopOrderNow.getTenantId() // 租户ID - ); - - // 6. 处理退款结果 - if (refundResponse.getSuccess()) { - // 退款成功,更新订单信息 - shopOrder.setRefundOrder(refundNo); - shopOrder.setRefundMoney(refundAmount); - shopOrder.setRefundTime(LocalDateTime.now()); - shopOrder.setOrderStatus(6); // 退款成功 - - // 根据退款状态决定订单状态 - // 如果微信返回退款处理中,则设置订单状态为5(退款处理中) - // 如果微信返回退款成功,则保持状态为6(退款成功) -// if (refundResponse.getPaymentStatus() != null) { -// switch (refundResponse.getPaymentStatus()) { -// case REFUNDING: -// shopOrder.setOrderStatus(5); // 退款处理中 -// logger.info("订单退款处理中,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), refundNo); -// break; -// case REFUNDED: -// shopOrder.setOrderStatus(6); // 退款成功 -// logger.info("订单退款成功,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), refundNo); -// break; -// case REFUND_FAILED: -// logger.error("订单退款失败,订单号: {}, 退款单号: {}", shopOrderNow.getOrderNo(), refundNo); -// return fail("退款失败,请联系管理员"); -// default: -// shopOrder.setOrderStatus(5); // 默认为退款处理中 -// } -// } - - logger.info("订单退款请求成功 - 订单号: {}, 退款单号: {}, 微信退款单号: {}", - shopOrderNow.getOrderNo(), refundNo, refundResponse.getTransactionId()); - } else { - // 退款失败 - logger.error("订单退款失败 - 订单号: {}, 错误: {}", shopOrderNow.getOrderNo(), refundResponse.getErrorMessage()); - return fail("退款失败: " + refundResponse.getErrorMessage()); - } - - } catch (Exception e) { - logger.error("处理订单退款异常 - 订单号: {}, 错误: {}", shopOrderNow.getOrderNo(), e.getMessage(), e); - return fail("退款处理异常: " + e.getMessage()); - } - } if (shopOrderService.updateById(shopOrder)) { return success("修改成功"); } return fail("修改失败"); } + @PreAuthorize("hasAuthority('shop:shopOrder:refund')") + @Operation(summary = "订单退款操作(申请退款/同意退款)", description = "orderStatus=4 申请退款;orderStatus=6 同意退款并发起原路退款") + @PutMapping("/refund") + public ApiResult refund(@RequestBody ShopOrder req) { + if (req == null || req.getOrderId() == null || req.getOrderStatus() == null) { + return fail("orderId 和 orderStatus 不能为空"); + } + if (!Objects.equals(req.getOrderStatus(), 4) && !Objects.equals(req.getOrderStatus(), 6)) { + return fail("orderStatus 仅支持 4(申请退款) 或 6(同意退款)"); + } + + ShopOrder current = shopOrderService.getById(req.getOrderId()); + if (current == null) { + return fail("订单不存在"); + } + + // 申请退款:只记录申请时间/原因/金额(如有) + if (Objects.equals(req.getOrderStatus(), 4)) { + ShopOrder patch = new ShopOrder(); + patch.setOrderId(req.getOrderId()); + patch.setOrderStatus(4); + patch.setRefundApplyTime(LocalDateTime.now()); + if (StrUtil.isNotBlank(req.getRefundReason())) { + patch.setRefundReason(req.getRefundReason()); + } + if (req.getRefundMoney() != null) { + patch.setRefundMoney(req.getRefundMoney()); + } + if (shopOrderService.updateById(patch)) { + return success("申请退款成功"); + } + return fail("申请退款失败"); + } + + // 同意退款:发起原路退款并更新退款信息 + try { + if (!Boolean.TRUE.equals(current.getPayStatus())) { + return fail("订单未支付,无法退款"); + } + if (StrUtil.isNotBlank(current.getRefundOrder())) { + logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", current.getOrderNo(), current.getRefundOrder()); + return fail("订单已退款,请勿重复操作"); + } + + String refundNo = "RF" + IdUtil.getSnowflakeNextId(); + + BigDecimal refundAmount = req.getRefundMoney(); + if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { + refundAmount = current.getTotalPrice(); + } + if (refundAmount.compareTo(current.getTotalPrice()) > 0) { + return fail("退款金额不能大于订单金额"); + } + + PaymentType paymentType = PaymentType.WECHAT_NATIVE; + if (current.getPayType() != null) { + // 支付方式:0余额支付,1微信支付,2支付宝支付,3银联支付,4现金支付,5POS机支付,6免费,7积分支付 + paymentType = PaymentType.getByCode(current.getPayType()); + if (paymentType == PaymentType.WECHAT) { + paymentType = PaymentType.WECHAT_NATIVE; + } + } + + logger.info("开始处理订单退款 - 订单号: {}, 退款单号: {}, 退款金额: {}, 支付方式: {}", + current.getOrderNo(), refundNo, refundAmount, paymentType); + + PaymentResponse refundResponse = paymentService.refund( + current.getOrderNo(), + refundNo, + paymentType, + current.getTotalPrice(), + refundAmount, + StrUtil.isNotBlank(req.getRefundReason()) ? req.getRefundReason() : "用户申请退款", + current.getTenantId() + ); + + if (!Boolean.TRUE.equals(refundResponse.getSuccess())) { + logger.error("订单退款失败 - 订单号: {}, 错误: {}", current.getOrderNo(), refundResponse.getErrorMessage()); + return fail("退款失败: " + refundResponse.getErrorMessage()); + } + + ShopOrder patch = new ShopOrder(); + patch.setOrderId(req.getOrderId()); + patch.setRefundOrder(refundNo); + patch.setRefundMoney(refundAmount); + patch.setRefundTime(LocalDateTime.now()); + patch.setOrderStatus(6); + if (StrUtil.isNotBlank(req.getRefundReason())) { + patch.setRefundReason(req.getRefundReason()); + } + + if (!shopOrderService.updateById(patch)) { + logger.error("退款已成功但订单更新失败 - orderId={}, orderNo={}, refundNo={}", current.getOrderId(), current.getOrderNo(), refundNo); + return fail("退款成功,但订单状态更新失败,请联系管理员"); + } + + logger.info("订单退款请求成功 - 订单号: {}, 退款单号: {}, 微信退款单号: {}", + current.getOrderNo(), refundNo, refundResponse.getTransactionId()); + return success("退款成功"); + + } catch (Exception e) { + logger.error("处理订单退款异常 - 订单号: {}, 错误: {}", current.getOrderNo(), e.getMessage(), e); + return fail("退款处理异常: " + e.getMessage()); + } + } + @Operation(summary = "删除订单") @DeleteMapping("/{id}") public ApiResult remove(@PathVariable("id") Integer id) { @@ -415,6 +419,13 @@ public class ShopOrderController extends BaseController { @Operation(summary = "批量修改订单") @PutMapping("/batch") public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + if (batchParam != null && batchParam.getData() != null) { + Integer status = batchParam.getData().getOrderStatus(); + // 退款相关操作单独走退款接口,避免绕过财务权限 + if (Objects.equals(status, 4) || Objects.equals(status, 6)) { + return fail("退款相关操作请使用退款接口: PUT /api/shop/shop-order/refund"); + } + } if (batchParam.update(shopOrderService, "order_id")) { return success("修改成功"); } diff --git a/src/main/java/com/gxwebsoft/shop/task/DealerOrderSettlement10584Task.java b/src/main/java/com/gxwebsoft/shop/task/DealerOrderSettlement10584Task.java index addd061..7ffd7ae 100644 --- a/src/main/java/com/gxwebsoft/shop/task/DealerOrderSettlement10584Task.java +++ b/src/main/java/com/gxwebsoft/shop/task/DealerOrderSettlement10584Task.java @@ -234,7 +234,7 @@ public class DealerOrderSettlement10584Task { thirdMoney, order, simpleDealerId, - buildCommissionComment("第3级佣金", commissionConfig.commissionType, commissionConfig.dealerThirdValue, goodsQty) + buildCommissionComment("分润收入", commissionConfig.commissionType, commissionConfig.dealerThirdValue, goodsQty) ); return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney);