feat(order): 分离订单退款功能到独立接口并优化水票统计
- 将订单退款逻辑从update方法中分离到独立的refund接口 - 添加退款相关操作权限控制和参数验证 - 实现申请退款和同意退款两种状态的分别处理 - 新增水票总数统计功能,包括service、mapper和controller层实现 - 修改佣金注释文本从"第3级佣金"为"分润收入" - 优化订单更新逻辑,禁止通过普通更新接口进行退款操作
This commit is contained in:
@@ -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<String, Object> data = new HashMap<>();
|
||||
data.put("userId", userId);
|
||||
data.put("totalQty", totalQty);
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('glt:gltUserTicket:list')")
|
||||
@Operation(summary = "查询全部我的水票")
|
||||
@GetMapping()
|
||||
|
||||
@@ -34,4 +34,12 @@ public interface GltUserTicketMapper extends BaseMapper<GltUserTicket> {
|
||||
*/
|
||||
List<GltUserTicket> selectListRel(@Param("param") GltUserTicketParam param);
|
||||
|
||||
/**
|
||||
* 统计用户水票总数量(sum(total_qty))
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 总数量
|
||||
*/
|
||||
Integer sumTotalQtyByUserId(@Param("userId") Integer userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -86,4 +86,12 @@
|
||||
<include refid="selectSql"></include>
|
||||
</select>
|
||||
|
||||
<!-- 我的水票总数(sum(total_qty)) -->
|
||||
<select id="sumTotalQtyByUserId" resultType="java.lang.Integer">
|
||||
SELECT IFNULL(SUM(total_qty), 0)
|
||||
FROM glt_user_ticket
|
||||
WHERE user_id = #{userId}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -39,4 +39,12 @@ public interface GltUserTicketService extends IService<GltUserTicket> {
|
||||
*/
|
||||
GltUserTicket getByIdRel(Integer id);
|
||||
|
||||
/**
|
||||
* 统计指定用户水票总数量(sum(total_qty))
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 总数量(无记录返回0)
|
||||
*/
|
||||
Integer sumTotalQtyByUserId(Integer userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -44,4 +44,10 @@ public class GltUserTicketServiceImpl extends ServiceImpl<GltUserTicketMapper, G
|
||||
return param.getOne(baseMapper.selectListRel(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer sumTotalQtyByUserId(Integer userId) {
|
||||
Integer totalQty = baseMapper.sumTotalQtyByUserId(userId);
|
||||
return totalQty == null ? 0 : totalQty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -261,18 +261,17 @@ public class ShopOrderController extends BaseController {
|
||||
@Operation(summary = "修改订单")
|
||||
@PutMapping()
|
||||
public ApiResult<?> 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<ShopOrder> 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("修改成功");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user