!262 接口实现-交易订单创建(未完全实现)

Merge pull request !262 from LeeYan9/ly_uniapp
pull/2/head
芋道源码 2022-09-05 13:16:11 +00:00 committed by Gitee
commit 61b0624a59
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
23 changed files with 831 additions and 4 deletions

View File

@ -0,0 +1,76 @@
/**todo cancelType 设置默认值 0?*/
CREATE TABLE `trade_order`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
`sn` varchar(32) NOT NULL COMMENT '',
`type` int NOT NULL DEFAULT '0' COMMENT '[0: 1: 2: 3:]',
`terminal` int NOT NULL COMMENT '[1: 2:H5 3:iOS 4:]',
`user_id` bigint unsigned NOT NULL COMMENT '',
`user_ip` varchar(30) NOT NULL DEFAULT '' COMMENT ' IP',
`user_remark` varchar(200) DEFAULT NULL COMMENT '',
`status` int NOT NULL DEFAULT '0' COMMENT '[0: 1: 2: 3: 4:]',
`product_count` int NOT NULL COMMENT '',
`cancel_type` int NOT NULL COMMENT '[10: 20:退 30: 40:]',
`remark` varchar(200) DEFAULT NULL COMMENT '',
`payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '[0: 1:]',
`finish_time` datetime DEFAULT NULL COMMENT '',
`cancel_time` datetime DEFAULT NULL COMMENT '',
`sku_original_price` int NOT NULL DEFAULT '0' COMMENT '',
`sku_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 '',
`pay_price` int NOT NULL DEFAULT '0' COMMENT '',
`pay_order_id` int NOT NULL COMMENT '',
`pay_channel` int NOT NULL COMMENT '',
`delivery_type` int NOT NULL DEFAULT '1' COMMENT ':[1: 2:]',
`actual_delivery_type` int NOT NULL DEFAULT '1' COMMENT ':[1: 2:]',
`delivery_templateid` int DEFAULT NULL COMMENT '',
`express_no` int DEFAULT NULL COMMENT '',
`delivery_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '[0: 1:]',
`delivery_time` datetime DEFAULT NULL COMMENT '',
`receive_time` datetime DEFAULT NULL COMMENT '',
`receiver_name` varchar(20) NOT NULL COMMENT '',
`receiver_mobile` varchar(20) NOT NULL COMMENT '',
`receiver_area_id` int NOT NULL COMMENT '',
`receiver_post_code` int DEFAULT NULL COMMENT '',
`receiver_detail_address` varchar(255) NOT NULL COMMENT '',
`refund_status` int NOT NULL DEFAULT '0' COMMENT '[0:退 1:退 2:退]',
`refund_price` int NOT NULL DEFAULT '0' COMMENT '退',
`coupon_id` bigint unsigned NOT NULL COMMENT '',
`creator` varchar(64) DEFAULT '' COMMENT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updater` varchar(64) DEFAULT '' COMMENT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT ='';
DROP TABLE IF EXISTS `trade_order_item`;
CREATE TABLE `trade_order_item`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` bigint unsigned NOT NULL COMMENT '',
`order_Id` bigint unsigned NOT NULL COMMENT '',
`spu_id` bigint unsigned NOT NULL COMMENT ' SPU ',
`sku_id` bigint unsigned NOT NULL COMMENT ' SKU ',
`properties` json DEFAULT NULL COMMENT 'JSON ',
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '',
`pic_url` varchar(200) DEFAULT NULL COMMENT '',
`count` int NOT NULL COMMENT '',
`commented` bit(1) NOT NULL DEFAULT b'0' COMMENT '[0: 1:]',
`original_price` int NOT NULL DEFAULT '0' COMMENT '',
`total_original_price` int NOT NULL DEFAULT '0' COMMENT '',
`total_promotion_price` int NOT NULL DEFAULT '0' COMMENT '',
`present_price` int NOT NULL DEFAULT '0' COMMENT '',
`total_present_price` int NOT NULL DEFAULT '0' COMMENT '',
`total_pay_price` int NOT NULL DEFAULT '0' COMMENT '',
`refund_status` int NOT NULL DEFAULT '0' COMMENT '退:[0:退 1:退 2:退 3:退]',
`refund_total` int NOT NULL DEFAULT '0' COMMENT '退',
`creator` varchar(64) DEFAULT '' COMMENT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updater` varchar(64) DEFAULT '' COMMENT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT ='';

View File

