Parcourir la source

trade:【交易售后】完善发起、同意、不同意、收货、拒绝收货、退款的逻辑

YunaiV il y a 2 ans
Parent
commit
cd2bc112cc
16 fichiers modifiés avec 277 ajouts et 208 suppressions
  1. 3 5
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
  2. 5 5
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleStatusEnum.java
  3. 2 2
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderAfterSaleStatusEnum.java
  4. 2 2
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java
  5. 25 17
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java
  6. 0 25
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleConfirmReqVO.java
  7. 5 8
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleDisagreeReqVO.java
  8. 21 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleRefuseReqVO.java
  9. 6 4
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/aftersale/TradeAfterSaleDO.java
  10. 4 4
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
  11. 6 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderItemMapper.java
  12. 29 13
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleService.java
  13. 116 113
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java
  14. 3 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java
  15. 48 7
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
  16. 2 2
      yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java

+ 3 - 5
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java

@@ -32,11 +32,9 @@ public interface ErrorCodeConstants {
     ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED = new ErrorCode(1011000105, "订单项已申请售后,无法重复申请");
     ErrorCode AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY = new ErrorCode(1011000106, "审批失败,售后状态不处于审批中");
     ErrorCode AFTER_SALE_UPDATE_STATUS_FAIL = new ErrorCode(1011000107, "操作售后单失败,请刷新后重试");
-    ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_PASS = new ErrorCode(1011000108, "退货失败,售后单状态不处于【待买家退货】");
-    ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_RETURN = new ErrorCode(1011000109, "确认收货失败,售后单状态不处于【待确认收货】");
-    ErrorCode AFTER_SALE_REFUND_FAIL_PAY_REFUND_NOT_FOUND = new ErrorCode(1011000110, "退款失败,支付退款单不存在");
-    ErrorCode AFTER_SALE_REFUND_FAIL_PAY_REFUND_STATUS_NOT_SUCCESS = new ErrorCode(1011000111, "退款失败,支付退款单状态不是【成功】");
-    ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1011000112, "退款失败,售后单状态不是【待退款】");
+    ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1011000108, "退货失败,售后单状态不处于【待买家退货】");
+    ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1011000109, "确认收货失败,售后单状态不处于【待确认收货】");
+    ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1011000110, "退款失败,售后单状态不是【待退款】");
 
     // ==========  Cart 模块 1-011-001-000 ==========
     ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在");

+ 5 - 5
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleStatusEnum.java

