commit
952e6aa4a8
@ -1,43 +1,43 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
|
||||
import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品 SKU API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @since 2022-08-26
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-06
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class ProductSkuApiImpl implements ProductSkuApi {
|
||||
|
||||
@Resource
|
||||
private ProductSkuService productSkuService;
|
||||
private ProductSkuMapper productSkuMapper;
|
||||
|
||||
@Override
|
||||
public ProductSkuRespDTO getSku(Long id) {
|
||||
ProductSkuDO skuDO = productSkuService.getSku(id);
|
||||
return ProductSkuConvert.INSTANCE.convert02(skuDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProductSkuRespDTO> getSkuList(Collection<Long> ids) {
|
||||
List<ProductSkuDO> list = productSkuService.getSkuList(ids);
|
||||
return ProductSkuConvert.INSTANCE.convertList02(list);
|
||||
public List<SkuInfoRespDTO> getSkusByIds(Collection<Long> skuIds) {
|
||||
if (CollectionUtils.isAnyEmpty(skuIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ProductSkuDO> productSkuDOList = productSkuMapper.selectBatchIds(skuIds);
|
||||
return ProductSkuConvert.INSTANCE.convertList03(productSkuDOList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO) {
|
||||
|
||||
productSkuMapper.decrementStockBatch(batchReqDTO.getItems());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,23 +1,35 @@
|
||||
package cn.iocoder.yudao.module.product.api.spu;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品 SPU API 实现类
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-08-26
|
||||
* @since 2022-09-06
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class ProductSpuApiImpl implements ProductSpuApi {
|
||||
|
||||
@Resource
|
||||
private ProductSpuMapper productSpuMapper;
|
||||
|
||||
@Override
|
||||
public List<SpuInfoRespDTO> getSpuList(Collection<Long> spuIds) {
|
||||
return null;
|
||||
if (CollectionUtils.isAnyEmpty(spuIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ProductSpuDO> productSpuDOList = productSpuMapper.selectBatchIds(spuIds);
|
||||
return ProductSpuConvert.INSTANCE.convertList2(productSpuDOList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,17 +1,33 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.pay;
|
||||
|
||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.Named;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author LeeYan9
|
||||
* @since 2022-08-26
|
||||
*/
|
||||
@Mapper
|
||||
public interface PayOrderConvert {
|
||||
|
||||
PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "payPrice", target = "amount"),
|
||||
@Mapping(target = "expireTime", source = "cancelTime" , qualifiedByName = "convertCreateTimeToPayExpireTime")
|
||||
})
|
||||
PayOrderInfoCreateReqDTO convert(TradeOrderDO tradeOrderDO);
|
||||
|
||||
PayOrderDataCreateReqDTO convert(TradeOrderDO tradeOrderDO);
|
||||
@Named("convertCreateTimeToPayExpireTime")
|
||||
default Date convertCreateTimeToPayExpireTime(Date cancelTime) {
|
||||
return DateUtil.offsetMinute(new Date(), 30);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package cn.iocoder.yudao.module.trade.framework.order.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-15
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TradeOrderProperties.class)
|
||||
public class TradeOrderConfig {
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.trade.framework.order.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-15
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "yudao.trade.order")
|
||||
@Data
|
||||
@Validated
|
||||
public class TradeOrderProperties {
|
||||
|
||||
/**
|
||||
* 商户订单编号
|
||||
*/
|
||||
@NotNull(message = "商户订单编号不能为空")
|
||||
private String merchantOrderId;
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
*/
|
||||
@NotNull(message = "应用编号不能为空")
|
||||
private Long appId;
|
||||
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.market.api.price.PriceApi;
|
||||
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
|
||||
import com.google.common.collect.Lists;
|
||||
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 java.util.Collections;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomInteger;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-07
|
||||
*/
|
||||
@Import({TradeOrderServiceImpl.class, TradeOrderConfig.class})
|
||||
class TradeOrderServiceTest extends BaseDbUnitTest {
|
||||
|
||||
|
||||
@Resource
|
||||
TradeOrderService tradeOrderService;
|
||||
@Resource
|
||||
TradeOrderMapper tradeOrderMapper;
|
||||
@Resource
|
||||
TradeOrderItemMapper tradeOrderItemMapper;
|
||||
@MockBean
|
||||
ProductSpuApi productSpuApi;
|
||||
@MockBean
|
||||
ProductSkuApi productSkuApi;
|
||||
@MockBean
|
||||
PriceApi priceApi;
|
||||
@MockBean
|
||||
private PayOrderApi payOrderApi;
|
||||
|
||||
@Test
|
||||
void testCreateTradeOrder_success() {
|
||||
// mock 商品SPU数据
|
||||
SpuInfoRespDTO spuInfoRespDTO = randomPojo(SpuInfoRespDTO.class, spuInfo -> {
|
||||
spuInfo.setId(1L);
|
||||
spuInfo.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
when(productSpuApi.getSpuList(Collections.singleton(1L))).thenReturn(Lists.newArrayList(spuInfoRespDTO));
|
||||
// mock 商品SkU数据
|
||||
ProductSkuRespDTO skuInfoRespDTO = randomPojo(ProductSkuRespDTO.class, skuInfo -> {
|
||||
skuInfo.setId(1L);
|
||||
skuInfo.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
skuInfo.setStock(randomInteger());
|
||||
skuInfo.setSpuId(1L);
|
||||
});
|
||||
when(productSkuApi.getSkuList(Collections.singleton(1L))).thenReturn(Lists.newArrayList(skuInfoRespDTO));
|
||||
// mock 价格信息
|
||||
PriceCalculateRespDTO calculateRespDTO = randomPojo(PriceCalculateRespDTO.class, priceCalculateRespDTO -> {
|
||||
PriceCalculateRespDTO.OrderItem item = priceCalculateRespDTO.getOrder().getItems().get(0);
|
||||
item.setSkuId(1L);
|
||||
item.setCount(2);
|
||||
priceCalculateRespDTO.getOrder().setItems(Collections.singletonList(item));
|
||||
});
|
||||
when(priceApi.calculatePrice(any())).thenReturn(calculateRespDTO);
|
||||
//mock 支付订单信息
|
||||
when(payOrderApi.createPayOrder(any())).thenReturn(1L);
|
||||
|
||||
// 准备请求数据
|
||||
AppTradeOrderCreateReqVO tradeOrderCreateReqVO = randomPojo(AppTradeOrderCreateReqVO.class, reqVO -> {
|
||||
AppTradeOrderCreateReqVO.Item item = randomPojo(AppTradeOrderCreateReqVO.Item.class, o -> {
|
||||
o.setSkuId(1L);
|
||||
o.setCount(2);
|
||||
});
|
||||
reqVO.setItems(Collections.singletonList(item));
|
||||
});
|
||||
// 创建交易订单,支付订单记录
|
||||
Long payOrderId = tradeOrderService.createTradeOrder(1L, "127.0.0.1", tradeOrderCreateReqVO);
|
||||
//断言交易订单
|
||||
TradeOrderDO tradeOrderDO = tradeOrderMapper.selectOne(TradeOrderDO::getUserId, 1L);
|
||||
assertNotNull(tradeOrderDO);
|
||||
//价格&用户
|
||||
assertEquals(calculateRespDTO.getOrder().getPayPrice(), tradeOrderDO.getPayPrice());
|
||||
assertEquals(1L, tradeOrderDO.getUserId());
|
||||
//断言交易订单项
|
||||
TradeOrderItemDO tradeOrderItemDO = tradeOrderItemMapper.selectOne(TradeOrderItemDO::getOrderId, tradeOrderDO.getId());
|
||||
assertNotNull(tradeOrderDO);
|
||||
//商品&用户
|
||||
assertEquals(skuInfoRespDTO.getId(), tradeOrderItemDO.getSkuId());
|
||||
assertEquals(1L, tradeOrderItemDO.getUserId());
|
||||
//价格
|
||||
assertEquals(calculateRespDTO.getOrder().getItems().get(0).getPresentPrice(), tradeOrderItemDO.getPresentPrice());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
</configuration>
|
||||
@ -0,0 +1,2 @@
|
||||
DELETE FROM trade_order;
|
||||
DELETE FROM trade_order_item;
|
||||
@ -0,0 +1,78 @@
|
||||
/**todo cancelType 设置默认值 0?*/
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `trade_order`
|
||||
(
|
||||
`id` number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`sn` varchar(32) NOT NULL,
|
||||
`type` int NOT NULL,
|
||||
`terminal` int NOT NULL,
|
||||
`user_id` bigint unsigned NOT NULL,
|
||||
`user_ip` varchar(30) NOT NULL,
|
||||
`user_remark` varchar(200),
|
||||
`status` int NOT NULL,
|
||||
`product_count` int NOT NULL,
|
||||
`cancel_type` int DEFAULT NULL,
|
||||
`remark` varchar(200),
|
||||
`payed` bit(1) NOT NULL DEFAULT FALSE,
|
||||
`pay_time` datetime DEFAULT NULL,
|
||||
`finish_time` datetime DEFAULT NULL,
|
||||
`cancel_time` datetime DEFAULT NULL,
|
||||
`sku_original_price` int NOT NULL DEFAULT '0',
|
||||
`sku_promotion_price` int NOT NULL DEFAULT '0',
|
||||
`order_promotion_price` int NOT NULL DEFAULT '0',
|
||||
`delivery_price` int NOT NULL DEFAULT '0',
|
||||
`pay_price` int DEFAULT '0',
|
||||
`pay_order_id` int DEFAULT NULL,
|
||||
`pay_channel` int DEFAULT NULL,
|
||||
`delivery_type` int NOT NULL DEFAULT '1',
|
||||
`actual_delivery_type` int NOT NULL DEFAULT '1',
|
||||
`delivery_template_id` int DEFAULT NULL,
|
||||
`express_no` int DEFAULT NULL,
|
||||
`delivery_status` bit(1) NOT NULL DEFAULT FALSE,
|
||||
`delivery_time` datetime DEFAULT NULL,
|
||||
`receive_time` datetime DEFAULT NULL,
|
||||
`receiver_name` varchar(20) DEFAULT NULL,
|
||||
`receiver_mobile` varchar(20) DEFAULT NULL,
|
||||
`receiver_area_id` int DEFAULT NULL,
|
||||
`receiver_post_code` int DEFAULT NULL,
|
||||
`receiver_detail_address` varchar(255) DEFAULT NULL,
|
||||
`refund_status` int NOT NULL DEFAULT '0',
|
||||
`refund_price` int NOT NULL DEFAULT '0',
|
||||
`coupon_id` bigint unsigned DEFAULT NULL,
|
||||
`creator` varchar(64) DEFAULT '',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` varchar(64) DEFAULT '',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted` bit(1) NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `trade_order_item`
|
||||
(
|
||||
`id` number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`user_id` bigint unsigned NOT NULL,
|
||||
`order_id` bigint unsigned NOT NULL,
|
||||
`spu_id` bigint unsigned NOT NULL,
|
||||
`sku_id` bigint unsigned NOT NULL,
|
||||
`properties` json DEFAULT NULL,
|
||||
`name` varchar(128) DEFAULT NULL,
|
||||
`pic_url` varchar(200) DEFAULT NULL,
|
||||
`count` int NOT NULL,
|
||||
`commented` bit(1) DEFAULT NULL,
|
||||
`original_price` int NOT NULL DEFAULT '0',
|
||||
`total_original_price` int NOT NULL DEFAULT '0',
|
||||
`total_promotion_price` int NOT NULL DEFAULT '0',
|
||||
`present_price` int NOT NULL DEFAULT '0',
|
||||
`total_present_price` int NOT NULL DEFAULT '0',
|
||||
`total_pay_price` int NOT NULL DEFAULT '0',
|
||||
`refund_status` int NOT NULL DEFAULT '0',
|
||||
`refund_total` int NOT NULL DEFAULT '0',
|
||||
`creator` varchar(64) DEFAULT '',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` varchar(64) DEFAULT '',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted` bit(1) DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.pay.api.order;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-06
|
||||
*/
|
||||
@Service
|
||||
public class PayOrderApiImpl implements PayOrderApi {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createPayOrder(PayOrderInfoCreateReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue