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

This commit is contained in:
YunaiV 2022-11-20 01:05:03 +08:00
parent 35528e8267
commit 00e66216c5
19 changed files with 370 additions and 180 deletions

View File

@ -17,15 +17,15 @@ import java.util.Arrays;
@Getter @Getter
public enum TradeAfterSaleStatusEnum implements IntArrayValuable { public enum TradeAfterSaleStatusEnum implements IntArrayValuable {
APPLY(10,"申请中"), APPLY(10,"申请中"), // 申请售后
SELLER_AGREE(20, "卖家通过"), // 卖家通过售后 SELLER_AGREE(20, "卖家通过"), // 卖家通过售后商品待退货
BUYER_DELIVERY(30,"待卖家收货"), // 买家已退货等待卖家收货 BUYER_DELIVERY(30,"待卖家收货"), // 买家已退货等待卖家收货商家待收货
WAIT_REFUND(40, "等待平台退款"), // 卖家已收货等待平台退款 WAIT_REFUND(40, "等待平台退款"), // 卖家已收货等待平台退款等待退款等待退款
COMPLETE(50, "完成"), // 完成退款 COMPLETE(50, "完成"), // 完成退款退款成功
BUYER_CANCEL(61, "买家取消售后"), BUYER_CANCEL(61, "买家取消售后"), // 买家取消
SELLER_DISAGREE(62,"卖家拒绝"), // 卖家拒绝售后 SELLER_DISAGREE(62,"卖家拒绝"), // 卖家拒绝售后商家拒绝商家拒绝
SELLER_REFUSE(63,"卖家拒绝收货"), // 卖家拒绝收货终止售后 SELLER_REFUSE(63,"卖家拒绝收货"), // 卖家拒绝收货终止售后商家拒收货
; ;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleStatusEnum::getStatus).toArray(); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleStatusEnum::getStatus).toArray();

View File

@ -9,23 +9,23 @@ import java.util.Arrays;
/** /**
* 交易售后 - 类型 * 交易售后 - 类型
* *
* @author Sin * @author 芋道源码
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Getter @Getter
public enum TradeAfterSaleTypeEnum implements IntArrayValuable { public enum TradeAfterSaleTypeEnum implements IntArrayValuable {
REFUND(10, "退款"), IN_SALE(10, "售中退款"), // 交易完成前买家申请退款
RETURN_AND_REFUND(20, "退货退款"); AFTER_SALE(20, "售后退款"); // 交易完成后买家申请退款
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleTypeEnum::getType).toArray(); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleTypeEnum::getType).toArray();
/** /**
* 状态值 * 类型
*/ */
private final Integer type; private final Integer type;
/** /**
* 状态 * 类型
*/ */
private final String name; private final String name;

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.trade.enums.aftersale;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
/**
* 交易售后 - 方式
*
* @author Sin
*/
@RequiredArgsConstructor
@Getter
public enum TradeAfterSaleWayEnum implements IntArrayValuable {
REFUND(10, "仅退款"),
RETURN_AND_REFUND(20, "退货退款");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleWayEnum::getWay).toArray();
/**
* 方式
*/
private final Integer way;
/**
* 方式名
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -52,6 +52,16 @@ public enum TradeOrderStatusEnum implements IntArrayValuable {
return ObjectUtil.equals(status, CANCELED.getStatus()); return ObjectUtil.equals(status, CANCELED.getStatus());
} }
/**
* 判断指定状态是否正处于已完成状态
*
* @param status 指定状态
* @return 是否
*/
public static boolean isCompleted(Integer status) {
return ObjectUtil.equals(status, COMPLETED.getStatus());
}
/** /**
* 判断指定状态是否有过已付款状态 * 判断指定状态是否有过已付款状态
* *

View File

@ -22,14 +22,18 @@ public class TradeAfterSaleBaseVO {
@NotNull(message = "售后流水号不能为空") @NotNull(message = "售后流水号不能为空")
private String no; private String no;
@ApiModelProperty(value = "售后状态", required = true, example = "2", notes = "参见 TradeAfterSaleStatusEnum 枚举") @ApiModelProperty(value = "售后状态", required = true, example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
@NotNull(message = "售后状态不能为空") @NotNull(message = "售后状态不能为空")
private Integer status; private Integer status;
@ApiModelProperty(value = "售后类型", required = true, example = "2", notes = "参见 TradeAfterSaleTypeEnum 枚举") @ApiModelProperty(value = "售后类型", required = true, example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
@NotNull(message = "售后类型不能为空") @NotNull(message = "售后类型不能为空")
private Integer type; private Integer type;
@ApiModelProperty(value = "售后方式", required = true, example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
@NotNull(message = "售后方式不能为空")
private Integer way;
@ApiModelProperty(value = "用户编号", required = true, example = "30337") @ApiModelProperty(value = "用户编号", required = true, example = "30337")
@NotNull(message = "用户编号不能为空") @NotNull(message = "用户编号不能为空")
private Long userId; private Long userId;

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -24,14 +25,18 @@ public class TradeAfterSalePageReqVO extends PageParam {
@ApiModelProperty(value = "售后流水号", example = "202211190847450020500077", notes = "模糊匹配") @ApiModelProperty(value = "售后流水号", example = "202211190847450020500077", notes = "模糊匹配")
private String no; private String no;
@ApiModelProperty(value = "售后状态", example = "2", notes = "参见 TradeAfterSaleStatusEnum 枚举") @ApiModelProperty(value = "售后状态", example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
@InEnum(value = TradeAfterSaleStatusEnum.class, message = "售后状态必须是 {value}") @InEnum(value = TradeAfterSaleStatusEnum.class, message = "售后状态必须是 {value}")
private Integer status; private Integer status;
@ApiModelProperty(value = "售后类型", example = "2", notes = "参见 TradeAfterSaleTypeEnum 枚举") @ApiModelProperty(value = "售后类型", example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
@InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}") @InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}")
private Integer type; private Integer type;
@ApiModelProperty(value = "售后方式", example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
@InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
private Integer way;
@ApiModelProperty(value = "订单编号", example = "18078", notes = "模糊匹配") @ApiModelProperty(value = "订单编号", example = "18078", notes = "模糊匹配")
private String orderNo; private String orderNo;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo; package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -18,10 +18,10 @@ public class AppTradeAfterSaleCreateReqVO {
@NotNull(message = "订单项编号不能为空") @NotNull(message = "订单项编号不能为空")
private Long orderItemId; private Long orderItemId;
@ApiModelProperty(name = "售后类型", required = true, example = "1", notes = "对应 TradeAfterSaleTypeEnum 枚举") @ApiModelProperty(name = "售后方式", required = true, example = "1", notes = "对应 TradeAfterSaleWayEnum 枚举")
@NotNull(message = "售后类型不能为空") @NotNull(message = "售后方式不能为空")
@InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}") @InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
private Integer type; private Integer way;
@ApiModelProperty(name = "退款金额", required = true, example = "100", notes = "单位:分") @ApiModelProperty(name = "退款金额", required = true, example = "100", notes = "单位:分")
@NotNull(message = "退款金额不能为空") @NotNull(message = "退款金额不能为空")

View File

@ -5,6 +5,7 @@ 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.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
@ -42,6 +43,12 @@ public class TradeAfterSaleDO extends BaseDO {
* 枚举 {@link TradeAfterSaleStatusEnum} * 枚举 {@link TradeAfterSaleStatusEnum}
*/ */
private Integer status; private Integer status;
/**
* 售后方式
*
* 枚举 {@link TradeAfterSaleWayEnum}
*/
private Integer way;
/** /**
* 售后类型 * 售后类型
* *

View File

@ -16,6 +16,7 @@ public interface TradeAfterSaleMapper extends BaseMapperX<TradeAfterSaleDO> {
.likeIfPresent(TradeAfterSaleDO::getNo, reqVO.getNo()) .likeIfPresent(TradeAfterSaleDO::getNo, reqVO.getNo())
.eqIfPresent(TradeAfterSaleDO::getStatus, reqVO.getStatus()) .eqIfPresent(TradeAfterSaleDO::getStatus, reqVO.getStatus())
.eqIfPresent(TradeAfterSaleDO::getType, reqVO.getType()) .eqIfPresent(TradeAfterSaleDO::getType, reqVO.getType())
.eqIfPresent(TradeAfterSaleDO::getWay, reqVO.getWay())
.likeIfPresent(TradeAfterSaleDO::getOrderNo, reqVO.getOrderNo()) .likeIfPresent(TradeAfterSaleDO::getOrderNo, reqVO.getOrderNo())
.likeIfPresent(TradeAfterSaleDO::getSpuName, reqVO.getSpuName()) .likeIfPresent(TradeAfterSaleDO::getSpuName, reqVO.getSpuName())
.betweenIfPresent(TradeAfterSaleDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(TradeAfterSaleDO::getCreateTime, reqVO.getCreateTime())

View File

@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper; import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@ -88,7 +89,6 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) { if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED); throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED);
} }
// TODO 芋艿超过一定时间不允许售后
// 申请的退款金额不能超过商品的价格 // 申请的退款金额不能超过商品的价格
if (createReqVO.getRefundPrice() > orderItem.getOrderDividePrice()) { if (createReqVO.getRefundPrice() > orderItem.getOrderDividePrice()) {
@ -100,6 +100,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
if (order == null) { if (order == null) {
throw exception(ORDER_NOT_FOUND); throw exception(ORDER_NOT_FOUND);
} }
// TODO 芋艿超过一定时间不允许售后
// 已取消无法发起售后 // 已取消无法发起售后
if (TradeOrderStatusEnum.isCanceled(order.getStatus())) { if (TradeOrderStatusEnum.isCanceled(order.getStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED); throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED);
@ -109,7 +110,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID); throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID);
} }
// 如果是退货退款的情况需要额外校验是否发货 // 如果是退货退款的情况需要额外校验是否发货
if (createReqVO.getType().equals(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType()) if (createReqVO.getWay().equals(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
&& !TradeOrderStatusEnum.haveDelivered(order.getStatus())) { && !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED); throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED);
} }
@ -117,16 +118,21 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
} }
private TradeAfterSaleDO createAfterSale(AppTradeAfterSaleCreateReqVO createReqVO, private TradeAfterSaleDO createAfterSale(AppTradeAfterSaleCreateReqVO createReqVO,
TradeOrderItemDO tradeOrderItem) { TradeOrderItemDO orderItem) {
// 创建售后单 // 创建售后单
TradeAfterSaleDO afterSale = TradeAfterSaleConvert.INSTANCE.convert(createReqVO, tradeOrderItem); TradeAfterSaleDO afterSale = TradeAfterSaleConvert.INSTANCE.convert(createReqVO, orderItem);
afterSale.setNo(RandomUtil.randomString(10)); // TODO 芋艿优化 no 生成逻辑 afterSale.setNo(RandomUtil.randomString(10)); // TODO 芋艿优化 no 生成逻辑
afterSale.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus()); afterSale.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
// 标记是售中还是售后
TradeOrderDO order = tradeOrderService.getOrder(orderItem.getUserId(), orderItem.getOrderId());
afterSale.setOrderNo(order.getNo()); // 记录 orderNo 订单流水方便后续检索
afterSale.setType(TradeOrderStatusEnum.isCompleted(order.getStatus())
? TradeAfterSaleTypeEnum.AFTER_SALE.getType() : TradeAfterSaleTypeEnum.IN_SALE.getType());
// TODO 退还积分 // TODO 退还积分
tradeAfterSaleMapper.insert(afterSale); tradeAfterSaleMapper.insert(afterSale);
// 更新交易订单项的售后状态 // 更新交易订单项的售后状态
tradeOrderService.updateOrderItemAfterSaleStatus(tradeOrderItem.getId(), tradeOrderService.updateOrderItemAfterSaleStatus(orderItem.getId(),
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), null); TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), null);
@ -145,7 +151,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
// 更新售后单的状态 // 更新售后单的状态
// 情况一退款标记为 WAIT_REFUND 状态后续等退款发起成功后在标记为 COMPLETE 状态 // 情况一退款标记为 WAIT_REFUND 状态后续等退款发起成功后在标记为 COMPLETE 状态
// 情况二退货退款需要等用户退货后才能发起退款 // 情况二退货退款需要等用户退货后才能发起退款
Integer newStatus = afterSale.getType().equals(TradeAfterSaleTypeEnum.REFUND.getType()) ? Integer newStatus = afterSale.getType().equals(TradeAfterSaleWayEnum.REFUND.getWay()) ?
TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus() : TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus(); TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus() : TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus();
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO() updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
.setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now())); .setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()));

View File

@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper; import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum; import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@ -58,7 +59,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
// 准备参数 // 准备参数
Long userId = 1024L; Long userId = 1024L;
AppTradeAfterSaleCreateReqVO createReqVO = new AppTradeAfterSaleCreateReqVO() AppTradeAfterSaleCreateReqVO createReqVO = new AppTradeAfterSaleCreateReqVO()
.setOrderItemId(1L).setRefundPrice(100).setType(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType()) .setOrderItemId(1L).setRefundPrice(100).setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
.setApplyReason("退钱").setApplyDescription("快退") .setApplyReason("退钱").setApplyDescription("快退")
.setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png")); .setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
// mock 方法交易订单项 // mock 方法交易订单项
@ -69,7 +70,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
when(tradeOrderService.getOrderItem(eq(1024L), eq(1L))) when(tradeOrderService.getOrderItem(eq(1024L), eq(1L)))
.thenReturn(orderItem); .thenReturn(orderItem);
// mock 方法交易订单 // mock 方法交易订单
TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> o.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())); TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> o.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
.setNo("202211301234"));
when(tradeOrderService.getOrder(eq(1024L), eq(111L))).thenReturn(order); when(tradeOrderService.getOrder(eq(1024L), eq(111L))).thenReturn(order);
// 调用 // 调用
@ -78,9 +80,11 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(afterSaleId); TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(afterSaleId);
assertNotNull(afterSale.getNo()); assertNotNull(afterSale.getNo());
assertEquals(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus()); assertEquals(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus());
assertEquals(afterSale.getType(), TradeAfterSaleTypeEnum.IN_SALE.getType());
assertPojoEquals(afterSale, createReqVO); assertPojoEquals(afterSale, createReqVO);
assertEquals(afterSale.getUserId(), 1024L); assertEquals(afterSale.getUserId(), 1024L);
assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime"); assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime");
assertEquals(afterSale.getOrderNo(), "202211301234");
assertNull(afterSale.getPayRefundId()); assertNull(afterSale.getPayRefundId());
assertNull(afterSale.getRefundTime()); assertNull(afterSale.getRefundTime());
assertNull(afterSale.getLogisticsId()); assertNull(afterSale.getLogisticsId());
@ -95,7 +99,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
TradeAfterSaleDO dbAfterSale = randomPojo(TradeAfterSaleDO.class, o -> { // 等会查询到 TradeAfterSaleDO dbAfterSale = randomPojo(TradeAfterSaleDO.class, o -> { // 等会查询到
o.setNo("202211190847450020500077"); o.setNo("202211190847450020500077");
o.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus()); o.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
o.setType(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType()); o.setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay());
o.setType(TradeAfterSaleTypeEnum.IN_SALE.getType());
o.setOrderNo("202211190847450020500011"); o.setOrderNo("202211190847450020500011");
o.setSpuName("芋艿"); o.setSpuName("芋艿");
o.setCreateTime(buildTime(2022, 1, 15)); o.setCreateTime(buildTime(2022, 1, 15));
@ -105,8 +110,10 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setNo("202211190847450020500066"))); tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setNo("202211190847450020500066")));
// 测试 status 不匹配 // 测试 status 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setStatus(TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus()))); tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setStatus(TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus())));
// 测试 way 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setWay(TradeAfterSaleWayEnum.REFUND.getWay())));
// 测试 type 不匹配 // 测试 type 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setType(TradeAfterSaleTypeEnum.REFUND.getType()))); tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setType(TradeAfterSaleTypeEnum.AFTER_SALE.getType())));
// 测试 orderNo 不匹配 // 测试 orderNo 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setOrderNo("202211190847450020500022"))); tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setOrderNo("202211190847450020500022")));
// 测试 spuName 不匹配 // 测试 spuName 不匹配
@ -117,7 +124,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
TradeAfterSalePageReqVO reqVO = new TradeAfterSalePageReqVO(); TradeAfterSalePageReqVO reqVO = new TradeAfterSalePageReqVO();
reqVO.setNo("20221119084745002050007"); reqVO.setNo("20221119084745002050007");
reqVO.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus()); reqVO.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
reqVO.setType(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType()); reqVO.setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay());
reqVO.setType(TradeAfterSaleTypeEnum.IN_SALE.getType());
reqVO.setOrderNo("20221119084745002050001"); reqVO.setOrderNo("20221119084745002050001");
reqVO.setSpuName(""); reqVO.setSpuName("");
reqVO.setCreateTime(new LocalDateTime[]{buildTime(2022, 1, 1), buildTime(2022, 1, 16)}); reqVO.setCreateTime(new LocalDateTime[]{buildTime(2022, 1, 1), buildTime(2022, 1, 16)});

View File

@ -75,6 +75,7 @@ CREATE TABLE IF NOT EXISTS "trade_after_sale" (
"no" varchar NOT NULL, "no" varchar NOT NULL,
"status" int NOT NULL, "status" int NOT NULL,
"type" int NOT NULL, "type" int NOT NULL,
"way" int NOT NULL,
"user_id" bigint NOT NULL, "user_id" bigint NOT NULL,
"apply_reason" varchar NOT NULL, "apply_reason" varchar NOT NULL,
"apply_description" varchar, "apply_description" varchar,

View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
// 获得交易售后
export function getAfterSale(id) {
return request({
url: '/trade/after-sale/get?id=' + id,
method: 'get'
})
}
// 获得交易售后分页
export function getAfterSalePage(query) {
return request({
url: '/trade/after-sale/page',
method: 'get',
params: query
})
}

View File

@ -216,12 +216,6 @@ export const constantRoutes = [
hidden: true, hidden: true,
meta: { title: '订单详情' }, meta: { title: '订单详情' },
component: (resolve) => require(['@/views/mall/trade/order/detail'], resolve) component: (resolve) => require(['@/views/mall/trade/order/detail'], resolve)
},
{
path: '/mall/trade/orderrefund',
name: '退款维权',
meta: { title: '退款维权' },
component: (resolve) => require(['@/views/mall/trade/orderrefund'], resolve)
} }
] ]
} }

View File

@ -3,6 +3,37 @@
* *
* 枚举类 * 枚举类
*/ */
import {beginOfDay, endOfDay} from "@/utils/dateUtils";
export const datePickerOptions = {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
const end = new Date();
picker.$emit('pick', [beginOfDay(start), endOfDay(end)]);
}
}, {
text: '最近一个月',
onClick(picker) {
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
const end = new Date();
picker.$emit('pick', [beginOfDay(start), endOfDay(end)]);
}
}, {
text: '最近三个月',
onClick(picker) {
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
const end = new Date();
picker.$emit('pick', [beginOfDay(start), endOfDay(end)]);
}
}]
}
// ========== 静态变量 ==========
/** /**
* 全局通用状态枚举 * 全局通用状态枚举

View File

@ -24,3 +24,11 @@ export function getDate(ms) {
return 0 + "秒"; return 0 + "秒";
} }
} }
export function beginOfDay(date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}
export function endOfDay(date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
}

View File

@ -60,6 +60,11 @@ export const DICT_TYPE = {
// ========== MALL - PRODUCT 模块 ========== // ========== MALL - PRODUCT 模块 ==========
PRODUCT_SPU_STATUS: 'product_spu_status', // 商品 SPU 状态 PRODUCT_SPU_STATUS: 'product_spu_status', // 商品 SPU 状态
// ========== MALL - ORDER 模块 ==========
TRADE_AFTER_SALE_STATUS: 'trade_after_sale_status', // 售后 - 状态
TRADE_AFTER_SALE_WAY: 'trade_after_sale_way', // 售后 - 方式
TRADE_AFTER_SALE_TYPE: 'trade_after_sale_type', // 售后 - 类型
// ========== MALL - PROMOTION 模块 ========== // ========== MALL - PROMOTION 模块 ==========
PROMOTION_DISCOUNT_TYPE: 'promotion_discount_type', // 优惠类型 PROMOTION_DISCOUNT_TYPE: 'promotion_discount_type', // 优惠类型
PROMOTION_PRODUCT_SCOPE: 'promotion_product_scope', // 营销的商品范围 PROMOTION_PRODUCT_SCOPE: 'promotion_product_scope', // 营销的商品范围

View File

@ -3,42 +3,6 @@
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-row :gutter="20"> <el-row :gutter="20">
<el-form :model="queryParams" label-width="68px" size="small"> <el-form :model="queryParams" label-width="68px" size="small">
<el-col :span="6" :xs="24">
<el-form-item label="商品名称">
<el-input v-model="queryParams.name" style="width: 240px"></el-input>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="订单编号">
<el-input v-model="queryParams.No" style="width: 240px"></el-input>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="退款编号">
<el-input v-model="queryParams.refundNo" style="width: 240px"></el-input>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="退款状态">
<el-select v-model="queryParams.refundStatus" clearable style="width: 240px">
<el-option v-for="dict in dicData.refundStatus" v-bind="dict" :key="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="退款方式">
<el-select v-model="queryParams.refundWay" clearable style="width: 240px">
<el-option v-for="dict in dicData.refundWay" v-bind="dict" :key="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="维权类型">
<el-select v-model="queryParams.refundType" clearable style="width: 240px">
<el-option v-for="dict in dicData.refundType" v-bind="dict" :key="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24"> <el-col :span="6" :xs="24">
<el-form-item label="下单时间"> <el-form-item label="下单时间">
<el-date-picker v-model="queryParams.date" type="daterange" range-separator="" <el-date-picker v-model="queryParams.date" type="daterange" range-separator=""
@ -60,12 +24,6 @@
<el-table :data="tableData" :show-header="false" class="table-wrapper"> <el-table :data="tableData" :show-header="false" class="table-wrapper">
<el-table-column> <el-table-column>
<template slot-scope="{ row }"> <template slot-scope="{ row }">
<div class="table-header">
退款编号{{row.tkbh}}
<el-button type="text" style="margin-left: 10px">
订单编号{{row.ddbh}}
</el-button>
</div>
<!-- 订单下的商品 --> <!-- 订单下的商品 -->
<el-table :data="row.goods" border> <el-table :data="row.goods" border>
<el-table-column label="商品信息" prop="spxx" header-align="center" width="auto" min-width="300"> <el-table-column label="商品信息" prop="spxx" header-align="center" width="auto" min-width="300">
@ -114,33 +72,7 @@ const dicData = {
{ label: '售后退款', value: 'shtk' } { label: '售后退款', value: 'shtk' }
] ]
} }
const rangePickerOptions = {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
}
export default { export default {
name: "index", name: "index",
data () { data () {
@ -164,15 +96,6 @@ export default {
tkbh: '20221026220424001', tkbh: '20221026220424001',
ddbh: '20221026220424001', ddbh: '20221026220424001',
goods: [ goods: [
{
name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
ddje: '199',
mj: '张三',
tkje: 460,
sqsj: '2022-11-19',
tkzt: '申请维权(仅退款)'
},
{ {
name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤', name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp', img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
@ -188,15 +111,6 @@ export default {
tkbh: '20221026220424001', tkbh: '20221026220424001',
ddbh: '20221026220424001', ddbh: '20221026220424001',
goods: [ goods: [
{
name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
ddje: '199',
mj: '张三',
tkje: 460,
sqsj: '2022-11-19',
tkzt: '申请维权(仅退款)'
},
{ {
name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤', name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp', img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
@ -212,15 +126,6 @@ export default {
tkbh: '20221026220424001', tkbh: '20221026220424001',
ddbh: '20221026220424001', ddbh: '20221026220424001',
goods: [ goods: [
{
name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
ddje: '199',
mj: '张三',
tkje: 460,
sqsj: '2022-11-19',
tkzt: '申请维权(仅退款)'
},
{ {
name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤', name: '颜衫短袖男polo衫夏季翻领衣服潮牌休闲上衣夏天翻领半袖男士t恤',
img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp', img: 'https://b2c-v5-yanshi.oss-cn-hangzhou.aliyuncs.com/upload/1/common/images/20220723/20220723115621165854858145027_SMALL.webp',
@ -242,48 +147,3 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
::v-deep .table-wrapper{
border-bottom: none;
&::before{
height: 0;
}
.table-header{
line-height: 36px;
}
.el-table__row{
.el-table__cell{
border-bottom: none;
.cell{
.el-table {
.el-table__row{
>.el-table__cell{
.goods-info{
display: flex;
img{
margin-right: 10px;
width: 60px;
height: 60px;
border: 1px solid #e2e2e2;
}
}
.ellipsis-2{
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
-webkit-line-clamp: 2; /* 要显示的行数 */
-webkit-box-orient: vertical;
word-break: break-all;
line-height: 22px !important;
max-height: 44px !important;
}
}
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,195 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品名称" prop="spuName">
<el-input v-model="queryParams.spuName" placeholder="请输入商品 SPU 名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="退款编号" prop="no">
<el-input v-model="queryParams.no" placeholder="请输入退款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="订单编号" prop="orderNo">
<el-input v-model="queryParams.orderNo" placeholder="请输入订单编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="售后状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择售后状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="售后方式" prop="way">
<el-select v-model="queryParams.way" placeholder="请选择售后方式" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_WAY)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="售后类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择售后类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="退款编号" align="center" prop="no" />
<el-table-column label="订单编号" align="center" prop="orderNo" />
<el-table-column label="订单编号" align="center" prop="orderNo" />
<el-table-column label="商品信息" align="center" prop="status" width="auto" min-width="300">
<!-- TODO @小红样式不太对辛苦改改 -->
<!-- <div slot-scope="{ row }" class="goods-info">-->
<!-- <img :src="row.picUrl"/>-->
<!-- <span class="ellipsis-2" :title="row.name">{{row.name}}</span>-->
<!-- </div>-->
</el-table-column>
<el-table-column label="订单金额" align="center" prop="refundPrice" />
<el-table-column label="买家" align="center" prop="userId" />
<el-table-column label="申请时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="售后状态" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="售后方式" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<!-- <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"-->
<!-- v-hasPermi="['trade:after-sale:update']">修改</el-button>-->
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getAfterSalePage } from "@/api/mall/trade/afterSale";
import { datePickerOptions } from "@/utils/constants";
export default {
name: "AfterSale",
components: {
},
data() {
return {
//
loading: true,
//
exportLoading: false,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
no: null,
status: null,
orderNo: null,
spuName: null,
createTime: [],
way: null,
type: null,
},
//
datePickerOptions: datePickerOptions
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getAfterSalePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
}
};
</script>
<style lang="scss" scoped>
::v-deep .table-wrapper {
.el-table__row{
.el-table__cell {
border-bottom: none;
.cell{
.el-table {
.el-table__row {
>.el-table__cell {
.goods-info{
display: flex;
img{
margin-right: 10px;
width: 60px;
height: 60px;
border: 1px solid #e2e2e2;
}
}
.ellipsis-2 {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
-webkit-line-clamp: 2; /* 要显示的行数 */
-webkit-box-orient: vertical;
word-break: break-all;
line-height: 22px !important;
max-height: 44px !important;
}
}
}
}
}
}
}
}
</style>