@@ -15,14 +15,14 @@ import lombok.Getter;
 public enum TradeAfterSaleStatusEnum {
 
     APPLY(10,"申请中"),
-    SELLER_PASS(20, "已通过"), // 卖家通过售后
-    BUYER_RETURN(30,"待卖家收货"), // 买家退货,等待卖家收货
-    WAIT_REFUND(40, "等待平台退款"), // 卖家收货,等待平台退款
+    SELLER_AGREE(20, "卖家通过"), // 卖家通过售后
+    BUYER_DELIVERY(30,"待卖家收货"), // 买家已退货,等待卖家收货
+    WAIT_REFUND(40, "等待平台退款"), // 卖家收货,等待平台退款
     COMPLETE(50, "完成"), // 完成退款
 
     BUYER_CANCEL(61, "买家取消售后"),
-    SELLER_REFUSE(62,"已拒绝"), // 卖家拒绝售后
-    SELLER_TERMINATION(63,"卖家终止售后"), // 卖家拒绝收货,终止售后
+    SELLER_DISAGREE(62,"卖家拒绝"), // 卖家拒绝售后
+    SELLER_REFUSE(63,"卖家拒绝收货"), // 卖家拒绝收货,终止售后
     ;
 
     /**

+ 2 - 2
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderRefundStatusEnum.java → yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderAfterSaleStatusEnum.java

@@ -4,13 +4,13 @@ import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 
 /**
- * 交易订单 - 退款状态
+ * 交易订单 - 售后状态
  *
  * @author Sin
  */
 @RequiredArgsConstructor
 @Getter
-public enum TradeOrderRefundStatusEnum {
+public enum TradeOrderAfterSaleStatusEnum {
 
     NONE(0, "未退款"),
     PART(1, "部分退款"),

+ 2 - 2
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java

@@ -13,9 +13,9 @@ import lombok.RequiredArgsConstructor;
 public enum TradeOrderCancelTypeEnum {
 
     PAY_TIMEOUT(10, "超时未支付"),
-    REFUND_CLOSE(20, "退款关闭"),
+    AFTER_SALE_CLOSE(20, "退款关闭"),
     MEMBER_CANCEL(30, "买家取消"),
-    PAY_ON_DELIVERY(40, "已通过货到付款交易"),;
+    PAY_ON_DELIVERY(40, "已通过货到付款交易"),; // TODO 芋艿:这个类型,是不是可以去掉
 
     /**
      * 关闭类型

+ 25 - 17
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java

@@ -1,8 +1,7 @@
 package cn.iocoder.yudao.module.trade.controller.admin.aftersale;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleAuditReqVO;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleConfirmReqVO;
+import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
 import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
@@ -13,7 +12,6 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
-import javax.annotation.security.PermitAll;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
@@ -29,28 +27,38 @@ public class TradeAfterSaleController {
     @Resource
     private TradeAfterSaleService afterSaleService;
 
-    @PutMapping("/audit")
-    @ApiOperation("审批售后")
-    @PreAuthorize("@ss.hasPermission('trade:after-sale:audit')")
-    public CommonResult<Boolean> auditAfterSale(@RequestBody TradeAfterSaleAuditReqVO auditReqVO) {
-        afterSaleService.auditAfterSale(getLoginUserId(), getClientIP(), auditReqVO);
+    @PutMapping("/agree")
+    @ApiOperation("同意售后")
+    @ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
+    @PreAuthorize("@ss.hasPermission('trade:after-sale:agree')")
+    public CommonResult<Boolean> agreeAfterSale(@RequestParam("id") Long id) {
+        afterSaleService.agreeAfterSale(getLoginUserId(), id);
         return success(true);
     }
 
-    @PutMapping("/confirm")
+    @PutMapping("/disagree")
+    @ApiOperation("拒绝售后")
+    @PreAuthorize("@ss.hasPermission('trade:after-sale:disagree')")
+    public CommonResult<Boolean> disagreeAfterSale(@RequestBody TradeAfterSaleDisagreeReqVO confirmReqVO) {
+        afterSaleService.disagreeAfterSale(getLoginUserId(), confirmReqVO);
+        return success(true);
+    }
+
+    @PutMapping("/receive")
     @ApiOperation("确认收货")
-    @PreAuthorize("@ss.hasPermission('trade:after-sale:audit')")
-    public CommonResult<Boolean> confirmAfterSale(@RequestBody TradeAfterSaleConfirmReqVO confirmReqVO) {
-        afterSaleService.confirmAfterSale(getLoginUserId(), getClientIP(), confirmReqVO);
+    @ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
+    @PreAuthorize("@ss.hasPermission('trade:after-sale:receive')")
+    public CommonResult<Boolean> receiveAfterSale(@RequestParam("id") Long id) {
+        afterSaleService.receiveAfterSale(getLoginUserId(), id);
         return success(true);
     }
 
     @PostMapping("/refund")
-    @ApiOperation(value = "确认退款", notes = "提供给【pay】支付服务,退款成功后进行回调")
-    @ApiImplicitParam(name = "payRefundId", value = "支付退款编号", required = true, example = "18888")
-    @PermitAll
-    public CommonResult<Boolean> refundAfterSale(@RequestParam("payRefundId") Long payRefundId) {
-        afterSaleService.refundAfterSale(payRefundId);
+    @ApiOperation(value = "确认退款")
+    @ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
+    @PreAuthorize("@ss.hasPermission('trade:after-sale:refund')")
+    public CommonResult<Boolean> refundAfterSale(@RequestParam("id") Long id) {
+        afterSaleService.refundAfterSale(getLoginUserId(), getClientIP(), id);
         return success(true);
     }
 

+ 0 - 25
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleConfirmReqVO.java

@@ -1,25 +0,0 @@
-package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-
-@ApiModel("管理后台 - 交易售后确认收货 Request VO")
-@Data
-public class TradeAfterSaleConfirmReqVO {
-
-    @ApiModelProperty(value = "售后编号", required = true, example = "1024")
-    @NotNull(message = "售后编号不能为空")
-    private Long id;
-
-    @ApiModelProperty(value = "收货结果", required = true, example = "true",
-            notes = "true - 确认收货;false - 拒绝收货")
-    @NotNull(message = "审批结果不能为空")
-    private Boolean pass;
-
-    @ApiModelProperty(value = "收货备注", example = "你猜")
-    private String receiptMemo;
-
-}

+ 5 - 8
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleAuditReqVO.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleDisagreeReqVO.java

@@ -4,22 +4,19 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 
-@ApiModel("管理后台 - 交易售后审批 Request VO")
+@ApiModel("管理后台 - 交易售后拒绝 Request VO")
 @Data
-public class TradeAfterSaleAuditReqVO {
+public class TradeAfterSaleDisagreeReqVO {
 
     @ApiModelProperty(value = "售后编号", required = true, example = "1024")
     @NotNull(message = "售后编号不能为空")
     private Long id;
 
-    @ApiModelProperty(value = "审批结果", required = true, example = "true",
-            notes = "true - 通过;false - 不通过")
-    @NotNull(message = "审批结果不能为空")
-    private Boolean pass;
-
-    @ApiModelProperty(value = "审批备注", example = "你猜")
+    @ApiModelProperty(value = "审批备注", required = true, example = "你猜")
+    @NotEmpty(message = "审批备注不能为空")
     private String auditReason;
 
 }

+ 21 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleRefuseReqVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@ApiModel("管理后台 - 交易售后拒绝收货 Request VO")
+@Data
+public class TradeAfterSaleRefuseReqVO {
+
+    @ApiModelProperty(value = "售后编号", required = true, example = "1024")
+    @NotNull(message = "售后编号不能为空")
+    private Long id;
+
+    @ApiModelProperty(value = "收货备注", required = true, example = "你猜")
+    @NotNull(message = "收货备注不能为空")
+    private String refuseMemo;
+
+}

+ 6 - 4
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/aftersale/TradeAfterSaleDO.java

@@ -86,6 +86,8 @@ public class TradeAfterSaleDO extends BaseDO {
     private Long auditUserId;
     /**
      * 审批备注
+     *
+     * 注意,只有审批不通过才会填写
      */
     private String auditReason;
 
@@ -132,8 +134,6 @@ public class TradeAfterSaleDO extends BaseDO {
     private Long payRefundId;
     /**
      * 退款时间
-     *
-     * 退款成功后,才记录该时间
      */
     private LocalDateTime refundTime;
 
@@ -155,10 +155,12 @@ public class TradeAfterSaleDO extends BaseDO {
     /**
      * 收货时间
      */
-    private LocalDateTime receiptTime;
+    private LocalDateTime receiveTime;
     /**
      * 收货备注
+     *
+     * 注意,只有拒绝收货才会填写
      */
-    private String receiptMemo;
+    private String receiveReason;
 
 }

+ 4 - 4
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java

@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
+import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -219,11 +219,11 @@ public class TradeOrderDO extends BaseDO {
 
     // ========== 退款基本信息 ==========
     /**
-     * 退款状态
+     * 收货状态
      *
-     * 枚举 {@link TradeOrderRefundStatusEnum}
+     * 枚举 {@link TradeOrderAfterSaleStatusEnum}
      */
-    private Integer refundStatus;
+    private Integer afterSaleStatus;
     /**
      * 退款金额,单位:分
      *

+ 6 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderItemMapper.java

@@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
 
@@ -13,4 +15,8 @@ public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
                 new LambdaUpdateWrapper<>(new TradeOrderItemDO().setId(id).setAfterSaleStatus(oldAfterSaleStatus)));
     }
 
+    default List<TradeOrderItemDO> selectListByOrderId(Long orderId) {
+        return selectList(TradeOrderItemDO::getOrderId, orderId);
+    }
+
 }

+ 29 - 13
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleService.java

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.module.trade.service.aftersale;
 
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleAuditReqVO;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleConfirmReqVO;
+import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
+import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO;
 
@@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSa
 public interface TradeAfterSaleService {
 
     /**
-     * 创建交易售后
+     * 【会员】创建交易售后
      * <p>
      * 一般是用户发起售后请求
      *
@@ -24,16 +24,23 @@ public interface TradeAfterSaleService {
     Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO);
 
     /**
-     * 审批交易售后
+     * 【管理员】同意交易售后
+     *
+     * @param userId 管理员用户编号
+     * @param id 交易售后编号
+     */
+    void agreeAfterSale(Long userId, Long id);
+
+    /**
+     * 【管理员】拒绝交易售后
      *
      * @param userId 管理员用户编号
-     * @param userIp 操作 IP
      * @param auditReqVO 审批 Request 信息
      */
-    void auditAfterSale(Long userId, String userIp, TradeAfterSaleAuditReqVO auditReqVO);
+    void disagreeAfterSale(Long userId, TradeAfterSaleDisagreeReqVO auditReqVO);
 
     /**
-     * 退回货物
+     * 【会员】退回货物
      *
      * @param userId 会员用户编号
      * @param deliveryReqVO 退货 Request 信息
@@ -41,19 +48,28 @@ public interface TradeAfterSaleService {
     void deliveryAfterSale(Long userId, AppTradeAfterSaleDeliveryReqVO deliveryReqVO);
 
     /**
-     * 确认收货
+     * 【管理员】确认收货
+     *
+     * @param userId 管理员编号
+     * @param id 交易售后编号
+     */
+    void receiveAfterSale(Long userId, Long id);
+
+    /**
+     * 【管理员】拒绝收货
      *
      * @param userId 管理员用户编号
-     * @param userIp 操作 IP
      * @param confirmReqVO 收货 Request 信息
      */
-    void confirmAfterSale(Long userId, String userIp, TradeAfterSaleConfirmReqVO confirmReqVO);
+    void refuseAfterSale(Long userId, TradeAfterSaleRefuseReqVO confirmReqVO);
 
     /**
-     * 确认退款,由【pay】支付服务回调
+     * 【管理员】确认退款
      *
-     * @param payRefundId 支付退款编号
+     * @param userId 管理员用户编号
+     * @param userIp 管理员用户 IP
+     * @param id 售后编号
      */
-    void refundAfterSale(Long payRefundId);
+    void refundAfterSale(Long userId, String userIp, Long id);
 
 }

+ 116 - 113
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java

@@ -4,10 +4,8 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.RandomUtil;
 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
-import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
-import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleAuditReqVO;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleConfirmReqVO;
+import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
+import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO;
 import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
@@ -28,7 +26,6 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-
 import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -66,6 +63,13 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
         return afterSale.getId();
     }
 
+    /**
+     * 校验交易订单项是否可以申请售后
+     *
+     * @param userId 用户编号
+     * @param createReqVO 售后创建信息
+     * @return 交易订单项
+     */
     private TradeOrderItemDO validateOrderItemApplicable(Long userId, AppTradeAfterSaleCreateReqVO createReqVO) {
         // 校验订单项存在
         TradeOrderItemDO orderItem = tradeOrderService.getOrderItem(userId, createReqVO.getOrderItemId());
@@ -116,7 +120,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
 
         // 更新交易订单项的售后状态
         tradeOrderService.updateOrderItemAfterSaleStatus(tradeOrderItem.getId(),
-                TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus());
+                TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
+                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), null);
 
         // TODO 记录售后日志
 
@@ -125,76 +130,61 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
     }
 
     @Override
-    @Transactional
-    public void auditAfterSale(Long userId, String userIp,
-                               TradeAfterSaleAuditReqVO auditReqVO) {
+    @Transactional(rollbackFor = Exception.class)
+    public void agreeAfterSale(Long userId, Long id) {
         // 校验售后单存在,并状态未审批
-        TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(auditReqVO.getId());
-        if (afterSale == null) {
-            throw exception(AFTER_SALE_NOT_FOUND);
-        }
-        if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus())) {
-            throw exception(AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY);
-        }
-
-        // 进行审批
-        if (auditReqVO.getPass()) {
-            auditAfterSalePass(userId, userIp, auditReqVO, afterSale);
-        } else {
-            auditAfterSaleReject(userId, auditReqVO, afterSale);
-        }
-    }
+        TradeAfterSaleDO afterSale = validateAfterSaleAuditable(id);
 
-    private void auditAfterSalePass(Long userId, String userIp,
-                                    TradeAfterSaleAuditReqVO auditReqVO, TradeAfterSaleDO afterSale) {
         // 更新售后单的状态
         // 情况一:退款:标记为 WAIT_REFUND 状态。后续等退款发起成功后,在标记为 COMPLETE 状态
         // 情况二:退货退款:需要等用户退货后,才能发起退款
         Integer newStatus = afterSale.getType().equals(TradeAfterSaleTypeEnum.REFUND.getType()) ?
-                TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus() : TradeAfterSaleStatusEnum.SELLER_PASS.getStatus();
+                TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus() : TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus();
         updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
-                .setStatus(newStatus).setAuditUserId(userId)
-                .setAuditReason(auditReqVO.getAuditReason()).setAuditTime(LocalDateTime.now()));
-
-        // 如果直接退款,则发起售后退款
-        if (afterSale.getType().equals(TradeAfterSaleTypeEnum.REFUND.getType())) {
-            createPayRefund(userIp, afterSale);
-        }
+                .setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()));
 
         // TODO 记录售后日志
 
         // TODO 发送售后消息
     }
 
