diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java index 909a54efa..238d66144 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java @@ -18,14 +18,14 @@ public class ProductSkuRespDTO { * 商品 SKU 编号,自增 */ private Long id; - /** - * SPU 名字 - */ - private String name; /** * SPU 编号 */ private Long spuId; + /** + * SPU 名字 + */ + private String spuName; /** * 规格值数组,JSON 格式 diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java index 7fafd678e..c6408d965 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java @@ -27,6 +27,7 @@ import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueServ import cn.iocoder.yudao.module.product.service.sku.ProductSkuServiceImpl; import com.google.common.collect.Lists; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.MockBean; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppTradeAfterSaleCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppTradeAfterSaleCreateReqVO.java index 32ae40614..7607a0280 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppTradeAfterSaleCreateReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/vo/AppTradeAfterSaleCreateReqVO.java @@ -18,14 +18,19 @@ 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 = "100", notes = "单位:分") @NotNull(message = "退款金额不能为空") @Min(value = 1, message = "退款金额必须大于 0") - private Integer applyPrice; + private Integer refundPrice; @ApiModelProperty(name = "申请原因", required = true, example = "1", notes = "使用数据字典枚举,对应 trade_refund_apply_reason 类型") @NotNull(message = "申请原因不能为空") - private Integer applyReason; + private String applyReason; @ApiModelProperty(name = "补充描述", example = "商品质量不好") private String applyDescription; @@ -33,9 +38,4 @@ public class AppTradeAfterSaleCreateReqVO { @ApiModelProperty(name = "补充凭证图片", example = "https://www.iocoder.cn/1.png, https://www.iocoder.cn/2.png") private List applyPicUrls; - @ApiModelProperty(name = "售后类型", required = true, example = "1", notes = "对应 TradeAfterSaleTypeEnum 枚举") - @NotNull(message = "售后类型不能为空") - @InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}") - private Integer type; - } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/aftersale/TradeAfterSaleDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/aftersale/TradeAfterSaleDO.java index 42a785f82..106c8a8d6 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/aftersale/TradeAfterSaleDO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/aftersale/TradeAfterSaleDO.java @@ -20,7 +20,7 @@ import java.util.List; * * @author 芋道源码 */ -@TableName(value = "trade_after_sale") +@TableName(value = "trade_after_sale", autoResultMap = true) @Data @EqualsAndHashCode(callSuper = true) @Accessors(chain = true) @@ -57,9 +57,10 @@ public class TradeAfterSaleDO extends BaseDO { /** * 申请原因 * - * 使用数据字典枚举,对应 trade_refund_apply_reason 类型 + * type = 退款,对应 trade_after_sale_refund_reason 类型 + * type = 退货退款,对应 trade_after_sale_refund_and_return_reason 类型 */ - private Integer applyReason; + private String applyReason; /** * 补充描述 */ @@ -72,25 +73,6 @@ public class TradeAfterSaleDO extends BaseDO { @TableField(typeHandler = JacksonTypeHandler.class) private List applyPicUrls; - // ========== 商家相关 ========== - - /** - * 审批时间 - */ - private LocalDateTime auditTime; - /** - * 审批人 - * - * 关联 AdminUserDO 的 id 编号 - */ - private Long auditUserId; - /** - * 审批备注 - * - * 注意,只有审批不通过才会填写 - */ - private String auditReason; - // ========== 交易订单相关 ========== /** * 交易订单编号 @@ -107,20 +89,60 @@ public class TradeAfterSaleDO extends BaseDO { /** * 商品 SPU 编号 * - * 关联 ProductSpuDO 的编号 + * 关联 ProductSpuDO 的 id 字段 + * 冗余 {@link TradeOrderItemDO#getSpuId()} */ private Long spuId; + /** + * 商品 SPU 名称 + * + * 关联 ProductSkuDO 的 name 字段 + * 冗余 {@link TradeOrderItemDO#getSpuName()} + */ + private String spuName; /** * 商品 SKU 编号 * * 关联 ProductSkuDO 的编号 */ - private Integer skuId; + private Long skuId; + /** + * 规格值数组,JSON 格式 + * + * 冗余 {@link TradeOrderItemDO#getProperties()} + */ + @TableField(typeHandler = TradeOrderItemDO.PropertyTypeHandler.class) + private List properties; + /** + * 商品图片 + * + * 冗余 {@link TradeOrderItemDO#getPicUrl()} + */ + private String picUrl; /** * 退货商品数量 */ private Integer count; + // ========== 审批相关 ========== + + /** + * 审批时间 + */ + private LocalDateTime auditTime; + /** + * 审批人 + * + * 关联 AdminUserDO 的 id 编号 + */ + private Long auditUserId; + /** + * 审批备注 + * + * 注意,只有审批不通过才会填写 + */ + private String auditReason; + // ========== 退款相关 ========== /** * 退款金额,单位:分。 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java index 9a385d871..25a4bb8df 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.order; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; @@ -41,13 +42,19 @@ public class TradeOrderItemDO extends BaseDO { */ private Long orderId; - // ========== 商品基本信息 ========== + // ========== 商品基本信息; 冗余较多字段,减少关联查询 ========== /** * 商品 SPU 编号 * * 关联 ProductSkuDO 的 spuId 编号 */ private Long spuId; + /** + * 商品 SPU 名称 + * + * 冗余 ProductSkuDO 的 spuName 编号 + */ + private String spuName; /** * 商品 SKU 编号 * @@ -56,15 +63,11 @@ public class TradeOrderItemDO extends BaseDO { private Long skuId; /** * 规格值数组,JSON 格式 + * + * 冗余 ProductSkuDO 的 properties 字段 */ @TableField(typeHandler = PropertyTypeHandler.class) private List properties; - /** - * 商品名称 - * - * 冗余 ProductSpuDO 的 name 字段 - */ - private String name; /** * 商品图片 */ @@ -142,7 +145,7 @@ public class TradeOrderItemDO extends BaseDO { * * 枚举 {@link TradeOrderItemAfterSaleStatusEnum} * - * @see cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO + * @see TradeAfterSaleDO */ private Integer afterSaleStatus; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java index 7db32ff2b..c295ffe36 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java @@ -84,7 +84,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService { // TODO 芋艿:超过一定时间,不允许售后 // 申请的退款金额,不能超过商品的价格 - if (createReqVO.getApplyPrice() > orderItem.getOrderDividePrice()) { + if (createReqVO.getRefundPrice() > orderItem.getOrderDividePrice()) { throw exception(AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java new file mode 100644 index 000000000..239daeec9 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.trade.service.aftersale; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; +import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO; +import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO; +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.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.order.TradeOrderItemAfterSaleStatusEnum; +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.service.order.TradeOrderService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +/** + * {@link TradeAfterSaleService} 的单元测试 + * + * @author 芋道源码 + */ +@Import(TradeAfterSaleServiceImpl.class) +public class TradeAfterSaleServiceTest extends BaseDbUnitTest { + + @Resource + private TradeAfterSaleServiceImpl tradeAfterSaleService; + + @Resource + private TradeAfterSaleMapper tradeAfterSaleMapper; + + @MockBean + private TradeOrderService tradeOrderService; + @MockBean + private PayRefundApi payRefundApi; + + @MockBean + private TradeOrderProperties tradeOrderProperties; + + @Test + public void testCreateAfterSale() { + // 准备参数 + Long userId = 1024L; + AppTradeAfterSaleCreateReqVO createReqVO = new AppTradeAfterSaleCreateReqVO() + .setOrderItemId(1L).setRefundPrice(100).setType(TradeAfterSaleTypeEnum.RETURN_AND_REFUND.getType()) + .setApplyReason("退钱").setApplyDescription("快退") + .setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png")); + // mock 方法(交易订单项) + TradeOrderItemDO orderItem = randomPojo(TradeOrderItemDO.class, o -> { + o.setOrderId(111L).setUserId(userId).setOrderDividePrice(200); + o.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); + }); + when(tradeOrderService.getOrderItem(eq(1024L), eq(1L))) + .thenReturn(orderItem); + // mock 方法(交易订单) + TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> o.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())); + when(tradeOrderService.getOrder(eq(1024L), eq(111L))).thenReturn(order); + + // 调用 + Long afterSaleId = tradeAfterSaleService.createAfterSale(userId, createReqVO); + // 断言 + TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(afterSaleId); + assertNotNull(afterSale.getNo()); + assertEquals(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus()); + assertPojoEquals(afterSale, createReqVO); + assertEquals(afterSale.getUserId(), 1024L); + assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime"); + assertNull(afterSale.getPayRefundId()); + assertNull(afterSale.getRefundTime()); + assertNull(afterSale.getLogisticsId()); + assertNull(afterSale.getLogisticsNo()); + assertNull(afterSale.getDeliveryTime()); + assertNull(afterSale.getReceiveReason()); + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java index e74a7b928..a5e472e9a 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java @@ -51,7 +51,7 @@ import static org.mockito.Mockito.when; * @since 2022-09-07 */ @Import({TradeOrderServiceImpl.class, TradeOrderConfig.class}) -class TradeOrderServiceTest extends BaseDbUnitTest { +public class TradeOrderServiceTest extends BaseDbUnitTest { @Resource private TradeOrderServiceImpl tradeOrderService; @@ -196,7 +196,7 @@ class TradeOrderServiceTest extends BaseDbUnitTest { assertEquals(tradeOrderItemDO01.getProperties().size(), 1); assertEquals(tradeOrderItemDO01.getProperties().get(0).getPropertyId(), 111L); assertEquals(tradeOrderItemDO01.getProperties().get(0).getValueId(), 222L); - assertEquals(tradeOrderItemDO01.getName(), sku01.getName()); + assertEquals(tradeOrderItemDO01.getSpuName(), sku01.getSpuName()); assertEquals(tradeOrderItemDO01.getPicUrl(), sku01.getPicUrl()); assertEquals(tradeOrderItemDO01.getCount(), 3); assertEquals(tradeOrderItemDO01.getOriginalPrice(), 150); @@ -216,7 +216,7 @@ class TradeOrderServiceTest extends BaseDbUnitTest { assertEquals(tradeOrderItemDO02.getProperties().size(), 1); assertEquals(tradeOrderItemDO02.getProperties().get(0).getPropertyId(), 333L); assertEquals(tradeOrderItemDO02.getProperties().get(0).getValueId(), 444L); - assertEquals(tradeOrderItemDO02.getName(), sku02.getName()); + assertEquals(tradeOrderItemDO02.getSpuName(), sku02.getSpuName()); assertEquals(tradeOrderItemDO02.getPicUrl(), sku02.getPicUrl()); assertEquals(tradeOrderItemDO02.getCount(), 4); assertEquals(tradeOrderItemDO02.getOriginalPrice(), 80); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/clean.sql b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/clean.sql index cd16db8c6..c4bb5a3c7 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/clean.sql +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/clean.sql @@ -1,2 +1,3 @@ DELETE FROM trade_order; -DELETE FROM trade_order_item; \ No newline at end of file +DELETE FROM trade_order_item; +DELETE FROM trade_after_sale; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql index 7b0b99b56..8e073c4ea 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql @@ -50,9 +50,9 @@ CREATE TABLE IF NOT EXISTS "trade_order_item" ( "user_id" bigint NOT NULL, "order_id" bigint NOT NULL, "spu_id" bigint NOT NULL, + "spu_name" varchar NOT NULL, "sku_id" bigint NOT NULL, "properties" varchar, - "name" varchar NOT NULL, "pic_url" varchar, "count" int NOT NULL, "original_price" int NOT NULL, @@ -69,3 +69,39 @@ CREATE TABLE IF NOT EXISTS "trade_order_item" ( "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") ) COMMENT '交易订单明细表'; + +CREATE TABLE IF NOT EXISTS "trade_after_sale" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar NOT NULL, + "status" int NOT NULL, + "type" int NOT NULL, + "user_id" bigint NOT NULL, + "apply_reason" varchar NOT NULL, + "apply_description" varchar, + "apply_pic_urls" varchar, + "order_id" bigint NOT NULL, + "order_item_id" bigint NOT NULL, + "spu_id" bigint NOT NULL, + "spu_name" varchar NOT NULL, + "sku_id" bigint NOT NULL, + "properties" varchar, + "pic_url" varchar, + "count" int NOT NULL, + "audit_time" varchar, + "audit_user_id" bigint, + "audit_reason" varchar, + "refund_price" int NOT NULL, + "pay_refund_id" bigint, + "refund_time" varchar, + "logistics_id" bigint, + "logistics_no" varchar, + "delivery_time" varchar, + "receive_time" varchar, + "receive_reason" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '交易售后表';