diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserReturnController.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserReturnController.java index 5fa657ae..17048bbb 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserReturnController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserReturnController.java @@ -1,6 +1,8 @@ package com.suisung.mall.shop.order.controller.mobile; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.api.ResultCode; @@ -15,10 +17,7 @@ import com.suisung.mall.shop.order.vo.OrderReturnVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; /** *

@@ -57,7 +56,7 @@ public class UserReturnController extends BaseControllerImpl { return shopOrderReturnService.returnItem(); } - @ApiOperation(value = "添加退款退货-部分退货,卖家也可以决定不退货退款,买家申请退款不支持。卖家可以主动退款。", notes = "添加退款退货-发货退货,卖家也可以决定不退货退款,买家申请退款不支持。卖家可以主动退款。") + @ApiOperation(value = "添加退款退货-部分退货", notes = "添加退款退货-发货退货,卖家也可以决定不退货退款,买家申请退款不支持。卖家可以主动退款。") @RequestMapping(value = "/addItem", method = RequestMethod.GET) public CommonResult addItem(OrderReturnVo orderReturnVo) { UserDto user = getCurrentUser(); @@ -77,6 +76,26 @@ public class UserReturnController extends BaseControllerImpl { return shopOrderReturnService.addItem(orderReturnInputVo, true); } + @ApiOperation(value = "批量添加退款退货(支持多个商品)", notes = "添加退款退货-发货退货,卖家也可以决定不退货退款,买家申请退款不支持。卖家可以主动退款。") + @RequestMapping(value = "/addItemBatch", method = RequestMethod.POST) + public CommonResult addItemBatch(@RequestBody JSONObject orderReturnVoJSON) { + if (orderReturnVoJSON == null) { + return CommonResult.failed("请求参数不能为空"); + } + + OrderReturnInputVo orderReturnInputVo = JSONUtil.toBean(orderReturnVoJSON, OrderReturnInputVo.class); + if (orderReturnInputVo == null) { + return CommonResult.failed("请求参数格式不正确"); + } + + UserDto user = getCurrentUser(); + if (user == null) { + return CommonResult.failed(ResultCode.NEED_LOGIN); + } + + return shopOrderReturnService.tryAddItemBatch(orderReturnInputVo); + } + @ApiOperation(value = "添加退款退货-整单退货", notes = "添加退款退货-整单退货") @RequestMapping(value = "/addWholeItems", method = RequestMethod.POST) public CommonResult addWholeItems(@RequestParam(name = "order_id") String order_id) { diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java index e00dcb59..e6a428c3 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java @@ -50,6 +50,14 @@ public interface ShopOrderReturnService extends IBaseService { */ CommonResult addItem(OrderReturnInputVo orderReturnInputVo, Boolean needSendMsg); + /** + * 尝试批量添加退款退货申请(自动使用最大可退数量和金额),等待商家审核处理(同意或不同意) + * + * @param orderReturnInputVo 退款申请输入参数,支持订单多个商品退款 + * @return CommonResult 退款申请结果 + */ + CommonResult tryAddItemBatch(OrderReturnInputVo orderReturnInputVo); + Map getReturnDetail(String return_id); boolean processReviewList(ShopOrderReturn shopOrderReturn, Integer receiving_address); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index f6fbb494..87046f57 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java @@ -4167,11 +4167,12 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl order_return_items = orderReturnItemService.find(returnItemQueryWrapper); - if (CollUtil.isNotEmpty(order_return_items)) { throw new ApiException(I18nUtil._("此订单商品正在退款审核!")); } @@ -568,17 +568,16 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl available_return_num) { throw new ApiException(String.format("商品 %s 的退款数量超过可退数量,最多可退 %d 件", shopOrderItem.getItem_name(), available_return_num)); @@ -650,6 +649,208 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl data = new HashMap<>(); + + // 获取用户ID + Integer user_id = orderReturnInputVo.getUser_id(); + if (CheckUtil.isEmpty(user_id)) { + UserDto user = getCurrentUser(); + if (user == null) { + throw new ApiUserException(I18nUtil._("用户信息异常!")); + } + user_id = user.getId(); + } + + // 获取买家店铺ID(如果有) + Integer buyer_store_id = shopStoreBaseService.getStoreId(user_id); + + // 获取订单信息 + String order_id = orderReturnInputVo.getOrder_id(); + List order_item_ids = orderReturnInputVo.getReturn_items().stream() + .map(OrderReturnItemInputVo::getOrder_item_id) + .distinct() + .collect(Collectors.toList()); + + ShopOrderInfo shopOrderInfo = shopOrderInfoService.get(order_id); + if (shopOrderInfo == null) { + throw new ApiException(I18nUtil._("此订单信息数据有误!")); + } + + ShopOrderBase orderBase = shopOrderBaseService.get(order_id); + if (orderBase == null) { + throw new ApiException(I18nUtil._("此订单详细信息数据有误!")); + } + + // 判断此订单商品是否有正在审核的退款单 + QueryWrapper returnQueryWrapper = new QueryWrapper<>(); + returnQueryWrapper.eq("order_id", order_id) + .eq("return_state_id", StateCode.RETURN_PROCESS_CHECK); + List order_return_rows = find(returnQueryWrapper); + + if (CollUtil.isNotEmpty(order_return_rows)) { + List return_ids = order_return_rows.stream() + .map(ShopOrderReturn::getReturn_id) + .collect(Collectors.toList()); + + QueryWrapper returnItemQueryWrapper = new QueryWrapper<>(); + returnItemQueryWrapper.in("order_item_id", order_item_ids) + .in("return_id", return_ids); + List order_return_items = orderReturnItemService.find(returnItemQueryWrapper); + if (CollUtil.isNotEmpty(order_return_items)) { + throw new ApiException(I18nUtil._("此订单商品正在退款审核!")); + } + } + + // 检查数据权限 + if (!CheckUtil.checkDataRights(user_id, orderBase, ShopOrderBase::getBuyer_user_id)) { + throw new ApiException(ResultCode.FORBIDDEN); + } + + // 获取退款申请参数 + String return_id = orderReturnInputVo.getReturn_id(); + Integer return_reason_id = orderReturnInputVo.getReturn_reason_id() == null ? 0 : orderReturnInputVo.getReturn_reason_id(); + String return_buyer_message = orderReturnInputVo.getReturn_buyer_message(); + Integer store_id = Convert.toInt(orderBase.getStore_id()); + String return_tel = orderReturnInputVo.getReturn_tel(); + + // 手机号验证 + if (StrUtil.isNotBlank(return_tel) && !PhoneUtil.isMobile(return_tel)) { + throw new ApiException(I18nUtil._("手机号输入有误!")); + } + + Date curTime = new Date(); + + // 构建退款单主表信息 + ShopOrderReturn orderReturn = new ShopOrderReturn(); + orderReturn.setReturn_id(return_id); + orderReturn.setOrder_id(order_id); + orderReturn.setBuyer_user_id(user_id); + orderReturn.setBuyer_store_id(buyer_store_id); + orderReturn.setReturn_reason_id(return_reason_id); + orderReturn.setReturn_buyer_message(return_buyer_message); + orderReturn.setStore_id(store_id); + orderReturn.setReturn_refund_amount(BigDecimal.ZERO); + orderReturn.setReturn_commision_fee(BigDecimal.ZERO); + orderReturn.setReturn_state_id(StateCode.RETURN_PROCESS_CHECK); + orderReturn.setReturn_tel(return_tel); + orderReturn.setReturn_year(DateUtil.year(curTime)); // 退单年份-索引查询 + orderReturn.setReturn_month(DateUtil.month(curTime) + 1); // 退单月份-索引查询 + orderReturn.setReturn_day(DateUtil.dayOfMonth(curTime)); // 退单日-索引查询 + orderReturn.setReturn_reason_id(0); // 注意:这里覆盖了前面设置的return_reason_id + orderReturn.setReturn_store_user_id(0); + orderReturn.setReturn_mobile(0L); + orderReturn.setReturn_telephone(""); + orderReturn.setSubsite_id(shopOrderInfo.getSubsite_id()); + + // 构建退款单明细信息 + List shopOrderReturnItems = new ArrayList<>(); + for (OrderReturnItemInputVo returnItemInputVo : orderReturnInputVo.getReturn_items()) { + // 获取订单商品信息 + ShopOrderItem shopOrderItem = shopOrderItemService.get(returnItemInputVo.getOrder_item_id()); + if (shopOrderItem == null) { + throw new ApiException("订单商品不存在,请检查商品ID是否正确: " + returnItemInputVo.getOrder_item_id()); + } + + // 验证是否为当前订单的商品 + if (!order_id.equals(shopOrderItem.getOrder_id())) { + throw new ApiException("商品ID " + returnItemInputVo.getOrder_item_id() + " 不属于订单 " + order_id); + } + + // 获取商品索引信息 + ShopProductIndex shopProductIndex = shopProductIndexService.get(shopOrderItem.getProduct_id()); + if (shopProductIndex == null) { + throw new ApiException("商品信息不存在,请检查商品ID是否正确: " + shopOrderItem.getProduct_id()); + } + + // 检查是否允许退货 + boolean is_denyreturn = ifDenyReturn(shopOrderInfo, shopOrderItem, shopProductIndex); + if (is_denyreturn) { + // 获取更详细的拒绝退货原因 + String denyReason = getDenyReturnReason(shopOrderInfo, shopOrderItem, shopProductIndex); + if (StrUtil.isBlank(denyReason)) { + denyReason = "此商品不允许退货!"; + } + throw new ApiException(denyReason); + } + + // 构建退款单明细 + ShopOrderReturnItem orderReturnItem = new ShopOrderReturnItem(); + + // 检查退款数量是否超过可退数量 + Integer order_item_quantity = shopOrderItem.getOrder_item_quantity(); // 订单中该商品的原始购买数量 + Integer order_item_return_agree_num = ObjectUtil.defaultIfNull(shopOrderItem.getOrder_item_return_agree_num(), 0); // 订单中该商品已经同意退货的数量 + Integer available_return_num = order_item_quantity - order_item_return_agree_num; // 当前还可以申请退款的商品数量 + if (available_return_num <= 0) { + throw new ApiException(String.format("商品 %s 已无可退数量", shopOrderItem.getItem_name())); + } + + // 检查退款金额是否合理 + BigDecimal order_item_payment_amount = shopOrderItem.getOrder_item_payment_amount(); + BigDecimal order_item_return_agree_amount = ObjectUtil.defaultIfNull(shopOrderItem.getOrder_item_return_agree_amount(), BigDecimal.ZERO); + BigDecimal available_return_amount = order_item_payment_amount.subtract(order_item_return_agree_amount); + if (available_return_amount.compareTo(BigDecimal.ZERO) <= 0) { + throw new ApiException(String.format("商品 %s 已无可退金额", shopOrderItem.getItem_name())); + } + + String return_item_image = ObjectUtil.defaultIfBlank( + orderReturnInputVo.getRefund_pic(), + orderReturnInputVo.getUpload_img()); + + orderReturnItem.setOrder_item_id(returnItemInputVo.getOrder_item_id()); + orderReturnItem.setOrder_id(order_id); + orderReturnItem.setReturn_item_num(available_return_num); + orderReturnItem.setReturn_item_subtotal(available_return_amount); + orderReturnItem.setReturn_reason_id(return_reason_id); + orderReturnItem.setReturn_item_note(return_buyer_message); + orderReturnItem.setReturn_item_image(return_item_image); + orderReturnItem.setReturn_state_id(StateCode.RETURN_PROCESS_CHECK); + + shopOrderReturnItems.add(orderReturnItem); + } + + // 保存退款单 + if (!addReturnByItem(orderReturn, shopOrderReturnItems)) { + throw new ApiException(ResultCode.FAILED); + } + + data.put("return", orderReturn); + data.put("items", shopOrderReturnItems); + + // 打票机打印退款订单到店(异步执行) + shopStorePrinterService.printShopStoreReturnOrder(return_id); + + // 发送消息通知, 退款提醒商家 + String message_id = "refund-reminder"; + Map args = new HashMap<>(); + args.put("order_id", orderReturn.getReturn_id()); + messageService.sendNoticeMsg(0, store_id, message_id, args); + + // 发送unipush消息 + String title = "您有一个退款的订单,请及时前往后台审核!"; + String content = String.format("订单号:%s 退单号:[%s],时间:%s,请及时前往后台审核!", + order_id, orderReturn.getReturn_id(), + DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss")); + JSONObject payload = JSONUtil.createObj(); + payload.set("category", CommonConstant.PUSH_MSG_CATE_MCH_RETURN_ORDER_LIST); + payload.set("orderId", order_id); + payload.set("returnId", orderReturn.getReturn_id()); + pushMessageService.noticeMerchantEmployeeOrderAction(store_id, order_id, title, content, payload); + + data.put("order_item_ids", order_item_ids); + return CommonResult.success(data); + } + /** * 取消退货 @@ -3099,4 +3300,116 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl availableReturnNum) { + if (!autoCorrect) { + throw new ApiException(String.format("商品 %s 的退款数量超过可退数量,最多可退 %d 件", + shopOrderItem.getItem_name(), availableReturnNum)); + } + + // 按比例调整退款金额 + BigDecimal ratio = BigDecimal.valueOf(availableReturnNum) + .divide(BigDecimal.valueOf(requestReturnNum), 6, RoundingMode.HALF_UP); + BigDecimal correctedAmount = requestRefundAmount.multiply(ratio); + return new BigDecimal[]{BigDecimal.valueOf(availableReturnNum), correctedAmount}; + } + + // 校验并处理请求退款金额是否超过可退金额 + if (requestRefundAmount.compareTo(availableReturnAmount) > 0) { + if (!autoCorrect) { + throw new ApiException(String.format("商品 %s 的退款金额超过可退金额,最多可退 %.2f 元", + shopOrderItem.getItem_name(), availableReturnAmount)); + } + return new BigDecimal[]{BigDecimal.valueOf(requestReturnNum), availableReturnAmount}; + } + + return new BigDecimal[]{BigDecimal.valueOf(requestReturnNum), requestRefundAmount}; + } + + /** + * 校验退款商品数量是否有效(专用于仅校验数量的场景) + * + * @param shopOrderItem 订单商品项 + * @param requestReturnNum 请求退款数量 + * @param autoCorrect 是否自动纠正数量(false:超出时抛出异常, true:超出时自动调整为最大可退数量) + * @return 校验后的有效退款数量 + * @throws ApiException 当autoCorrect为false且请求数量超出可退数量时抛出异常 + */ + private Integer validateReturnQuantity(ShopOrderItem shopOrderItem, Integer requestReturnNum, boolean autoCorrect) { + // 校验请求参数有效性 + if (requestReturnNum <= 0) { + throw new ApiException("退款数量必须大于0,请检查商品 " + shopOrderItem.getItem_name() + " 的退款数量"); + } + + // 计算可退数量 + Integer orderItemQuantity = shopOrderItem.getOrder_item_quantity(); + Integer orderItemReturnAgreeNum = ObjectUtil.defaultIfNull(shopOrderItem.getOrder_item_return_agree_num(), 0); + Integer availableReturnNum = orderItemQuantity - orderItemReturnAgreeNum; + + // 检查是否还有可退数量 + if (availableReturnNum <= 0) { + if (autoCorrect) { + return 0; + } + throw new ApiException(String.format("商品 %s 已无可退数量,无法申请退款", shopOrderItem.getItem_name())); + } + + // 校验并处理请求退款数量是否超过可退数量 + if (requestReturnNum > availableReturnNum) { + if (!autoCorrect) { + throw new ApiException(String.format("商品 %s 的退款数量超过可退数量,最多可退 %d 件", + shopOrderItem.getItem_name(), availableReturnNum)); + } + return availableReturnNum; + } + + return requestReturnNum; + } + + } \ No newline at end of file