-    private void auditAfterSaleReject(Long userId,
-                                      TradeAfterSaleAuditReqVO auditReqVO, TradeAfterSaleDO afterSale) {
-        // 更新售后单的状态
-        Integer newStatus = TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus();
-                updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
-                .setStatus(newStatus).setAuditUserId(userId)
-                .setAuditReason(auditReqVO.getAuditReason()).setAuditTime(LocalDateTime.now()));
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void disagreeAfterSale(Long userId, TradeAfterSaleDisagreeReqVO auditReqVO) {
+        // 校验售后单存在,并状态未审批
+        TradeAfterSaleDO afterSale = validateAfterSaleAuditable(auditReqVO.getId());
 
-        // 更新交易订单项的售后状态为【未申请】
-        tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
-                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
+        // 更新售后单的状态
+        Integer newStatus = TradeAfterSaleStatusEnum.SELLER_DISAGREE.getStatus();
+        updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
+                .setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now())
+                .setAuditReason(auditReqVO.getAuditReason()));
 
         // TODO 记录售后日志
 
         // TODO 发送售后消息
-    }
 
-    private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) {
-        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+        // 更新交易订单项的售后状态为【未申请】
+        tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
+                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
+                TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
+    }
 
-            @Override
-            public void afterCommit() {
-                // 创建退款单
-                PayRefundCreateReqDTO createReqDTO = TradeAfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties);
-                Long payRefundId = payRefundApi.createPayRefund(createReqDTO);
-                // 更新售后单的退款单号
-                tradeAfterSaleMapper.updateById(new TradeAfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
-            }
-        });
+    /**
+     * 校验售后单是否可审批(同意售后、拒绝售后)
+     *
+     * @param id 售后编号
+     * @return 售后单
+     */
+    private TradeAfterSaleDO validateAfterSaleAuditable(Long id) {
+        TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
+        if (afterSale == null) {
+            throw exception(AFTER_SALE_NOT_FOUND);
+        }
+        if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus())) {
+            throw exception(AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY);
+        }
+        return afterSale;
     }
 
     private void updateAfterSaleStatus(Long id, Integer status, TradeAfterSaleDO updateObj) {
@@ -205,19 +195,20 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void deliveryAfterSale(Long userId, AppTradeAfterSaleDeliveryReqVO deliveryReqVO) {
         // 校验售后单存在,并状态未退货
         TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(deliveryReqVO.getId());
         if (afterSale == null) {
             throw exception(AFTER_SALE_NOT_FOUND);
         }
-        if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_PASS.getStatus())) {
-            throw exception(AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_PASS);
+        if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus())) {
+            throw exception(AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE);
         }
 
         // 更新售后单的物流信息
