Commit 00e66216c591c7c55e82758ef3ebdc0b60679eac

Authored by YunaiV
1 parent 35528e82

trade:【交易售后】查询分页列表的前端

Showing 19 changed files with 371 additions and 181 deletions
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleStatusEnum.java
... ... @@ -17,15 +17,15 @@ import java.util.Arrays;
17 17 @Getter
18 18 public enum TradeAfterSaleStatusEnum implements IntArrayValuable {
19 19  
20   - APPLY(10,"申请中"),
21   - SELLER_AGREE(20, "卖家通过"), // 卖家通过售后
22   - BUYER_DELIVERY(30,"待卖家收货"), // 买家已退货,等待卖家收货
23   - WAIT_REFUND(40, "等待平台退款"), // 卖家已收货,等待平台退款
24   - COMPLETE(50, "完成"), // 完成退款
25   -
26   - BUYER_CANCEL(61, "买家取消售后"),
27   - SELLER_DISAGREE(62,"卖家拒绝"), // 卖家拒绝售后
28   - SELLER_REFUSE(63,"卖家拒绝收货"), // 卖家拒绝收货,终止售后
  20 + APPLY(10,"申请中"), // 【申请售后】
  21 + SELLER_AGREE(20, "卖家通过"), // 卖家通过售后;【商品待退货】
  22 + BUYER_DELIVERY(30,"待卖家收货"), // 买家已退货,等待卖家收货;【商家待收货】
  23 + WAIT_REFUND(40, "等待平台退款"), // 卖家已收货,等待平台退款;等待退款【等待退款】
  24 + COMPLETE(50, "完成"), // 完成退款【退款成功】
  25 +
  26 + BUYER_CANCEL(61, "买家取消售后"), // 【买家取消】
  27 + SELLER_DISAGREE(62,"卖家拒绝"), // 卖家拒绝售后;商家拒绝【商家拒绝】
  28 + SELLER_REFUSE(63,"卖家拒绝收货"), // 卖家拒绝收货,终止售后;【商家拒收货】
29 29 ;
30 30  
31 31 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleStatusEnum::getStatus).toArray();
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleTypeEnum.java
... ... @@ -9,23 +9,23 @@ import java.util.Arrays;
9 9 /**
10 10 * 交易售后 - 类型
11 11 *
12   - * @author Sin
  12 + * @author 芋道源码
13 13 */
14 14 @RequiredArgsConstructor
15 15 @Getter
16 16 public enum TradeAfterSaleTypeEnum implements IntArrayValuable {
17 17  
18   - REFUND(10, "退款"),
19   - RETURN_AND_REFUND(20, "退货退款");
  18 + IN_SALE(10, "售中退款"), // 交易完成前买家申请退款
  19 + AFTER_SALE(20, "售后退款"); // 交易完成后买家申请退款
20 20  
21 21 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleTypeEnum::getType).toArray();
22 22  
23 23 /**
24   - * 状态值
  24 + * 类型
25 25 */
26 26 private final Integer type;
27 27 /**
28   - * 状态名
  28 + * 类型名
29 29 */
30 30 private final String name;
31 31  
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleWayEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.enums.aftersale;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.Getter;
  5 +import lombok.RequiredArgsConstructor;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * 交易售后 - 方式
  11 + *
  12 + * @author Sin
  13 + */
  14 +@RequiredArgsConstructor
  15 +@Getter
  16 +public enum TradeAfterSaleWayEnum implements IntArrayValuable {
  17 +
  18 + REFUND(10, "仅退款"),
  19 + RETURN_AND_REFUND(20, "退货退款");
  20 +
  21 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleWayEnum::getWay).toArray();
  22 +
  23 + /**
  24 + * 方式
  25 + */
  26 + private final Integer way;
  27 + /**
  28 + * 方式名
  29 + */
  30 + private final String name;
  31 +
  32 + @Override
  33 + public int[] array() {
  34 + return ARRAYS;
  35 + }
  36 +
  37 +}
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java
... ... @@ -53,6 +53,16 @@ public enum TradeOrderStatusEnum implements IntArrayValuable {
53 53 }
54 54  
55 55 /**
  56 + * 判断指定状态,是否正处于【已完成】状态
  57 + *
  58 + * @param status 指定状态
  59 + * @return 是否
  60 + */
  61 + public static boolean isCompleted(Integer status) {
  62 + return ObjectUtil.equals(status, COMPLETED.getStatus());
  63 + }
  64 +
  65 + /**
56 66 * 判断指定状态,是否有过【已付款】状态
57 67 *
58 68 * @param status 指定状态
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleBaseVO.java
... ... @@ -22,14 +22,18 @@ public class TradeAfterSaleBaseVO {
22 22 @NotNull(message = "售后流水号不能为空")
23 23 private String no;
24 24  
25   - @ApiModelProperty(value = "售后状态", required = true, example = "2", notes = "参见 TradeAfterSaleStatusEnum 枚举")
  25 + @ApiModelProperty(value = "售后状态", required = true, example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
26 26 @NotNull(message = "售后状态不能为空")
27 27 private Integer status;
28 28  
29   - @ApiModelProperty(value = "售后类型", required = true, example = "2", notes = "参见 TradeAfterSaleTypeEnum 枚举")
  29 + @ApiModelProperty(value = "售后类型", required = true, example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
30 30 @NotNull(message = "售后类型不能为空")
31 31 private Integer type;
32 32  
  33 + @ApiModelProperty(value = "售后方式", required = true, example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
  34 + @NotNull(message = "售后方式不能为空")
  35 + private Integer way;
  36 +
33 37 @ApiModelProperty(value = "用户编号", required = true, example = "30337")
34 38 @NotNull(message = "用户编号不能为空")
35 39 private Long userId;
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSalePageReqVO.java
... ... @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
5 5 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
6 6 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
  7 +import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
7 8 import io.swagger.annotations.ApiModel;
8 9 import io.swagger.annotations.ApiModelProperty;
9 10 import lombok.Data;
... ... @@ -24,14 +25,18 @@ public class TradeAfterSalePageReqVO extends PageParam {
24 25 @ApiModelProperty(value = "售后流水号", example = "202211190847450020500077", notes = "模糊匹配")
25 26 private String no;
26 27  
27   - @ApiModelProperty(value = "售后状态", example = "2", notes = "参见 TradeAfterSaleStatusEnum 枚举")
  28 + @ApiModelProperty(value = "售后状态", example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
28 29 @InEnum(value = TradeAfterSaleStatusEnum.class, message = "售后状态必须是 {value}")
29 30 private Integer status;
30 31  
31   - @ApiModelProperty(value = "售后类型", example = "2", notes = "参见 TradeAfterSaleTypeEnum 枚举")
  32 + @ApiModelProperty(value = "售后类型", example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
32 33 @InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}")
33 34 private Integer type;
34 35  
  36 + @ApiModelProperty(value = "售后方式", example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
  37 + @InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
  38 + private Integer way;
  39 +
35 40 @ApiModelProperty(value = "订单编号", example = "18078", notes = "模糊匹配")
36 41 private String orderNo;
37 42  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppTradeAfterSaleCreateReqVO.java
1 1 package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4   -import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
  4 +import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
5 5 import io.swagger.annotations.ApiModel;
6 6 import io.swagger.annotations.ApiModelProperty;
7 7 import lombok.Data;
... ... @@ -18,10 +18,10 @@ public class AppTradeAfterSaleCreateReqVO {
18 18 @NotNull(message = "订单项编号不能为空")
19 19 private Long orderItemId;
20 20  
21   - @ApiModelProperty(name = "售后类型", required = true, example = "1", notes = "对应 TradeAfterSaleTypeEnum 枚举")
22   - @NotNull(message = "售后类型不能为空")
23   - @InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}")
24   - private Integer type;
  21 + @ApiModelProperty(name = "售后方式", required = true, example = "1", notes = "对应 TradeAfterSaleWayEnum 枚举")
  22 + @NotNull(message = "售后方式不能为空")
  23 + @InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
  24 + private Integer way;
25 25  
26 26 @ApiModelProperty(name = "退款金额", required = true, example = "100", notes = "单位:分")
27 27 @NotNull(message = "退款金额不能为空")
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/aftersale/TradeAfterSaleDO.java
... ... @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
5 5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
6 6 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
7 7 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
  8 +import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
8 9 import com.baomidou.mybatisplus.annotation.TableField;
9 10 import com.baomidou.mybatisplus.annotation.TableName;
10 11 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
... ... @@ -43,6 +44,12 @@ public class TradeAfterSaleDO extends BaseDO {
43 44 */
44 45 private Integer status;
45 46 /**
  47 + * 售后方式
  48 + *
  49 + * 枚举 {@link TradeAfterSaleWayEnum}
  50 + */
  51 + private Integer way;
  52 + /**
46 53 * 售后类型
47 54 *
48 55 * 枚举 {@link TradeAfterSaleTypeEnum}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/aftersale/TradeAfterSaleMapper.java
... ... @@ -16,6 +16,7 @@ public interface TradeAfterSaleMapper extends BaseMapperX<TradeAfterSaleDO> {
16 16 .likeIfPresent(TradeAfterSaleDO::getNo, reqVO.getNo())
17 17 .eqIfPresent(TradeAfterSaleDO::getStatus, reqVO.getStatus())
18 18 .eqIfPresent(TradeAfterSaleDO::getType, reqVO.getType())
  19 + .eqIfPresent(TradeAfterSaleDO::getWay, reqVO.getWay())
19 20 .likeIfPresent(TradeAfterSaleDO::getOrderNo, reqVO.getOrderNo())
20 21 .likeIfPresent(TradeAfterSaleDO::getSpuName, reqVO.getSpuName())
21 22 .betweenIfPresent(TradeAfterSaleDO::getCreateTime, reqVO.getCreateTime())
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java
... ... @@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
17 17 import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
18 18 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
19 19 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
  20 +import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
20 21 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
21 22 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
22 23 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
... ... @@ -88,7 +89,6 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
88 89 if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) {
89 90 throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED);
90 91 }
91   - // TODO 芋艿:超过一定时间,不允许售后
92 92  
93 93 // 申请的退款金额,不能超过商品的价格
94 94 if (createReqVO.getRefundPrice() > orderItem.getOrderDividePrice()) {
... ... @@ -100,6 +100,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
100 100 if (order == null) {
101 101 throw exception(ORDER_NOT_FOUND);
102 102 }
  103 + // TODO 芋艿:超过一定时间,不允许售后
103 104 // 已取消,无法发起售后
104 105 if (TradeOrderStatusEnum.isCanceled(order.getStatus())) {
105 106 throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED);
... ... @@ -109,7 +110,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
109 110 throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID);
110 111 }
111 112 // 如果是【退货退款】的情况,需要额外校验是否发货
112   - if (createReqVO.getType().equals(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType())
  113 + if (createReqVO.getWay().equals(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
113 114 && !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
114 115 throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED);
115 116 }
... ... @@ -117,16 +118,21 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
117 118 }
118 119  
119 120 private TradeAfterSaleDO createAfterSale(AppTradeAfterSaleCreateReqVO createReqVO,
120   - TradeOrderItemDO tradeOrderItem) {
  121 + TradeOrderItemDO orderItem) {
121 122 // 创建售后单
122   - TradeAfterSaleDO afterSale = TradeAfterSaleConvert.INSTANCE.convert(createReqVO, tradeOrderItem);
  123 + TradeAfterSaleDO afterSale = TradeAfterSaleConvert.INSTANCE.convert(createReqVO, orderItem);
123 124 afterSale.setNo(RandomUtil.randomString(10)); // TODO 芋艿:优化 no 生成逻辑
124 125 afterSale.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
  126 + // 标记是售中还是售后
  127 + TradeOrderDO order = tradeOrderService.getOrder(orderItem.getUserId(), orderItem.getOrderId());
  128 + afterSale.setOrderNo(order.getNo()); // 记录 orderNo 订单流水,方便后续检索
  129 + afterSale.setType(TradeOrderStatusEnum.isCompleted(order.getStatus())
  130 + ? TradeAfterSaleTypeEnum.AFTER_SALE.getType() : TradeAfterSaleTypeEnum.IN_SALE.getType());
125 131 // TODO 退还积分
126 132 tradeAfterSaleMapper.insert(afterSale);
127 133  
128 134 // 更新交易订单项的售后状态
129   - tradeOrderService.updateOrderItemAfterSaleStatus(tradeOrderItem.getId(),
  135 + tradeOrderService.updateOrderItemAfterSaleStatus(orderItem.getId(),
130 136 TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
131 137 TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), null);
132 138  
... ... @@ -145,7 +151,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
145 151 // 更新售后单的状态
146 152 // 情况一:退款:标记为 WAIT_REFUND 状态。后续等退款发起成功后,在标记为 COMPLETE 状态
147 153 // 情况二:退货退款:需要等用户退货后,才能发起退款
148   - Integer newStatus = afterSale.getType().equals(TradeAfterSaleTypeEnum.REFUND.getType()) ?
  154 + Integer newStatus = afterSale.getType().equals(TradeAfterSaleWayEnum.REFUND.getWay()) ?
149 155 TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus() : TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus();
150 156 updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
151 157 .setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()));
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java
... ... @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
11 11 import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
12 12 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
13 13 import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
  14 +import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
14 15 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
15 16 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
16 17 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
... ... @@ -58,7 +59,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
58 59 // 准备参数
59 60 Long userId = 1024L;
60 61 AppTradeAfterSaleCreateReqVO createReqVO = new AppTradeAfterSaleCreateReqVO()
61   - .setOrderItemId(1L).setRefundPrice(100).setType(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType())
  62 + .setOrderItemId(1L).setRefundPrice(100).setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
62 63 .setApplyReason("退钱").setApplyDescription("快退")
63 64 .setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
64 65 // mock 方法(交易订单项)
... ... @@ -69,7 +70,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
69 70 when(tradeOrderService.getOrderItem(eq(1024L), eq(1L)))
70 71 .thenReturn(orderItem);
71 72 // mock 方法(交易订单)
72   - TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> o.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()));
  73 + TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> o.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
  74 + .setNo("202211301234"));
73 75 when(tradeOrderService.getOrder(eq(1024L), eq(111L))).thenReturn(order);
74 76  
75 77 // 调用
... ... @@ -78,9 +80,11 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
78 80 TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(afterSaleId);
79 81 assertNotNull(afterSale.getNo());
80 82 assertEquals(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus());
  83 + assertEquals(afterSale.getType(), TradeAfterSaleTypeEnum.IN_SALE.getType());
81 84 assertPojoEquals(afterSale, createReqVO);
82 85 assertEquals(afterSale.getUserId(), 1024L);
83 86 assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime");
  87 + assertEquals(afterSale.getOrderNo(), "202211301234");
84 88 assertNull(afterSale.getPayRefundId());
85 89 assertNull(afterSale.getRefundTime());
86 90 assertNull(afterSale.getLogisticsId());
... ... @@ -95,7 +99,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
95 99 TradeAfterSaleDO dbAfterSale = randomPojo(TradeAfterSaleDO.class, o -> { // 等会查询到
96 100 o.setNo("202211190847450020500077");
97 101 o.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
98   - o.setType(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType());
  102 + o.setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay());
  103 + o.setType(TradeAfterSaleTypeEnum.IN_SALE.getType());
99 104 o.setOrderNo("202211190847450020500011");
100 105 o.setSpuName("芋艿");
101 106 o.setCreateTime(buildTime(2022, 1, 15));
... ... @@ -105,8 +110,10 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
105 110 tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setNo("202211190847450020500066")));
106 111 // 测试 status 不匹配
107 112 tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setStatus(TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus())));
  113 + // 测试 way 不匹配
  114 + tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setWay(TradeAfterSaleWayEnum.REFUND.getWay())));
108 115 // 测试 type 不匹配
109   - tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setType(TradeAfterSaleTypeEnum.REFUND.getType())));
  116 + tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setType(TradeAfterSaleTypeEnum.AFTER_SALE.getType())));
110 117 // 测试 orderNo 不匹配
111 118 tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setOrderNo("202211190847450020500022")));
112 119 // 测试 spuName 不匹配
... ... @@ -117,7 +124,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
117 124 TradeAfterSalePageReqVO reqVO = new TradeAfterSalePageReqVO();
118 125 reqVO.setNo("20221119084745002050007");
119 126 reqVO.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
120   - reqVO.setType(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType());
  127 + reqVO.setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay());
  128 + reqVO.setType(TradeAfterSaleTypeEnum.IN_SALE.getType());
121 129 reqVO.setOrderNo("20221119084745002050001");
122 130 reqVO.setSpuName("芋");
123 131 reqVO.setCreateTime(new LocalDateTime[]{buildTime(2022, 1, 1), buildTime(2022, 1, 16)});
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql
... ... @@ -75,6 +75,7 @@ CREATE TABLE IF NOT EXISTS "trade_after_sale" (
75 75 "no" varchar NOT NULL,
76 76 "status" int NOT NULL,
77 77 "type" int NOT NULL,
  78 + "way" int NOT NULL,
78 79 "user_id" bigint NOT NULL,
79 80 "apply_reason" varchar NOT NULL,
80 81 "apply_description" varchar,
... ...
yudao-ui-admin/src/api/mall/trade/afterSale.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获得交易售后
  4 +export function getAfterSale(id) {
  5 + return request({
  6 + url: '/trade/after-sale/get?id=' + id,
  7 + method: 'get'
  8 + })
  9 +}
  10 +
  11 +// 获得交易售后分页
  12 +export function getAfterSalePage(query) {
  13 + return request({
  14 + url: '/trade/after-sale/page',
  15 + method: 'get',
  16 + params: query
  17 + })
  18 +}
... ...
yudao-ui-admin/src/router/index.js
... ... @@ -216,12 +216,6 @@ export const constantRoutes = [
216 216 hidden: true,
217 217 meta: { title: '订单详情' },
218 218 component: (resolve) => require(['@/views/mall/trade/order/detail'], resolve)
219   - },
220   - {
221   - path: '/mall/trade/orderrefund',
222   - name: '退款维权',
223   - meta: { title: '退款维权' },
224   - component: (resolve) => require(['@/views/mall/trade/orderrefund'], resolve)
225 219 }
226 220 ]
227 221 }
... ...
yudao-ui-admin/src/utils/constants.js
... ... @@ -3,6 +3,37 @@
3 3 *
4 4 * 枚举类
5 5 */
  6 +import {beginOfDay, endOfDay} from "@/utils/dateUtils";
  7 +
  8 +export const datePickerOptions = {
  9 + shortcuts: [{
  10 + text: '最近一周',
  11 + onClick(picker) {
  12 + const start = new Date();
  13 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
  14 + const end = new Date();
  15 + picker.$emit('pick', [beginOfDay(start), endOfDay(end)]);
  16 + }
  17 + }, {
  18 + text: '最近一个月',
  19 + onClick(picker) {
  20 + const start = new Date();
  21 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
  22 + const end = new Date();
  23 + picker.$emit('pick', [beginOfDay(start), endOfDay(end)]);
  24 + }
  25 + }, {
  26 + text: '最近三个月',
  27 + onClick(picker) {
  28 + const start = new Date();
  29 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
  30 + const end = new Date();
  31 + picker.$emit('pick', [beginOfDay(start), endOfDay(end)]);
  32 + }
  33 + }]
  34 +}
  35 +
  36 +// ========== 静态变量 ==========