@ -15,6 +15,7 @@ import java.util.Arrays;
@Getter
public enum TerminalEnum implements IntArrayValuable {
//TODO terminal 重复,请参考 '订单来源终端:[1:小程序 2:H5 3:iOS 4:安卓]'
MINI_PROGRAM(1, "小程序"),
H5(2, "H5"),
IOS(3, "iOS"),

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.product.api.sku;
import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
import java.util.Collection;
import java.util.List;
/**
* @author LeeYan9
* @since 2022-08-26
*/
public interface ProductSkuApi {
/**
* skuId sku
*
* @param skuIds sku ID
* @return sku
*/
List<SkuInfoRespDTO> getSkusByIds(Collection<Long> skuIds);
/**
* sku
*
* @param batchReqDTO sku
*/
void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO);
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.product.api.sku.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SkuDecrementStockBatchReqDTO {
private List<Item> items;
@Data
public static class Item {
/**
* SPU
*/
private Long productId;
/**
* SKU
*/
private Long skuId;
/**
*
*/
private Integer count;
}
public static SkuDecrementStockBatchReqDTO of(List<Item> items) {
return new SkuDecrementStockBatchReqDTO(items);
}
}

View File

@ -0,0 +1,93 @@
package cn.iocoder.yudao.module.product.api.sku.dto;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import lombok.Data;
import java.util.List;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Data
public class SkuInfoRespDTO {
/**
* SKU
*/
private Long id;
/**
* SKU
*/
private String name;
/**
* SPU
*/
private Long spuId;
/**
* JSON
*/
private List<Property> properties;
/**
*
*/
private Integer price;
/**
*
*/
private Integer marketPrice;
/**
*
*/
private Integer costPrice;
/**
* SKU
*/
private String barCode;
/**
*
*/
private String picUrl;
/**
* SKU
* <p>
* {@link CommonStatusEnum}
*/
private Integer status;
/**
*
*/
private Integer stock;
/**
*
*/
private Integer warnStock;
/**
* kg
*/
private Double weight;
/**
* m^3
*/
private Double volume;
/**
*
*/
@Data
public static class Property {
/**
*
*/
private Long propertyId;
/**
*
*/
private Long valueId;
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.product.api.spu;
import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO;
import java.util.Collection;
import java.util.List;
/**
* @author LeeYan9
* @since 2022-08-26
*/
public interface ProductSpuApi {
/**
* spuId spu
*
* @param spuIds spu ID
* @return spu
*/
List<SpuInfoRespDTO> getSpusByIds(Collection<Long> spuIds);
}

View File

@ -0,0 +1,124 @@
package cn.iocoder.yudao.module.product.api.spu.dto;
import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import lombok.Data;
import java.util.List;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Data
public class SpuInfoRespDTO {
/**
* SPU
*/
private Long id;
// ========== 基本信息 =========
/**
*
*/
private String name;
/**
*
*/
private String code;
/**
*
*/
private String sellPoint;
/**
*
*/
private String description;
/**
*
*/
private Long categoryId;
/**
*
*/
private Long brandId;
/**
*
* <p>
* 1.
* 2. 使 800x800 1M
* 3. 1 10
*/
private List<String> picUrls;
/**
*
*/
private String videoUrl;
/**
*
*/
private Integer sort;
/**
*
* <p>
* {@link ProductSpuStatusEnum}
*/
private Integer status;
// ========== SKU 相关字段 =========
/**
*
* <p>
* {@link ProductSpuSpecTypeEnum}
*/
private Integer specType;
/**
* 使
* <p>
* {@link SkuInfoRespDTO#getPrice()}
*/
private Integer minPrice;
/**
* 使
* <p>
* {@link SkuInfoRespDTO#getPrice()}
*/
private Integer maxPrice;
/**
* 使
* <p>
* {@link SkuInfoRespDTO#getMarketPrice()}
*/
private Integer marketPrice;
/**
*
* <p>
* {@link SkuInfoRespDTO#getStock()}
*/
private Integer totalStock;
/**
*
*/
private Boolean showStock;
// ========== 统计相关字段 =========
/**
*
*/
private Integer salesCount;
/**
*
*/
private Integer virtualSalesCount;
/**
*
*/
private Integer clickCount;
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.trade.enums.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
* Trade-Order Core
* <p>
* Trade-Order 使 1-011-000-000
*
* @author LeeYan9
* @since 2022-08-26
*/
public interface ErrorCodeConstants {
/**
* ========== Order 1-011-000-000 ==========
*/
ErrorCode ORDER_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品不存在");
ErrorCode ORDER_SPU_NOT_SALE = new ErrorCode(1011000002, "商品不可售卖");
ErrorCode ORDER_SKU_NOT_SALE = new ErrorCode(1011000003, "商品Sku不可售卖");
ErrorCode ORDER_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品库存不足");
}

View File

@ -23,12 +23,25 @@
<artifactId>yudao-module-trade-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-product-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-pay-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-market-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>

View File

@ -1,15 +1,19 @@
package cn.iocoder.yudao.module.trade.controller.app.order;
import cn.hutool.extra.servlet.ServletUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderGetCreateInfoRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderRespVO;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -19,12 +23,16 @@ import javax.servlet.http.HttpServletRequest;
@Api(tags = "用户 App - 交易订单")
@RestController
@RequestMapping("/trade/order")
@RequiredArgsConstructor
@Validated
@Slf4j
public class AppTradeOrderController {
// TODO 在思考下;
private final TradeOrderService tradeOrderService;
@GetMapping("/get-create-info")
@ApiOperation("基于商品,确认创建订单")
@PreAuthenticated
@ -36,11 +44,18 @@ public class AppTradeOrderController {
@PostMapping("/create")
@ApiOperation("创建订单")
@PreAuthenticated
public CommonResult<Integer> createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,
HttpServletRequest servletRequest) {
public CommonResult<Long> createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,
HttpServletRequest servletRequest) {
// return success(tradeOrderService.createTradeOrder(UserSecurityContextHolder.getUserId(),
// HttpUtil.getIp(servletRequest), createReqVO));
return null;
// 获取登录用户
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
// 获取用户ip地址
String clientIp = ServletUtil.getClientIP(servletRequest);
// 创建交易订单,预支付记录
Long result = tradeOrderService.createTradeOrder(loginUserId, clientIp, createReqVO);
return CommonResult.success(result);
}
@GetMapping("/get")

View File

@ -38,7 +38,7 @@ public class AppTradeOrderCreateReqVO {
@ApiModelProperty(name = "商品 SKU 编号", required = true, example = "111")
@NotNull(message = "商品 SKU 编号不能为空")
private Integer skuId;
private Long skuId;
@ApiModelProperty(name = "商品 SKU 购买数量", required = true, example = "1024")
@NotNull(message = "商品 SKU 购买数量不能为空")

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.trade.convert.order;
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Mapper
public interface TradeOrderConvert {
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
TradeOrderDO convert(AppTradeOrderCreateReqVO createReqVO, PriceCalculateRespDTO.Order order);
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.trade.convert.order;
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 org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Mapper
public interface TradeOrderItemConvert {
TradeOrderItemConvert INSTANCE = Mappers.getMapper(TradeOrderItemConvert.class);
/**
*
* @param tradeOrder
* @param items sku
* @return
*/
@Mappings({
@Mapping(source = "tradeOrder.userId", target = "userId"),
@Mapping(source = "tradeOrder.orderId", target = "orderId")
})
List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrder, List<PriceCalculateRespDTO.Item> items);
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.trade.convert.pay;
import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import org.mapstruct.factory.Mappers;
/**
* @author LeeYan9
* @since 2022-08-26
*/
public interface PayOrderConvert {
PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class);
PayOrderDataCreateReqDTO convert(TradeOrderDO tradeOrderDO);
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.trade.convert.price;
import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Mapper
public interface PriceConvert {
PriceConvert INSTANCE = Mappers.getMapper(PriceConvert.class);
@Mappings(
@Mapping(source = "userId" , target = "userId")
)
PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO , Long userId);
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.trade.convert.sku;
import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Mapper
public interface ProductSkuConvert {
ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);
List<SkuDecrementStockBatchReqDTO.Item> convert(List<TradeOrderItemDO> tradeOrderItems);
}

View File

@ -0,0 +1,11 @@
package cn.iocoder.yudao.module.trade.dal.mysql.order;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
/**
* @author LeeYan9
* @since 2022-08-26
*/
public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
}

View File

@ -0,0 +1,11 @@
package cn.iocoder.yudao.module.trade.dal.mysql.orderitem;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
/**
* @author LeeYan9
* @since 2022-08-26
*/
public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.trade.service.order;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
/**
* @author LeeYan9
* @since 2022-08-26
*/
public interface TradeOrderService {
/**
*
* @param loginUserId
* @param clientIp ip
* @param createReqVO
* @return
*/
Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO);
}

View File

@ -0,0 +1,138 @@
package cn.iocoder.yudao.module.trade.service.order;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
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.PriceCalculateRespDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO;
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.SkuInfoRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderItemConvert;
import cn.iocoder.yudao.module.trade.convert.pay.PayOrderConvert;
import cn.iocoder.yudao.module.trade.convert.price.PriceConvert;
import cn.iocoder.yudao.module.trade.convert.sku.ProductSkuConvert;
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.enums.enums.ErrorCodeConstants;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Service
@RequiredArgsConstructor
public class TradeOrderServiceImpl implements TradeOrderService {
private final TradeOrderMapper tradeOrderMapper;
private final TradeOrderItemMapper tradeOrderItemMapper;
private final PriceApi priceApi;
private final ProductSkuApi productSkuApi;
private final ProductSpuApi productSpuApi;
private final PayOrderApi payOrderApi;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) {
List<Item> items = createReqVO.getItems();
// 商品SKU检查 sku可售状态,库存
List<SkuInfoRespDTO> skuInfos = productSkuApi.getSkusByIds(CollectionUtils.convertSet(items, Item::getSkuId));
Map<Long, SkuInfoRespDTO> skuInfoMap = CollectionUtils.convertMap(skuInfos, SkuInfoRespDTO::getId);
checkSaleableAndStockFromSpu(skuInfoMap, items);
// 商品SPU检查 sku可售状态,库存
List<SpuInfoRespDTO> spuInfos = productSpuApi.getSpusByIds(CollectionUtils.convertSet(skuInfos, SkuInfoRespDTO::getSpuId));
checkSaleableFromSpu(spuInfos);
// 价格计算
PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, loginUserId);
PriceCalculateRespDTO priceResp = priceApi.calculatePrice(priceCalculateReqDTO);
// 订单信息记录
TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(createReqVO, priceResp.getOrder());
tradeOrderMapper.insert(tradeOrderDO);
// 订单项信息记录
List<TradeOrderItemDO> tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(tradeOrderDO, priceResp.getItems());
//-填充订单项-SKU信息
fillItemsInfoFromSku(tradeOrderItems, skuInfoMap);
tradeOrderItemMapper.insertBatch(tradeOrderItems);
// 库存扣减
List<SkuDecrementStockBatchReqDTO.Item> skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems);
productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems));
// 生成预支付
PayOrderDataCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO);
return payOrderApi.createPayOrder(payOrderCreateReqDTO);
}
private void fillItemsInfoFromSku(List<TradeOrderItemDO> tradeOrderItems,
Map<Long, SkuInfoRespDTO> spuInfos) {
for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) {
// 填充SKU信息
SkuInfoRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId());
tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId());
tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl());
tradeOrderItem.setName(skuInfoRespDTO.getName());
// todo
List<TradeOrderItemDO.Property> property =
BeanUtil.copyToList(skuInfoRespDTO.getProperties(), TradeOrderItemDO.Property.class);
tradeOrderItem.setProperties(property);
}
}
private void checkSaleableFromSpu(List<SpuInfoRespDTO> spuInfos) {
SpuInfoRespDTO spu = CollectionUtils.findFirst(spuInfos,
spuInfoDTO -> !Objects.equals(ProductSpuStatusEnum.ENABLE.getStyle(), spuInfoDTO.getStatus()));
if (Objects.isNull(spu)) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SPU_NOT_SALE);
}
}
private void checkSaleableAndStockFromSpu(Map<Long, SkuInfoRespDTO> skuInfoMap,
List<Item> items) {
// sku 不存在
if (items.size() != skuInfoMap.size()) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_FOUND);
}
for (Item item : items) {
SkuInfoRespDTO skuInfoDTO = skuInfoMap.get(item.getSkuId());
// sku禁用
if (!Objects.equals(CommonStatusEnum.ENABLE.getStatus(), skuInfoDTO.getStatus())) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_SALE);
}
// sku库存不足
if (item.getCount() > skuInfoDTO.getStock()) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_STOCK_NOT_ENOUGH);
}
}
}
}