-        updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.SELLER_PASS.getStatus(), new TradeAfterSaleDO()
-                .setStatus(TradeAfterSaleStatusEnum.BUYER_RETURN.getStatus())
+        updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus(), new TradeAfterSaleDO()
+                .setStatus(TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())
                 .setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo())
                 .setDeliveryTime(deliveryReqVO.getDeliveryTime()));
 
@@ -227,64 +218,69 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
     }
 
     @Override
-    public void confirmAfterSale(Long userId, String userIp,
-                                 TradeAfterSaleConfirmReqVO confirmReqVO) {
-        // 校验售后单存在,并状态未审批
+    @Transactional(rollbackFor = Exception.class)
+    public void receiveAfterSale(Long userId, Long id) {
+        // 校验售后单存在,并状态为已退货
+        TradeAfterSaleDO afterSale = validateAfterSaleReceivable(id);
+
+        // 更新售后单的状态
+        updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), new TradeAfterSaleDO()
+                .setStatus(TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus()).setReceiveTime(LocalDateTime.now()));
+
+        // TODO 记录售后日志
+
+        // TODO 发送售后消息
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void refuseAfterSale(Long userId, TradeAfterSaleRefuseReqVO confirmReqVO) {
+        // 校验售后单存在,并状态为已退货
         TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(confirmReqVO.getId());
         if (afterSale == null) {
             throw exception(AFTER_SALE_NOT_FOUND);
         }
-        if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_RETURN.getStatus())) {
-            throw exception(AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_RETURN);
-        }
-
-        // 进行审批
-        if (confirmReqVO.getPass()) {
-            confirmAfterSalePass(userId, userIp, confirmReqVO, afterSale);
-        } else {
-            confirmAfterSaleReject(userId, confirmReqVO, afterSale);
+        if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) {
+            throw exception(AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY);
         }