6 37  
7 38 /**
8 39 * 全局通用状态枚举
... ...
yudao-ui-admin/src/utils/dateUtils.js
... ... @@ -24,3 +24,11 @@ export function getDate(ms) {
24 24 return 0 + "秒";
25 25 }
26 26 }
  27 +
  28 +export function beginOfDay(date) {
  29 + return new Date(date.getFullYear(), date.getMonth(), date.getDate());
  30 +}
  31 +
  32 +export function endOfDay(date) {
  33 + return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
  34 +}
... ...
yudao-ui-admin/src/utils/dict.js
... ... @@ -60,6 +60,11 @@ export const DICT_TYPE = {
60 60 // ========== MALL - PRODUCT 模块 ==========
61 61 PRODUCT_SPU_STATUS: 'product_spu_status', // 商品 SPU 状态
62 62  
  63 + // ========== MALL - ORDER 模块 ==========
  64 + TRADE_AFTER_SALE_STATUS: 'trade_after_sale_status', // 售后 - 状态
  65 + TRADE_AFTER_SALE_WAY: 'trade_after_sale_way', // 售后 - 方式
  66 + TRADE_AFTER_SALE_TYPE: 'trade_after_sale_type', // 售后 - 类型
  67 +
63 68 // ========== MALL - PROMOTION 模块 ==========
64 69 PROMOTION_DISCOUNT_TYPE: 'promotion_discount_type', // 优惠类型
65 70 PROMOTION_PRODUCT_SCOPE: 'promotion_product_scope', // 营销的商品范围
... ...
yudao-ui-admin/src/views/mall/trade/orderrefund/index.vue renamed to yudao-ui-admin/src/views/mall/trade/afterSale/bak.vue
... ... @@ -4,42 +4,6 @@
4 4 <el-row :gutter="20">
5 5 <el-form :model="queryParams" label-width="68px" size="small">
6 6 <el-col :span="6" :xs="24">
7   - <el-form-item label="商品名称">
8   - <el-input v-model="queryParams.name" style="width: 240px"></el-input>
9   - </el-form-item>
10   - </el-col>
11   - <el-col :span="6" :xs="24">
12   - <el-form-item label="订单编号">
13   - <el-input v-model="queryParams.No" style="width: 240px"></el-input>
14   - </el-form-item>
15   - </el-col>
16   - <el-col :span="6" :xs="24">
17   - <el-form-item label="退款编号">
18   - <el-input v-model="queryParams.refundNo" style="width: 240px"></el-input>
19   - </el-form-item>
20   - </el-col>
21   - <el-col :span="6" :xs="24">
22   - <el-form-item label="退款状态">
23   - <el-select v-model="queryParams.refundStatus" clearable style="width: 240px">
24   - <el-option v-for="dict in dicData.refundStatus" v-bind="dict" :key="dict.value"/>
25   - </el-select>
26   - </el-form-item>
27   - </el-col>
28   - <el-col :span="6" :xs="24">
29   - <el-form-item label="退款方式">
30   - <el-select v-model="queryParams.refundWay" clearable style="width: 240px">
31   - <el-option v-for="dict in dicData.refundWay" v-bind="dict" :key="dict.value"/>
32   - </el-select>
33   - </el-form-item>
34   - </el-col>
35   - <el-col :span="6" :xs="24">
36   - <el-form-item label="维权类型">
37   - <el-select v-model="queryParams.refundType" clearable style="width: 240px">
38   - <el-option v-for="dict in dicData.refundType" v-bind="dict" :key="dict.value"/>
39   - </el-select>
40   - </el-form-item>
41   - </el-col>
42   - <el-col :span="6" :xs="24">
43 7 <el-form-item label="下单时间">
44 8 <el-date-picker v-model="queryParams.date" type="daterange" range-separator="至"
45 9 start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="rangePickerOptions" style="width: 240px"/>
... ... @@ -60,12 +24,6 @@
60 24 <el-table :data="tableData" :show-header="false" class="table-wrapper">
61 25 <el-table-column>
62 26 <template slot-scope="{ row }">
63   - <div class="table-header">
64   - 退款编号:{{row.tkbh}}
65   - <el-button type="text" style="margin-left: 10px">
66   - 订单编号:{{row.ddbh}}
67   - </el-button>
68   - </div>
69 27 <!-- 订单下的商品 -->
70 28 <el-table :data="row.goods" border>
71 29 <el-table-column label="商品信息" prop="spxx" header-align="center" width="auto" min-width="300">
... ... @@ -114,33 +72,7 @@ const dicData = {
114 72 { label: '售后退款', value: 'shtk' }
115 73 ]
116 74 }
117   -const rangePickerOptions = {
118   - shortcuts: [{
119   - text: '最近一周',
120   - onClick(picker) {
121   - const end = new Date();
122   - const start = new Date();
123   - start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
124   - picker.$emit('pick', [start, end]);
125   - }
126   - }, {
127   - text: '最近一个月',
128   - onClick(picker) {
129   - const end = new Date();
130   - const start = new Date();
131   - start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
132   - picker.$emit('pick', [start, end]);
133   - }
134   - }, {
135   - text: '最近三个月',
136   - onClick(picker) {
137   - const end = new Date();
138   - const start = new Date();
139   - start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
140   - picker.$emit('pick', [start, end]);
141   - }
142   - }]
143   -}
  75 +
144 76 export default {
145 77 name: "index",
146 78 data () {
... ... @@ -172,15 +104,6 @@ export default {
172 104 tkje: 460,
173 105 sqsj: '2022-11-19',
174 106 tkzt: '申请维权(仅退款)'
175   - },
176   - {
177   - name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
178   - img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
179   - ddje: '199',
180   - mj: '张三',
181   - tkje: 460,
182   - sqsj: '2022-11-19',
183   - tkzt: '申请维权(仅退款)'
184 107 }
185 108 ]
186 109 },
... ... @@ -196,15 +119,6 @@ export default {
196 119 tkje: 460,
197 120 sqsj: '2022-11-19',
198 121 tkzt: '申请维权(仅退款)'
199   - },
200   - {
201   - name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
202   - img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
203   - ddje: '199',
204   - mj: '张三',
205   - tkje: 460,
206   - sqsj: '2022-11-19',
207   - tkzt: '申请维权(仅退款)'
208 122 }
209 123 ]
210 124 },
... ... @@ -220,15 +134,6 @@ export default {
220 134 tkje: 460,
221 135 sqsj: '2022-11-19',
222 136 tkzt: '申请维权(仅退款)'
223   - },
224   - {
225   - name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
226   - img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
227   - ddje: '199',
228   - mj: '张三',
229   - tkje: 460,
230   - sqsj: '2022-11-19',
231   - tkzt: '申请维权(仅退款)'
232 137 }
233 138 ]
234 139 }
... ... @@ -242,48 +147,3 @@ export default {
242 147 }
243 148 }
244 149 </script>
245   -
246   -<style lang="scss" scoped>
247   - ::v-deep .table-wrapper{
248   - border-bottom: none;
249   - &::before{
250   - height: 0;
251   - }
252   - .table-header{
253   - line-height: 36px;
254   - }
255   - .el-table__row{
256   - .el-table__cell{
257   - border-bottom: none;
258   - .cell{
259   - .el-table {
260   - .el-table__row{
261   - >.el-table__cell{
262   - .goods-info{
263   - display: flex;
264   - img{
265   - margin-right: 10px;
266   - width: 60px;
267   - height: 60px;
268   - border: 1px solid #e2e2e2;
269   - }
270   - }
271   - .ellipsis-2{
272   - display: -webkit-box;
273   - overflow: hidden;
274   - text-overflow: ellipsis;
275   - white-space: normal;
276   - -webkit-line-clamp: 2; /* 要显示的行数 */
277   - -webkit-box-orient: vertical;
278   - word-break: break-all;
279   - line-height: 22px !important;
280   - max-height: 44px !important;
281   - }
282   - }
283   - }
284   - }
285   - }
286   - }
287   - }
288   - }
289   -</style>
... ...
yudao-ui-admin/src/views/mall/trade/afterSale/index.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="商品名称" prop="spuName">
  7 + <el-input v-model="queryParams.spuName" placeholder="请输入商品 SPU 名称" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="退款编号" prop="no">
  10 + <el-input v-model="queryParams.no" placeholder="请输入退款编号" clearable @keyup.enter.native="handleQuery"/>
  11 + </el-form-item>
  12 + <el-form-item label="订单编号" prop="orderNo">
  13 + <el-input v-model="queryParams.orderNo" placeholder="请输入订单编号" clearable @keyup.enter.native="handleQuery"/>
  14 + </el-form-item>
  15 + <el-form-item label="售后状态" prop="status">
  16 + <el-select v-model="queryParams.status" placeholder="请选择售后状态" clearable size="small">
  17 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)"
  18 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="售后方式" prop="way">
  22 + <el-select v-model="queryParams.way" placeholder="请选择售后方式" clearable size="small">
  23 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_WAY)"
  24 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  25 + </el-select>
  26 + </el-form-item>
  27 + <el-form-item label="售后类型" prop="type">
  28 + <el-select v-model="queryParams.type" placeholder="请选择售后类型" clearable size="small">
  29 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_TYPE)"
  30 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  31 + </el-select>
  32 + </el-form-item>
  33 + <el-form-item label="创建时间" prop="createTime">
  34 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  35 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
  36 + :picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
  37 + </el-form-item>
  38 + <el-form-item>
  39 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  40 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  41 + </el-form-item>
  42 + </el-form>
  43 +
  44 + <!-- 操作工具栏 -->
  45 + <el-row :gutter="10" class="mb8">
  46 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  47 + </el-row>
  48 +
  49 + <!-- 列表 -->
  50 + <el-table v-loading="loading" :data="list">
  51 + <el-table-column label="退款编号" align="center" prop="no" />
  52 + <el-table-column label="订单编号" align="center" prop="orderNo" />
  53 + <el-table-column label="订单编号" align="center" prop="orderNo" />
  54 + <el-table-column label="商品信息" align="center" prop="status" width="auto" min-width="300">
  55 + <!-- TODO @小红:样式不太对,辛苦改改 -->
  56 +<!-- <div slot-scope="{ row }" class="goods-info">-->
  57 +<!-- <img :src="row.picUrl"/>-->
  58 +<!-- <span class="ellipsis-2" :title="row.name">{{row.name}}</span>-->
  59 +<!-- </div>-->
  60 + </el-table-column>
  61 + <el-table-column label="订单金额" align="center" prop="refundPrice" />
  62 + <el-table-column label="买家" align="center" prop="userId" />
  63 + <el-table-column label="申请时间" align="center" prop="createTime" width="180">
  64 + <template v-slot="scope">
  65 + <span>{{ parseTime(scope.row.createTime) }}</span>
  66 + </template>
  67 + </el-table-column>
  68 + <el-table-column label="售后状态" align="center">
  69 + <template v-slot="scope">
  70 + <dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
  71 + </template>
  72 + </el-table-column>
  73 + <el-table-column label="售后方式" align="center">
  74 + <template v-slot="scope">
  75 + <dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
  76 + </template>
  77 + </el-table-column>
  78 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  79 + <template v-slot="scope">
  80 +<!-- <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"-->
  81 +<!-- v-hasPermi="['trade:after-sale:update']">修改</el-button>-->
  82 + </template>
  83 + </el-table-column>
  84 + </el-table>
  85 + <!-- 分页组件 -->
  86 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  87 + @pagination="getList"/>
  88 + </div>
  89 +</template>
  90 +
  91 +<script>
  92 +import { getAfterSalePage } from "@/api/mall/trade/afterSale";
  93 +import { datePickerOptions } from "@/utils/constants";
  94 +
  95 +export default {
  96 + name: "AfterSale",
  97 + components: {
  98 + },
  99 + data() {
  100 + return {
  101 + // 遮罩层
  102 + loading: true,
  103 + // 导出遮罩层
  104 + exportLoading: false,
  105 + // 显示搜索条件
  106 + showSearch: true,
  107 + // 总条数
  108 + total: 0,
  109 + // 交易售后列表
  110 + list: [],
  111 + // 弹出层标题
  112 + title: "",
  113 + // 是否显示弹出层
  114 + open: false,
  115 + // 查询参数
  116 + queryParams: {
  117 + pageNo: 1,
  118 + pageSize: 10,
  119 + no: null,
  120 + status: null,
  121 + orderNo: null,
  122 + spuName: null,
  123 + createTime: [],
  124 + way: null,
  125 + type: null,
  126 + },
  127 + // 静态变量
  128 + datePickerOptions: datePickerOptions
  129 + };
  130 + },
  131 + created() {
  132 + this.getList();
  133 + },
  134 + methods: {
  135 + /** 查询列表 */
  136 + getList() {
  137 + this.loading = true;
  138 + // 执行查询
  139 + getAfterSalePage(this.queryParams).then(response => {
  140 + this.list = response.data.list;
  141 + this.total = response.data.total;
  142 + this.loading = false;
  143 + });
  144 + },
  145 + /** 搜索按钮操作 */
  146 + handleQuery() {
  147 + this.queryParams.pageNo = 1;
  148 + this.getList();
  149 + },
  150 + /** 重置按钮操作 */
  151 + resetQuery() {
  152 + this.resetForm("queryForm");
  153 + this.handleQuery();
  154 + },
  155 + }
  156 +};
  157 +</script>
  158 +
  159 +<style lang="scss" scoped>
  160 +::v-deep .table-wrapper {
  161 + .el-table__row{
  162 + .el-table__cell {
  163 + border-bottom: none;
  164 + .cell{
  165 + .el-table {
  166 + .el-table__row {
  167 + >.el-table__cell {
  168 + .goods-info{
  169 + display: flex;
  170 + img{
  171 + margin-right: 10px;
  172 + width: 60px;
  173 + height: 60px;
  174 + border: 1px solid #e2e2e2;
  175 + }
  176 + }
  177 + .ellipsis-2 {
  178 + display: -webkit-box;
  179 + overflow: hidden;
  180 + text-overflow: ellipsis;
  181 + white-space: normal;
  182 + -webkit-line-clamp: 2; /* 要显示的行数 */
  183 + -webkit-box-orient: vertical;
  184 + word-break: break-all;
  185 + line-height: 22px !important;
  186 + max-height: 44px !important;
  187 + }
  188 + }
  189 + }
  190 + }
  191 + }
  192 + }
  193 + }
  194 +}
  195 +</style>
... ...