View File

@ -21,6 +21,13 @@
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.pay.api.order;
import javax.validation.Valid;
/**
* @author LeeYan9
* @since 2022-08-26
*/
public interface PayOrderApi {
/**
*
*
* @param reqDTO
* @return
*/
Long createPayOrder(@Valid PayOrderDataCreateReqDTO reqDTO);
}

View File

@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.pay.api.order;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
* Request DTO
* @author LeeYan9
*/
@Data
public class PayOrderDataCreateReqDTO implements Serializable {
/**
*
*/
@NotNull(message = "应用编号不能为空")
private Long appId;
/**
* IP
*/
@NotEmpty(message = "用户 IP 不能为空")
private String userIp;
// ========== 商户相关字段 ==========
/**
*
*/
@NotEmpty(message = "商户订单编号不能为空")
private String merchantOrderId;
/**
*
*/
@NotEmpty(message = "商品标题不能为空")
@Length(max = 32, message = "商品标题不能超过 32")
private String subject;
/**
*
*/
@NotEmpty(message = "商品描述信息不能为空")
@Length(max = 128, message = "商品描述信息长度不能超过128")
private String body;
// ========== 订单相关字段 ==========
/**
*
*/
@NotNull(message = "支付金额不能为空")
@DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
private Integer amount;
/**
*
*/
@NotNull(message = "支付过期时间不能为空")
private Date expireTime;
}