-    }
 
-    private void confirmAfterSalePass(Long userId, String userIp,
-                                      TradeAfterSaleConfirmReqVO confirmReqVO, TradeAfterSaleDO afterSale) {
         // 更新售后单的状态
-        Integer newStatus = TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus();
-        updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.BUYER_RETURN.getStatus(), new TradeAfterSaleDO()
-                .setStatus(newStatus).setReceiptTime(LocalDateTime.now()).setReceiptMemo(confirmReqVO.getReceiptMemo()));
-
-        // 如果直接退款,则发起售后退款
-        if (afterSale.getType().equals(TradeAfterSaleTypeEnum.REFUND.getType())) {
-            createPayRefund(userIp, afterSale);
-        }
+        updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), new TradeAfterSaleDO()
+                .setStatus(TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus()).setReceiveTime(LocalDateTime.now())
+                .setReceiveReason(confirmReqVO.getRefuseMemo()));
 
         // TODO 记录售后日志
 
         // TODO 发送售后消息
-    }
-
-    private void confirmAfterSaleReject(Long userId, TradeAfterSaleConfirmReqVO confirmReqVO, TradeAfterSaleDO afterSale) {
-        // 更新售后单的状态
-        Integer newStatus = TradeAfterSaleStatusEnum.SELLER_TERMINATION.getStatus();
-        updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.BUYER_RETURN.getStatus(), new TradeAfterSaleDO()
-                .setStatus(newStatus).setReceiptTime(LocalDateTime.now()).setReceiptMemo(confirmReqVO.getReceiptMemo()));
 
         // 更新交易订单项的售后状态为【未申请】
         tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
