commit
952e6aa4a8
|
@ -1,4 +1,5 @@
|
||||||
/**todo cancelType 设置默认值 0?*/
|
/**todo cancelType 设置默认值 0?*/
|
||||||
|
DROP TABLE IF EXISTS `trade_order`;
|
||||||
CREATE TABLE `trade_order`
|
CREATE TABLE `trade_order`
|
||||||
(
|
(
|
||||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
||||||
|
@ -10,9 +11,10 @@ CREATE TABLE `trade_order`
|
||||||
`user_remark` varchar(200) DEFAULT NULL COMMENT '用户备注',
|
`user_remark` varchar(200) DEFAULT NULL COMMENT '用户备注',
|
||||||
`status` int NOT NULL DEFAULT '0' COMMENT '订单状态:[0:待付款 1:待发货 2:待收货 3:已完成 4:已关闭]',
|
`status` int NOT NULL DEFAULT '0' COMMENT '订单状态:[0:待付款 1:待发货 2:待收货 3:已完成 4:已关闭]',
|
||||||
`product_count` int NOT NULL COMMENT '购买的商品数量',
|
`product_count` int NOT NULL COMMENT '购买的商品数量',
|
||||||
`cancel_type` int NOT NULL COMMENT '取消类型:[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]',
|
`cancel_type` int DEFAULT NULL COMMENT '取消类型:[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]',
|
||||||
`remark` varchar(200) DEFAULT NULL COMMENT '商家备注',
|
`remark` varchar(200) DEFAULT NULL COMMENT '商家备注',
|
||||||
`payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已支付:[0:未支付 1:已经支付过]',
|
`payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已支付:[0:未支付 1:已经支付过]',
|
||||||
|
`pay_time` datetime DEFAULT NULL COMMENT '订单支付时间',
|
||||||
`finish_time` datetime DEFAULT NULL COMMENT '订单完成时间',
|
`finish_time` datetime DEFAULT NULL COMMENT '订单完成时间',
|
||||||
`cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间',
|
`cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间',
|
||||||
`sku_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分',
|
`sku_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分',
|
||||||
|
@ -20,11 +22,11 @@ CREATE TABLE `trade_order`
|
||||||
`order_promotion_price` int NOT NULL DEFAULT '0' COMMENT '订单优惠(总),单位:分',
|
`order_promotion_price` int NOT NULL DEFAULT '0' COMMENT '订单优惠(总),单位:分',
|
||||||
`delivery_price` int NOT NULL DEFAULT '0' COMMENT '运费金额,单位:分',
|
`delivery_price` int NOT NULL DEFAULT '0' COMMENT '运费金额,单位:分',
|
||||||
`pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分',
|
`pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分',
|
||||||
`pay_order_id` int NOT NULL COMMENT '支付订单编号',
|
`pay_order_id` int DEFAULT NULL COMMENT '支付订单编号',
|
||||||
`pay_channel` int NOT NULL COMMENT '支付成功的支付渠道',
|
`pay_channel` int DEFAULT NULL COMMENT '支付成功的支付渠道',
|
||||||
`delivery_type` int NOT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]',
|
`delivery_type` int DEFAULT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]',
|
||||||
`actual_delivery_type` int NOT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]',
|
`actual_delivery_type` int DEFAULT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]',
|
||||||
`delivery_templateid` int DEFAULT NULL COMMENT '配置模板的编号',
|
`delivery_template_id` int DEFAULT NULL COMMENT '配置模板的编号',
|
||||||
`express_no` int DEFAULT NULL COMMENT '物流公司单号',
|
`express_no` int DEFAULT NULL COMMENT '物流公司单号',
|
||||||
`delivery_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '发货状态[0:未发货 1:已发货]',
|
`delivery_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '发货状态[0:未发货 1:已发货]',
|
||||||
`delivery_time` datetime DEFAULT NULL COMMENT '发货时间',
|
`delivery_time` datetime DEFAULT NULL COMMENT '发货时间',
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
package cn.iocoder.yudao.module.product.api.sku;
|
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.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.convert.sku.ProductSkuConvert;
|
||||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
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.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品 SKU API 实现类
|
* @author LeeYan9
|
||||||
*
|
* @since 2022-09-06
|
||||||
* @author 芋道源码
|
|
||||||
* @since 2022-08-26
|
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Validated
|
||||||
public class ProductSkuApiImpl implements ProductSkuApi {
|
public class ProductSkuApiImpl implements ProductSkuApi {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ProductSkuService productSkuService;
|
private ProductSkuMapper productSkuMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProductSkuRespDTO getSku(Long id) {
|
public List<SkuInfoRespDTO> getSkusByIds(Collection<Long> skuIds) {
|
||||||
ProductSkuDO skuDO = productSkuService.getSku(id);
|
if (CollectionUtils.isAnyEmpty(skuIds)) {
|
||||||
return ProductSkuConvert.INSTANCE.convert02(skuDO);
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
List<ProductSkuDO> productSkuDOList = productSkuMapper.selectBatchIds(skuIds);
|
||||||
@Override
|
return ProductSkuConvert.INSTANCE.convertList03(productSkuDOList);
|
||||||
public List<ProductSkuRespDTO> getSkuList(Collection<Long> ids) {
|
|
||||||
List<ProductSkuDO> list = productSkuService.getSkuList(ids);
|
|
||||||
return ProductSkuConvert.INSTANCE.convertList02(list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO) {
|
public void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO) {
|
||||||
|
productSkuMapper.decrementStockBatch(batchReqDTO.getItems());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,35 @@
|
||||||
package cn.iocoder.yudao.module.product.api.spu;
|
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.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.stereotype.Service;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品 SPU API 实现类
|
|
||||||
*
|
|
||||||
* @author LeeYan9
|
* @author LeeYan9
|
||||||
* @since 2022-08-26
|
* @since 2022-09-06
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Validated
|
||||||
public class ProductSpuApiImpl implements ProductSpuApi {
|
public class ProductSpuApiImpl implements ProductSpuApi {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ProductSpuMapper productSpuMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SpuInfoRespDTO> getSpuList(Collection<Long> spuIds) {
|
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,5 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.product.convert.sku;
|
package cn.iocoder.yudao.module.product.convert.sku;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
|
||||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||||
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
|
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
|
||||||
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
|
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
|
||||||
|
@ -34,4 +36,7 @@ public interface ProductSkuConvert {
|
||||||
|
|
||||||
List<ProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> list);
|
List<ProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> list);
|
||||||
|
|
||||||
|
List<SkuInfoRespDTO> convertList03(List<ProductSkuDO> list);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.product.convert.spu;
|
package cn.iocoder.yudao.module.product.convert.spu;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO;
|
||||||
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
|
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
|
||||||
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
|
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO;
|
||||||
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
|
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO;
|
||||||
|
@ -34,4 +35,7 @@ public interface ProductSpuConvert {
|
||||||
|
|
||||||
AppSpuPageRespVO convertAppResp(ProductSpuDO list);
|
AppSpuPageRespVO convertAppResp(ProductSpuDO list);
|
||||||
|
|
||||||
|
List<SpuInfoRespDTO> convertList2(List<ProductSpuDO> list);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.product.dal.mysql.sku;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
|
import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuPageReqVO;
|
||||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -31,4 +34,16 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
|
||||||
delete(lambdaQueryWrapperX);
|
delete(lambdaQueryWrapperX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void decrementStockBatch(List<SkuDecrementStockBatchReqDTO.Item> items) {
|
||||||
|
for (SkuDecrementStockBatchReqDTO.Item item : items) {
|
||||||
|
// 扣减库存 cas 逻辑
|
||||||
|
LambdaUpdateWrapper<ProductSkuDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()
|
||||||
|
.setSql(" stock = stock-" + item.getCount())
|
||||||
|
.eq(ProductSkuDO::getSpuId, item.getProductId())
|
||||||
|
.eq(ProductSkuDO::getId, item.getSkuId())
|
||||||
|
.ge(ProductSkuDO::getStock, item.getCount());
|
||||||
|
// 执行
|
||||||
|
this.update(null, lambdaUpdateWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,16 @@
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
<artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- DB 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- DB 相关 -->
|
<!-- DB 相关 -->
|
||||||
|
|
|
@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreate
|
||||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Mappings;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,6 +17,12 @@ public interface TradeOrderConvert {
|
||||||
|
|
||||||
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
|
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
|
||||||
|
|
||||||
@Mapping(source = "order.couponId", target = "couponId")
|
|
||||||
TradeOrderDO convert(AppTradeOrderCreateReqVO createReqVO, PriceCalculateRespDTO.Order order);
|
@Mappings({
|
||||||
|
@Mapping(source = "order.couponId", target = "couponId"),
|
||||||
|
@Mapping(target = "remark", ignore = true),
|
||||||
|
@Mapping(source = "createVO.remark", target = "userRemark"),
|
||||||
|
@Mapping(source = "createVO.addressId", target = "receiverAreaId")
|
||||||
|
})
|
||||||
|
TradeOrderDO convert(AppTradeOrderCreateReqVO createVO, PriceCalculateRespDTO.Order order);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package cn.iocoder.yudao.module.trade.convert.order;
|
package cn.iocoder.yudao.module.trade.convert.order;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
|
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
|
||||||
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 org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
|
||||||
import org.mapstruct.Mappings;
|
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -20,17 +17,8 @@ public interface TradeOrderItemConvert {
|
||||||
TradeOrderItemConvert INSTANCE = Mappers.getMapper(TradeOrderItemConvert.class);
|
TradeOrderItemConvert INSTANCE = Mappers.getMapper(TradeOrderItemConvert.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param tradeOrder 交易订单
|
|
||||||
* @param items sku列表价格
|
* @param items sku列表价格
|
||||||
* @return 订单项
|
* @return 订单项
|
||||||
*/
|
*/
|
||||||
@Mappings({
|
List<TradeOrderItemDO> convertList(List<PriceCalculateRespDTO.OrderItem> items);
|
||||||
@Mapping(source = "tradeOrder.userId", target = "userId"),
|
|
||||||
@Mapping(source = "tradeOrder.orderId", target = "orderId")
|
|
||||||
})
|
|
||||||
default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrder, List<PriceCalculateRespDTO.OrderItem> items) {
|
|
||||||
// TODO @Com: Mapstruct 生成会报错
|
|
||||||
throw new UnsupportedOperationException("无法实现");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,33 @@
|
||||||
package cn.iocoder.yudao.module.trade.convert.pay;
|
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 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 org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author LeeYan9
|
* @author LeeYan9
|
||||||
* @since 2022-08-26
|
* @since 2022-08-26
|
||||||
*/
|
*/
|
||||||
|
@Mapper
|
||||||
public interface PayOrderConvert {
|
public interface PayOrderConvert {
|
||||||
|
|
||||||
PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class);
|
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;
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
package cn.iocoder.yudao.module.trade.service.order;
|
package cn.iocoder.yudao.module.trade.service.order;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.text.StrBuilder;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||||
import cn.iocoder.yudao.module.market.api.price.PriceApi;
|
import cn.iocoder.yudao.module.market.api.price.PriceApi;
|
||||||
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
|
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
|
||||||
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
|
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
|
||||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
||||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO;
|
import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||||
import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
|
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.ProductSkuRespDTO;
|
||||||
|
@ -27,10 +31,15 @@ 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.order.TradeOrderMapper;
|
||||||
import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper;
|
import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper;
|
||||||
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
|
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
|
||||||
import lombok.RequiredArgsConstructor;
|
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemRefundStatusEnum;
|
||||||
|
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
|
||||||
|
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||||
|
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -40,20 +49,31 @@ import java.util.Objects;
|
||||||
* @since 2022-08-26
|
* @since 2022-08-26
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class TradeOrderServiceImpl implements TradeOrderService {
|
public class TradeOrderServiceImpl implements TradeOrderService {
|
||||||
|
|
||||||
private final TradeOrderMapper tradeOrderMapper;
|
@Resource
|
||||||
|
private TradeOrderMapper tradeOrderMapper;
|
||||||
|
|
||||||
private final TradeOrderItemMapper tradeOrderItemMapper;
|
@Resource
|
||||||
|
private TradeOrderItemMapper tradeOrderItemMapper;
|
||||||
|
|
||||||
private final PriceApi priceApi;
|
@Resource
|
||||||
|
private PriceApi priceApi;
|
||||||
|
|
||||||
private final ProductSkuApi productSkuApi;
|
@Resource
|
||||||
|
private ProductSkuApi productSkuApi;
|
||||||
|
|
||||||
private final ProductSpuApi productSpuApi;
|
@Resource
|
||||||
|
private ProductSpuApi productSpuApi;
|
||||||
|
|
||||||
private final PayOrderApi payOrderApi;
|
@Resource
|
||||||
|
private PayOrderApi payOrderApi;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TradeOrderProperties tradeOrderProperties;
|
||||||
|
|
||||||
|
private static final String BLANK_PLACEHOLDER = " ";
|
||||||
|
private static final String MULTIPLIER_PLACEHOLDER = "x";
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,46 +81,92 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||||
public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) {
|
public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) {
|
||||||
|
|
||||||
List<Item> items = createReqVO.getItems();
|
List<Item> items = createReqVO.getItems();
|
||||||
// 商品SKU检查 sku可售状态,库存
|
// 商品SKU检查 sku可售状态,库存
|
||||||
List<ProductSkuRespDTO> skuInfos = productSkuApi.getSkuList(CollectionUtils.convertSet(items, Item::getSkuId));
|
List<ProductSkuRespDTO> skuInfos = productSkuApi.getSkuList(CollectionUtils.convertSet(items, Item::getSkuId));
|
||||||
Map<Long, ProductSkuRespDTO> skuInfoMap = CollectionUtils.convertMap(skuInfos, ProductSkuRespDTO::getId);
|
Map<Long, ProductSkuRespDTO> skuInfoMap = CollectionUtils.convertMap(skuInfos, ProductSkuRespDTO::getId);
|
||||||
checkSaleableAndStockFromSpu(skuInfoMap, items);
|
checkSaleableAndStockFromSpu(skuInfoMap, items);
|
||||||
|
|
||||||
// 商品SPU检查 sku可售状态,库存
|
// 商品SPU检查 sku可售状态,库存
|
||||||
List<SpuInfoRespDTO> spuInfos = productSpuApi.getSpuList(CollectionUtils.convertSet(skuInfos, ProductSkuRespDTO::getSpuId));
|
List<SpuInfoRespDTO> spuInfos = productSpuApi.getSpuList(CollectionUtils.convertSet(skuInfos, ProductSkuRespDTO::getSpuId));
|
||||||
checkSaleableFromSpu(spuInfos);
|
checkSaleableFromSpu(spuInfos);
|
||||||
|
|
||||||
// 价格计算
|
// 价格计算
|
||||||
PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, loginUserId);
|
PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, loginUserId);
|
||||||
PriceCalculateRespDTO priceResp = priceApi.calculatePrice(priceCalculateReqDTO);
|
PriceCalculateRespDTO priceResp = priceApi.calculatePrice(priceCalculateReqDTO);
|
||||||
|
|
||||||
// 订单信息记录
|
// 订单信息记录
|
||||||
TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(createReqVO, priceResp.getOrder());
|
TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(createReqVO, priceResp.getOrder());
|
||||||
|
fillTradeOrderInfoFromReqInfo(tradeOrderDO,createReqVO,loginUserId, clientIp);
|
||||||
tradeOrderMapper.insert(tradeOrderDO);
|
tradeOrderMapper.insert(tradeOrderDO);
|
||||||
|
|
||||||
// 订单项信息记录
|
// 订单项信息记录
|
||||||
List<TradeOrderItemDO> tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(tradeOrderDO, priceResp.getOrder().getItems());
|
List<TradeOrderItemDO> tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(priceResp.getOrder().getItems());
|
||||||
//-填充订单项-SKU信息
|
//-填充订单项-SKU信息
|
||||||
fillItemsInfoFromSku(tradeOrderItems, skuInfoMap);
|
fillItemsInfoFromSkuAndOrder(tradeOrderDO, tradeOrderItems, skuInfoMap);
|
||||||
tradeOrderItemMapper.insertBatch(tradeOrderItems);
|
tradeOrderItemMapper.insertBatch(tradeOrderItems);
|
||||||
|
|
||||||
// 库存扣减
|
// 库存扣减
|
||||||
List<SkuDecrementStockBatchReqDTO.Item> skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems);
|
List<SkuDecrementStockBatchReqDTO.Item> skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems);
|
||||||
productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems));
|
productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems));
|
||||||
// 生成预支付
|
|
||||||
|
|
||||||
PayOrderDataCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO);
|
// 构建预支付请求参数
|
||||||
|
PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO);
|
||||||
|
fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems);
|
||||||
|
// 生成预支付
|
||||||
return payOrderApi.createPayOrder(payOrderCreateReqDTO);
|
return payOrderApi.createPayOrder(payOrderCreateReqDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillItemsInfoFromSku(List<TradeOrderItemDO> tradeOrderItems,
|
private void fillTradeOrderInfoFromReqInfo(TradeOrderDO tradeOrderDO, AppTradeOrderCreateReqVO createReqVO,
|
||||||
Map<Long, ProductSkuRespDTO> spuInfos) {
|
Long loginUserId, String clientIp) {
|
||||||
|
tradeOrderDO.setUserId(loginUserId);
|
||||||
|
tradeOrderDO.setUserIp(clientIp);
|
||||||
|
tradeOrderDO.setSn(IdUtil.getSnowflakeNextId() + "");
|
||||||
|
tradeOrderDO.setStatus(TradeOrderStatusEnum.WAITING_PAYMENT.getStatus());
|
||||||
|
tradeOrderDO.setType(TradeOrderTypeEnum.NORMAL.getType());
|
||||||
|
tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
|
||||||
|
tradeOrderDO.setProductCount(CollectionUtils.getSumValue(createReqVO.getItems(), Item::getCount,Integer::sum));
|
||||||
|
// todo 地址&用户信息解析
|
||||||
|
|
||||||
|
// todo 数据来源?
|
||||||
|
tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillPayOrderInfoFromItems(PayOrderInfoCreateReqDTO payOrderInfoCreateReqDTO,
|
||||||
|
List<TradeOrderItemDO> tradeOrderItems) {
|
||||||
|
// 填写 商品&应用信息
|
||||||
|
payOrderInfoCreateReqDTO.setMerchantOrderId(tradeOrderProperties.getMerchantOrderId());
|
||||||
|
payOrderInfoCreateReqDTO.setAppId(tradeOrderProperties.getAppId());
|
||||||
|
|
||||||
|
// 填写商品信息
|
||||||
|
StrBuilder subject = new StrBuilder();
|
||||||
|
StrBuilder body = new StrBuilder();
|
||||||
|
|
||||||
for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) {
|
for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) {
|
||||||
|
// append subject
|
||||||
|
subject.append(BLANK_PLACEHOLDER);
|
||||||
|
subject.append(tradeOrderItem.getName());
|
||||||
|
// append body
|
||||||
|
body.append(BLANK_PLACEHOLDER);
|
||||||
|
body.append(tradeOrderItem.getName());
|
||||||
|
body.append(MULTIPLIER_PLACEHOLDER);
|
||||||
|
body.append(tradeOrderItem.getCount());
|
||||||
|
}
|
||||||
|
// 设置 subject & body
|
||||||
|
payOrderInfoCreateReqDTO.setSubject(StrUtils.maxLength(subject.subString(1), 32));
|
||||||
|
payOrderInfoCreateReqDTO.setBody(StrUtils.maxLength(body.subString(1), 128));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillItemsInfoFromSkuAndOrder(TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItems,
|
||||||
|
Map<Long, ProductSkuRespDTO> spuInfos) {
|
||||||
|
for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) {
|
||||||
|
// 填充订单信息
|
||||||
|
tradeOrderItem.setOrderId(tradeOrderDO.getId());
|
||||||
|
tradeOrderItem.setUserId(tradeOrderDO.getUserId());
|
||||||
// 填充SKU信息
|
// 填充SKU信息
|
||||||
ProductSkuRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId());
|
ProductSkuRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId());
|
||||||
tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId());
|
tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId());
|
||||||
tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl());
|
tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl());
|
||||||
tradeOrderItem.setName(skuInfoRespDTO.getName());
|
tradeOrderItem.setName(skuInfoRespDTO.getName());
|
||||||
|
tradeOrderItem.setRefundStatus(TradeOrderItemRefundStatusEnum.NONE.getStatus());
|
||||||
// todo
|
// todo
|
||||||
List<TradeOrderItemDO.Property> property =
|
List<TradeOrderItemDO.Property> property =
|
||||||
BeanUtil.copyToList(skuInfoRespDTO.getProperties(), TradeOrderItemDO.Property.class);
|
BeanUtil.copyToList(skuInfoRespDTO.getProperties(), TradeOrderItemDO.Property.class);
|
||||||
|
|
|
@ -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,53 @@
|
||||||
|
spring:
|
||||||
|
main:
|
||||||
|
lazy-initialization: true # 开启懒加载,加快速度
|
||||||
|
banner-mode: off # 单元测试,禁用 Banner
|
||||||
|
|
||||||
|
--- #################### 数据库相关配置 ####################
|
||||||
|
|
||||||
|
spring:
|
||||||
|
# 数据源配置项
|
||||||
|
datasource:
|
||||||
|
name: ruoyi-vue-pro
|
||||||
|
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||||
|
driver-class-name: org.h2.Driver
|
||||||
|
username: sa
|
||||||
|
password:
|
||||||
|
druid:
|
||||||
|
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||||
|
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||||
|
sql:
|
||||||
|
init:
|
||||||
|
schema-locations: classpath:/sql/create_tables.sql
|
||||||
|
|
||||||
|
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1 # 地址
|
||||||
|
port: 16379 # 端口(单元测试,使用 16379 端口)
|
||||||
|
database: 0 # 数据库索引
|
||||||
|
|
||||||
|
mybatis:
|
||||||
|
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||||
|
|
||||||
|
--- #################### 定时任务相关配置 ####################
|
||||||
|
|
||||||
|
--- #################### 配置中心相关配置 ####################
|
||||||
|
|
||||||
|
--- #################### 服务保障相关配置 ####################
|
||||||
|
|
||||||
|
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||||
|
|
||||||
|
# Resilience4j 配置项
|
||||||
|
|
||||||
|
--- #################### 监控相关配置 ####################
|
||||||
|
|
||||||
|
--- #################### 芋道相关配置 ####################
|
||||||
|
|
||||||
|
# 芋道配置项,设置当前项目所有自定义的配置
|
||||||
|
yudao:
|
||||||
|
info:
|
||||||
|
base-package: cn.iocoder.yudao.module
|
||||||
|
trade:
|
||||||
|
order:
|
||||||
|
app-id: 1
|
||||||
|
merchant-order-id: 1
|
|
@ -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")
|
||||||
|
);
|
||||||
|
|
|
@ -15,6 +15,6 @@ public interface PayOrderApi {
|
||||||
* @param reqDTO 创建请求
|
* @param reqDTO 创建请求
|
||||||
* @return 支付单编号
|
* @return 支付单编号
|
||||||
*/
|
*/
|
||||||
Long createPayOrder(@Valid PayOrderDataCreateReqDTO reqDTO);
|
Long createPayOrder(@Valid PayOrderInfoCreateReqDTO reqDTO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import java.util.Date;
|
||||||
* @author LeeYan9
|
* @author LeeYan9
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class PayOrderDataCreateReqDTO implements Serializable {
|
public class PayOrderInfoCreateReqDTO implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用编号
|
* 应用编号
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -95,6 +95,10 @@ yudao:
|
||||||
base-package: ${yudao.info.base-package}
|
base-package: ${yudao.info.base-package}
|
||||||
captcha:
|
captcha:
|
||||||
enable: true # 验证码的开关,默认为 true;注意,优先读取数据库 infra_config 的 yudao.captcha.enable,所以请从数据库修改,可能需要重启项目
|
enable: true # 验证码的开关,默认为 true;注意,优先读取数据库 infra_config 的 yudao.captcha.enable,所以请从数据库修改,可能需要重启项目
|
||||||
|
trade:
|
||||||
|
order:
|
||||||
|
app-id: 1
|
||||||
|
merchant-order-id: 1
|
||||||
codegen:
|
codegen:
|
||||||
base-package: ${yudao.info.base-package}
|
base-package: ${yudao.info.base-package}
|
||||||
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
|
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
|
||||||
|
|
Loading…
Reference in New Issue