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
public enum TradeAfterSaleStatusEnum implements IntArrayValuable {
APPLY(10,"申请中"),
SELLER_AGREE(20, "卖家通过"), // 卖家通过售后
BUYER_DELIVERY(30,"待卖家收货"), // 买家已退货,等待卖家收货
WAIT_REFUND(40, "等待平台退款"), // 卖家已收货,等待平台退款
COMPLETE(50, "完成"), // 完成退款
APPLY(10,"申请中"), // 【申请售后】
SELLER_AGREE(20, "卖家通过"), // 卖家通过售后;【商品待退货】
BUYER_DELIVERY(30,"待卖家收货"), // 买家已退货,等待卖家收货;【商家待收货】
WAIT_REFUND(40, "等待平台退款"), // 卖家已收货,等待平台退款;等待退款【等待退款】
COMPLETE(50, "完成"), // 完成退款【退款成功】
BUYER_CANCEL(61, "买家取消售后"),
SELLER_DISAGREE(62,"卖家拒绝"), // 卖家拒绝售后
SELLER_REFUSE(63,"卖家拒绝收货"), // 卖家拒绝收货,终止售后
BUYER_CANCEL(61, "买家取消售后"), // 【买家取消】
SELLER_DISAGREE(62,"卖家拒绝"), // 卖家拒绝售后;商家拒绝【商家拒绝】
SELLER_REFUSE(63,"卖家拒绝收货"), // 卖家拒绝收货,终止售后;【商家拒收货】
;
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
@Getter
public enum TradeAfterSaleTypeEnum implements IntArrayValuable {
REFUND(10, "退款"),
RETURN_AND_REFUND(20, "退货退款");
IN_SALE(10, "售中退款"), // 交易完成前买家申请退款
AFTER_SALE(20, "售后退款"); // 交易完成后买家申请退款
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleTypeEnum::getType).toArray();
/**
* 状态值
* 类型
*/
private final Integer type;
/**
* 状态
* 类型
*/
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());
}
/**
* 判断指定状态,是否正处于【已完成】状态
*
* @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 = "售后流水号不能为空")
private String no;
@ApiModelProperty(value = "售后状态", required = true, example = "2", notes = "参见 TradeAfterSaleStatusEnum 枚举")
@ApiModelProperty(value = "售后状态", required = true, example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
@NotNull(message = "售后状态不能为空")
private Integer status;
@ApiModelProperty(value = "售后类型", required = true, example = "2", notes = "参见 TradeAfterSaleTypeEnum 枚举")
@ApiModelProperty(value = "售后类型", required = true, example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
@NotNull(message = "售后类型不能为空")
private Integer type;
@ApiModelProperty(value = "售后方式", required = true, example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
@NotNull(message = "售后方式不能为空")
private Integer way;
@ApiModelProperty(value = "用户编号", required = true, example = "30337")
@NotNull(message = "用户编号不能为空")
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.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
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.ApiModelProperty;
import lombok.Data;
@ -24,14 +25,18 @@ public class TradeAfterSalePageReqVO extends PageParam {
@ApiModelProperty(value = "售后流水号", example = "202211190847450020500077", notes = "模糊匹配")
private String no;
@ApiModelProperty(value = "售后状态", example = "2", notes = "参见 TradeAfterSaleStatusEnum 枚举")
@ApiModelProperty(value = "售后状态", example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
@InEnum(value = TradeAfterSaleStatusEnum.class, message = "售后状态必须是 {value}")
private Integer status;
@ApiModelProperty(value = "售后类型", example = "2", notes = "参见 TradeAfterSaleTypeEnum 枚举")
@ApiModelProperty(value = "售后类型", example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
@InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}")
private Integer type;
@ApiModelProperty(value = "售后方式", example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
@InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
private Integer way;
@ApiModelProperty(value = "订单编号", example = "18078", notes = "模糊匹配")
private String orderNo;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
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.ApiModelProperty;
import lombok.Data;
@ -18,10 +18,10 @@ public class AppTradeAfterSaleCreateReqVO {
@NotNull(message = "订单项编号不能为空")
private Long orderItemId;
@ApiModelProperty(name = "售后类型", required = true, example = "1", notes = "对应 TradeAfterSaleTypeEnum 枚举")
@NotNull(message = "售后类型不能为空")
@InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}")
private Integer type;
@ApiModelProperty(name = "售后方式", required = true, example = "1", notes = "对应 TradeAfterSaleWayEnum 枚举")
@NotNull(message = "售后方式不能为空")
@InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
private Integer way;
@ApiModelProperty(name = "退款金额", required = true, example = "100", notes = "单位:分")
@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.enums.aftersale.TradeAfterSaleStatusEnum;
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.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
@ -42,6 +43,12 @@ public class TradeAfterSaleDO extends BaseDO {
* 枚举 {@link TradeAfterSaleStatusEnum}
*/
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())
.eqIfPresent(TradeAfterSaleDO::getStatus, reqVO.getStatus())
.eqIfPresent(TradeAfterSaleDO::getType, reqVO.getType())
.eqIfPresent(TradeAfterSaleDO::getWay, reqVO.getWay())
.likeIfPresent(TradeAfterSaleDO::getOrderNo, reqVO.getOrderNo())
.likeIfPresent(TradeAfterSaleDO::getSpuName, reqVO.getSpuName())
.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.enums.aftersale.TradeAfterSaleStatusEnum;
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.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@ -88,7 +89,6 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED);
}
// TODO 芋艿:超过一定时间,不允许售后
// 申请的退款金额,不能超过商品的价格
if (createReqVO.getRefundPrice() > orderItem.getOrderDividePrice()) {
@ -100,6 +100,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// TODO 芋艿:超过一定时间,不允许售后
// 已取消,无法发起售后
if (TradeOrderStatusEnum.isCanceled(order.getStatus())) {
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);
}
// 如果是【退货退款】的情况,需要额外校验是否发货
if (createReqVO.getType().equals(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType())
if (createReqVO.getWay().equals(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
&& !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED);
}
@ -117,16 +118,21 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
}
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.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 退还积分
tradeAfterSaleMapper.insert(afterSale);
// 更新交易订单项的售后状态
tradeOrderService.updateOrderItemAfterSaleStatus(tradeOrderItem.getId(),
tradeOrderService.updateOrderItemAfterSaleStatus(orderItem.getId(),
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), null);
@ -145,7 +151,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
// 更新售后单的状态
// 情况一:退款:标记为 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();
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
.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.enums.aftersale.TradeAfterSaleStatusEnum;
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.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@ -58,7 +59,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
// 准备参数
Long userId = 1024L;
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("快退")
.setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
// mock 方法(交易订单项)
@ -69,7 +70,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
when(tradeOrderService.getOrderItem(eq(1024L), eq(1L)))
.thenReturn(orderItem);
// 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);
// 调用
@ -78,9 +80,11 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(afterSaleId);
assertNotNull(afterSale.getNo());
assertEquals(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus());
assertEquals(afterSale.getType(), TradeAfterSaleTypeEnum.IN_SALE.getType());
assertPojoEquals(afterSale, createReqVO);
assertEquals(afterSale.getUserId(), 1024L);
assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime");
assertEquals(afterSale.getOrderNo(), "202211301234");
assertNull(afterSale.getPayRefundId());
assertNull(afterSale.getRefundTime());
assertNull(afterSale.getLogisticsId());
@ -95,7 +99,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
TradeAfterSaleDO dbAfterSale = randomPojo(TradeAfterSaleDO.class, o -> { // 等会查询到
o.setNo("202211190847450020500077");
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.setSpuName("芋艿");
o.setCreateTime(buildTime(2022, 1, 15));
@ -105,8 +110,10 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setNo("202211190847450020500066")));
// 测试 status 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setStatus(TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus())));
// 测试 way 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setWay(TradeAfterSaleWayEnum.REFUND.getWay())));
// 测试 type 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setType(TradeAfterSaleTypeEnum.REFUND.getType())));
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setType(TradeAfterSaleTypeEnum.AFTER_SALE.getType())));
// 测试 orderNo 不匹配
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setOrderNo("202211190847450020500022")));
// 测试 spuName 不匹配
@ -117,7 +124,8 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
TradeAfterSalePageReqVO reqVO = new TradeAfterSalePageReqVO();
reqVO.setNo("20221119084745002050007");
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.setSpuName("");
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,
"status" int NOT NULL,
"type" int NOT NULL,
"way" int NOT NULL,
"user_id" bigint NOT NULL,
"apply_reason" varchar NOT NULL,
"apply_description" varchar,