-                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
-
-        // TODO 记录售后日志
+                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
+                TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
+    }
 
-        // TODO 发送售后消息
+    /**
+     * 校验售后单是否可收货,即处于买家已发货
+     *
+     * @param id 售后编号
+     * @return 售后单
+     */
+    private TradeAfterSaleDO validateAfterSaleReceivable(Long id) {
+        TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
+        if (afterSale == null) {
+            throw exception(AFTER_SALE_NOT_FOUND);
+        }
+        if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) {
+            throw exception(AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY);
+        }
+        return afterSale;
     }
 
     @Override
-    public void refundAfterSale(Long payRefundId) {
-        // 校验退款单
-        PayRefundRespDTO payRefund = validatePayRefundSuccess(payRefundId);
-
+    @Transactional(rollbackFor = Exception.class)
+    public void refundAfterSale(Long userId, String userIp, Long id) {
         // 校验售后单的状态,并状态待退款
-        TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectByPayRefundId(payRefundId);
+        TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectByPayRefundId(id);
         if (afterSale == null) {
             throw exception(AFTER_SALE_NOT_FOUND);
         }
@@ -292,28 +288,35 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
             throw exception(AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND);
         }
 
+        // 发起退款单。注意,需要在事务提交后,再进行发起,避免重复发起
+        createPayRefund(userIp, afterSale);
+
         // 更新售后单的状态为【已完成】
         updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus(), new TradeAfterSaleDO()
-                .setStatus(TradeAfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(payRefund.getSuccessTime()));
-
-        // 更新交易订单项的售后状态为【已完成】
-        tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
-                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus());
+                .setStatus(TradeAfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now()));
 
         // TODO 记录售后日志
 
         // TODO 发送售后消息
+
+        // 更新交易订单项的售后状态为【已完成】
+        tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
+                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
+                TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), afterSale.getRefundPrice());
     }
 
-    private PayRefundRespDTO validatePayRefundSuccess(Long payRefundId) {
-        PayRefundRespDTO payRefund = payRefundApi.getPayRefund(payRefundId);
-        if (payRefund == null) {
-            throw exception(AFTER_SALE_REFUND_FAIL_PAY_REFUND_NOT_FOUND);
-        }
-        if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
-            throw exception(AFTER_SALE_REFUND_FAIL_PAY_REFUND_STATUS_NOT_SUCCESS);
-        }
-        return payRefund;
+    private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) {
+        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+
+            @Override
+            public void afterCommit() {
+                // 创建退款单
+                PayRefundCreateReqDTO createReqDTO = TradeAfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties);
+                Long payRefundId = payRefundApi.createPayRefund(createReqDTO);
+                // 更新售后单的退款单号
+                tradeAfterSaleMapper.updateById(new TradeAfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
+            }
+        });
     }
 
 }

+ 3 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java

@@ -50,6 +50,8 @@ public interface TradeOrderService {
      * @param id 交易订单项编号
      * @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
      * @param newAfterSaleStatus 目标售后状态
+     * @param refundPrice 退款金额;当订单项退款成功时,必须传递该值
      */
-    void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus);
+    void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus,
+                                        Integer newAfterSaleStatus, Integer refundPrice);
 }

+ 48 - 7
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.service.order;
 
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@@ -24,17 +25,16 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreate
 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
-import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
+import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
 import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
+import cn.iocoder.yudao.module.trade.enums.order.*;
 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -180,12 +180,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         tradeOrderDO.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
         tradeOrderDO.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
         tradeOrderDO.setType(TradeOrderTypeEnum.NORMAL.getType());
-        tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
+        tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus());
         tradeOrderDO.setProductCount(getSumValue(order.getItems(),  PriceCalculateRespDTO.OrderItem::getCount, Integer::sum));
         tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
         tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息
         tradeOrderDO.setDeliveryStatus(false); // 物流信息
-        tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息
+        tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息
         tradeOrderMapper.insert(tradeOrderDO);
         return tradeOrderDO;
     }
@@ -252,11 +252,52 @@ public class TradeOrderServiceImpl implements TradeOrderService {
     }
 
     @Override
-    public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus) {
+    public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus, Integer refundPrice) {
+        // 如果退款成功,则 refundPrice 非空
+        if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())
+            && refundPrice == null) {
+            throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
+        }
+
+        // 更新订单项
         int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus);
         if (updateCount <= 0) {
             throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
         }
+
+        // 如果有退款金额,则需要更新订单
+        if (refundPrice == null) {
+            return;
+        }
+        // 计算总的退款金额
+        TradeOrderDO order = tradeOrderMapper.selectById(tradeOrderItemMapper.selectById(id).getOrderId());
+        Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
+        if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
+            tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
+                    .setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice)
+                    .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
+
+            // TODO 芋艿:记录订单日志
+
+            // TODO 芋艿:站内信?
+        } else { // 如果部分售后,则更新退款金额
+            tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
+                    .setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
+        }
+
+        // TODO 芋艿:未来如果有分佣,需要更新相关分佣订单为已失效
+    }
+
+    /**
+     * 判断指定订单的所有订单项,是不是都售后成功
+     *
+     * @param id 订单编号
+     * @return 是否都售后成功
+     */
+    private boolean isAllOrderItemAfterSaleSuccess(Long id) {
+        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
+        return orderItems.stream().allMatch(orderItem -> Objects.equals(orderItem.getAfterSaleStatus(),
+                TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus()));
     }
 
 }

+ 2 - 2
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java

@@ -21,7 +21,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
+import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
@@ -182,7 +182,7 @@ class TradeOrderServiceTest extends BaseDbUnitTest {
         assertEquals(tradeOrderDO.getReceiverAreaId(), 3306L);
         assertEquals(tradeOrderDO.getReceiverPostCode(), 85757);
         assertEquals(tradeOrderDO.getReceiverDetailAddress(), "土豆村");
-        assertEquals(tradeOrderDO.getRefundStatus(), TradeOrderRefundStatusEnum.NONE.getStatus());
+        assertEquals(tradeOrderDO.getAfterSaleStatus(), TradeOrderAfterSaleStatusEnum.NONE.getStatus());
         assertEquals(tradeOrderDO.getRefundPrice(), 0);
         assertEquals(tradeOrderDO.getCouponPrice(), 30);
         assertEquals(tradeOrderDO.getPointPrice(), 10);