Commit 61b0624a591753da3d855f353f37a2e175134d28
Committed by
Gitee
!262 接口实现-交易订单创建(未完全实现)
Merge pull request !262 from LeeYan9/ly_uniapp
Showing
23 changed files
with
831 additions
and
4 deletions
sql/optional/mall/order.sql
0 → 100644
| 1 | +/**todo cancelType 设置默认值 0?*/ | ||
| 2 | +CREATE TABLE `trade_order` | ||
| 3 | +( | ||
| 4 | + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', | ||
| 5 | + `sn` varchar(32) NOT NULL COMMENT '订单流水号', | ||
| 6 | + `type` int NOT NULL DEFAULT '0' COMMENT '订单类型:[0:普通订单 1:秒杀订单 2:拼团订单 3:砍价订单]', | ||
| 7 | + `terminal` int NOT NULL COMMENT '订单来源终端:[1:小程序 2:H5 3:iOS 4:安卓]', | ||
| 8 | + `user_id` bigint unsigned NOT NULL COMMENT '用户编号', | ||
| 9 | + `user_ip` varchar(30) NOT NULL DEFAULT '' COMMENT '用户 IP', | ||
| 10 | + `user_remark` varchar(200) DEFAULT NULL COMMENT '用户备注', | ||
| 11 | + `status` int NOT NULL DEFAULT '0' COMMENT '订单状态:[0:待付款 1:待发货 2:待收货 3:已完成 4:已关闭]', | ||
| 12 | + `product_count` int NOT NULL COMMENT '购买的商品数量', | ||
| 13 | + `cancel_type` int NOT NULL COMMENT '取消类型:[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]', | ||
| 14 | + `remark` varchar(200) DEFAULT NULL COMMENT '商家备注', | ||
| 15 | + `payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已支付:[0:未支付 1:已经支付过]', | ||
| 16 | + `finish_time` datetime DEFAULT NULL COMMENT '订单完成时间', | ||
| 17 | + `cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间', | ||
| 18 | + `sku_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分', | ||
| 19 | + `sku_promotion_price` int NOT NULL DEFAULT '0' COMMENT '商品优惠(总),单位:分', | ||
| 20 | + `order_promotion_price` int NOT NULL DEFAULT '0' COMMENT '订单优惠(总),单位:分', | ||
| 21 | + `delivery_price` int NOT NULL DEFAULT '0' COMMENT '运费金额,单位:分', | ||
| 22 | + `pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分', | ||
| 23 | + `pay_order_id` int NOT NULL COMMENT '支付订单编号', | ||
| 24 | + `pay_channel` int NOT NULL COMMENT '支付成功的支付渠道', | ||
| 25 | + `delivery_type` int NOT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]', | ||
| 26 | + `actual_delivery_type` int NOT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]', | ||
| 27 | + `delivery_templateid` int DEFAULT NULL COMMENT '配置模板的编号', | ||
| 28 | + `express_no` int DEFAULT NULL COMMENT '物流公司单号', | ||
| 29 | + `delivery_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '发货状态[0:未发货 1:已发货]', | ||
| 30 | + `delivery_time` datetime DEFAULT NULL COMMENT '发货时间', | ||
| 31 | + `receive_time` datetime DEFAULT NULL COMMENT '收货时间', | ||
| 32 | + `receiver_name` varchar(20) NOT NULL COMMENT '收件人名称', | ||
| 33 | + `receiver_mobile` varchar(20) NOT NULL COMMENT '收件人手机', | ||
| 34 | + `receiver_area_id` int NOT NULL COMMENT '收件人地区编号', | ||
| 35 | + `receiver_post_code` int DEFAULT NULL COMMENT '收件人邮编', | ||
| 36 | + `receiver_detail_address` varchar(255) NOT NULL COMMENT '收件人详细地址', | ||
| 37 | + `refund_status` int NOT NULL DEFAULT '0' COMMENT '订单状态:[0:未退款 1:部分退款 2:全部退款]', | ||
| 38 | + `refund_price` int NOT NULL DEFAULT '0' COMMENT '退款金额,单位:分', | ||
| 39 | + `coupon_id` bigint unsigned NOT NULL COMMENT '优惠劵编号', | ||
| 40 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | ||
| 41 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 42 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | ||
| 43 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 44 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | ||
| 45 | + PRIMARY KEY (`id`) USING BTREE | ||
| 46 | +) ENGINE = InnoDB COMMENT ='交易订单表'; | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +DROP TABLE IF EXISTS `trade_order_item`; | ||
| 50 | +CREATE TABLE `trade_order_item` | ||
| 51 | +( | ||
| 52 | + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', | ||
| 53 | + `user_id` bigint unsigned NOT NULL COMMENT '用户编号', | ||
| 54 | + `order_Id` bigint unsigned NOT NULL COMMENT '订单编号', | ||
| 55 | + `spu_id` bigint unsigned NOT NULL COMMENT '商品 SPU 编号', | ||
| 56 | + `sku_id` bigint unsigned NOT NULL COMMENT '商品 SKU 编号', | ||
| 57 | + `properties` json DEFAULT NULL COMMENT '规格值数组,JSON 格式', | ||
| 58 | + `name` varchar(128) NOT NULL DEFAULT '' COMMENT '商品名称', | ||
| 59 | + `pic_url` varchar(200) DEFAULT NULL COMMENT '商品图片', | ||
| 60 | + `count` int NOT NULL COMMENT '购买数量', | ||
| 61 | + `commented` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否评论:[0:未评论 1:已评论]', | ||
| 62 | + `original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(单),单位:分', | ||
| 63 | + `total_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分', | ||
| 64 | + `total_promotion_price` int NOT NULL DEFAULT '0' COMMENT '商品级优惠(总),单位:分', | ||
| 65 | + `present_price` int NOT NULL DEFAULT '0' COMMENT '最终购买金额(单),单位:分。', | ||
| 66 | + `total_present_price` int NOT NULL DEFAULT '0' COMMENT '最终购买金额(总),单位:分。', | ||
| 67 | + `total_pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分', | ||
| 68 | + `refund_status` int NOT NULL DEFAULT '0' COMMENT '退款状态:[0:未申请退款 1:申请退款 2:等待退款 3:退款成功]', | ||
| 69 | + `refund_total` int NOT NULL DEFAULT '0' COMMENT '退款总金额,单位:分', | ||
| 70 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | ||
| 71 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 72 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | ||
| 73 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 74 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | ||
| 75 | + PRIMARY KEY (`id`) USING BTREE | ||
| 76 | +) ENGINE = InnoDB COMMENT ='交易订单明细表'; | ||
| 0 | \ No newline at end of file | 77 | \ No newline at end of file |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
| @@ -15,6 +15,7 @@ import java.util.Arrays; | @@ -15,6 +15,7 @@ import java.util.Arrays; | ||
| 15 | @Getter | 15 | @Getter |
| 16 | public enum TerminalEnum implements IntArrayValuable { | 16 | public enum TerminalEnum implements IntArrayValuable { |
| 17 | 17 | ||
| 18 | + //TODO terminal 重复,请参考 '订单来源终端:[1:小程序 2:H5 3:iOS 4:安卓]' | ||
| 18 | MINI_PROGRAM(1, "小程序"), | 19 | MINI_PROGRAM(1, "小程序"), |
| 19 | H5(2, "H5"), | 20 | H5(2, "H5"), |
| 20 | IOS(3, "iOS"), | 21 | IOS(3, "iOS"), |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.api.sku; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; | ||
| 4 | +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; | ||
| 5 | + | ||
| 6 | +import java.util.Collection; | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * @author LeeYan9 | ||
| 11 | + * @since 2022-08-26 | ||
| 12 | + */ | ||
| 13 | +public interface ProductSkuApi { | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 根据skuId列表 查询sku信息 | ||
| 18 | + * | ||
| 19 | + * @param skuIds sku ID列表 | ||
| 20 | + * @return sku信息列表 | ||
| 21 | + */ | ||
| 22 | + List<SkuInfoRespDTO> getSkusByIds(Collection<Long> skuIds); | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 批量扣减sku库存 | ||
| 26 | + * | ||
| 27 | + * @param batchReqDTO sku库存信息列表 | ||
| 28 | + */ | ||
| 29 | + void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO); | ||
| 30 | +} |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuDecrementStockBatchReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.api.sku.dto; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.NoArgsConstructor; | ||
| 6 | + | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * @author LeeYan9 | ||
| 11 | + * @since 2022-08-26 | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +@NoArgsConstructor | ||
| 15 | +@AllArgsConstructor | ||
| 16 | +public class SkuDecrementStockBatchReqDTO { | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + private List<Item> items; | ||
| 20 | + | ||
| 21 | + @Data | ||
| 22 | + public static class Item { | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 商品 SPU 编号,自增 | ||
| 26 | + */ | ||
| 27 | + private Long productId; | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 商品 SKU 编号,自增 | ||
| 31 | + */ | ||
| 32 | + private Long skuId; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 数量 | ||
| 36 | + */ | ||
| 37 | + private Integer count; | ||
| 38 | + | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public static SkuDecrementStockBatchReqDTO of(List<Item> items) { | ||
| 42 | + return new SkuDecrementStockBatchReqDTO(items); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | +} |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.api.sku.dto; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * @author LeeYan9 | ||
| 10 | + * @since 2022-08-26 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class SkuInfoRespDTO { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 商品 SKU 编号,自增 | ||
| 17 | + */ | ||
| 18 | + private Long id; | ||
| 19 | + /** | ||
| 20 | + * 商品 SKU 名字 | ||
| 21 | + */ | ||
| 22 | + private String name; | ||
| 23 | + /** | ||
| 24 | + * SPU 编号 | ||
| 25 | + */ | ||
| 26 | + private Long spuId; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 规格值数组,JSON 格式 | ||
| 30 | + */ | ||
| 31 | + private List<Property> properties; | ||
| 32 | + /** | ||
| 33 | + * 销售价格,单位:分 | ||
| 34 | + */ | ||
| 35 | + private Integer price; | ||
| 36 | + /** | ||
| 37 | + * 市场价,单位:分 | ||
| 38 | + */ | ||
| 39 | + private Integer marketPrice; | ||
| 40 | + /** | ||
| 41 | + * 成本价,单位:分 | ||
| 42 | + */ | ||
| 43 | + private Integer costPrice; | ||
| 44 | + /** | ||
| 45 | + * SKU 的条形码 | ||
| 46 | + */ | ||
| 47 | + private String barCode; | ||
| 48 | + /** | ||
| 49 | + * 图片地址 | ||
| 50 | + */ | ||
| 51 | + private String picUrl; | ||
| 52 | + /** | ||
| 53 | + * SKU 状态 | ||
| 54 | + * <p> | ||
| 55 | + * 枚举 {@link CommonStatusEnum} | ||
| 56 | + */ | ||
| 57 | + private Integer status; | ||
| 58 | + /** | ||
| 59 | + * 库存 | ||
| 60 | + */ | ||
| 61 | + private Integer stock; | ||
| 62 | + /** | ||
| 63 | + * 预警预存 | ||
| 64 | + */ | ||
| 65 | + private Integer warnStock; | ||
| 66 | + /** | ||
| 67 | + * 商品重量,单位:kg 千克 | ||
| 68 | + */ | ||
| 69 | + private Double weight; | ||
| 70 | + /** | ||
| 71 | + * 商品体积,单位:m^3 平米 | ||
| 72 | + */ | ||
| 73 | + private Double volume; | ||
| 74 | + | ||
| 75 | + /** | ||
| 76 | + * 商品属性 | ||
| 77 | + */ | ||
| 78 | + @Data | ||
| 79 | + public static class Property { | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 属性编号 | ||
| 83 | + */ | ||
| 84 | + private Long propertyId; | ||
| 85 | + /** | ||
| 86 | + * 属性值编号 | ||
| 87 | + */ | ||
| 88 | + private Long valueId; | ||
| 89 | + | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + | ||
| 93 | +} |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.api.spu; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; | ||
| 4 | +import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; | ||
| 5 | + | ||
| 6 | +import java.util.Collection; | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * @author LeeYan9 | ||
| 11 | + * @since 2022-08-26 | ||
| 12 | + */ | ||
| 13 | +public interface ProductSpuApi { | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 根据spuId列表 查询spu信息 | ||
| 18 | + * | ||
| 19 | + * @param spuIds spu ID列表 | ||
| 20 | + * @return spu信息列表 | ||
| 21 | + */ | ||
| 22 | + List<SpuInfoRespDTO> getSpusByIds(Collection<Long> spuIds); | ||
| 23 | +} |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.api.spu.dto; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; | ||
| 4 | +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; | ||
| 5 | +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||
| 6 | +import lombok.Data; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * @author LeeYan9 | ||
| 12 | + * @since 2022-08-26 | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +public class SpuInfoRespDTO { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 商品 SPU 编号,自增 | ||
| 19 | + */ | ||
| 20 | + private Long id; | ||
| 21 | + | ||
| 22 | + // ========== 基本信息 ========= | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 商品名称 | ||
| 26 | + */ | ||
| 27 | + private String name; | ||
| 28 | + /** | ||
| 29 | + * 商品编码 | ||
| 30 | + */ | ||
| 31 | + private String code; | ||
| 32 | + /** | ||
| 33 | + * 商品卖点 | ||
| 34 | + */ | ||
| 35 | + private String sellPoint; | ||
| 36 | + /** | ||
| 37 | + * 商品详情 | ||
| 38 | + */ | ||
| 39 | + private String description; | ||
| 40 | + /** | ||
| 41 | + * 商品分类编号 | ||
| 42 | + */ | ||
| 43 | + private Long categoryId; | ||
| 44 | + /** | ||
| 45 | + * 商品品牌编号 | ||
| 46 | + */ | ||
| 47 | + private Long brandId; | ||
| 48 | + /** | ||
| 49 | + * 商品图片的数组 | ||
| 50 | + * <p> | ||
| 51 | + * 1. 第一张图片将作为商品主图,支持同时上传多张图; | ||
| 52 | + * 2. 建议使用尺寸 800x800 像素以上、大小不超过 1M 的正方形图片; | ||
| 53 | + * 3. 至少 1 张,最多上传 10 张 | ||
| 54 | + */ | ||
| 55 | + private List<String> picUrls; | ||
| 56 | + /** | ||
| 57 | + * 商品视频 | ||
| 58 | + */ | ||
| 59 | + private String videoUrl; | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 排序字段 | ||
| 63 | + */ | ||
| 64 | + private Integer sort; | ||
| 65 | + /** | ||
| 66 | + * 商品状态 | ||
| 67 | + * <p> | ||
| 68 | + * 枚举 {@link ProductSpuStatusEnum} | ||
| 69 | + */ | ||
| 70 | + private Integer status; | ||
| 71 | + | ||
| 72 | + // ========== SKU 相关字段 ========= | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * 规格类型 | ||
| 76 | + * <p> | ||
| 77 | + * 枚举 {@link ProductSpuSpecTypeEnum} | ||
| 78 | + */ | ||
| 79 | + private Integer specType; | ||
| 80 | + /** | ||
| 81 | + * 最小价格,单位使用:分 | ||
| 82 | + * <p> | ||
| 83 | + * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最小值 | ||
| 84 | + */ | ||
| 85 | + private Integer minPrice; | ||
| 86 | + /** | ||
| 87 | + * 最大价格,单位使用:分 | ||
| 88 | + * <p> | ||
| 89 | + * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最大值 | ||
| 90 | + */ | ||
| 91 | + private Integer maxPrice; | ||
| 92 | + /** | ||
| 93 | + * 市场价,单位使用:分 | ||
| 94 | + * <p> | ||
| 95 | + * 基于其对应的 {@link SkuInfoRespDTO#getMarketPrice()} 最大值 | ||
| 96 | + */ | ||
| 97 | + private Integer marketPrice; | ||
| 98 | + /** | ||
| 99 | + * 总库存 | ||
| 100 | + * <p> | ||
| 101 | + * 基于其对应的 {@link SkuInfoRespDTO#getStock()} 求和 | ||
| 102 | + */ | ||
| 103 | + private Integer totalStock; | ||
| 104 | + /** | ||
| 105 | + * 是否展示库存 | ||
| 106 | + */ | ||
| 107 | + private Boolean showStock; | ||
| 108 | + | ||
| 109 | + // ========== 统计相关字段 ========= | ||
| 110 | + | ||
| 111 | + /** | ||
| 112 | + * 商品销量 | ||
| 113 | + */ | ||
| 114 | + private Integer salesCount; | ||
| 115 | + /** | ||
| 116 | + * 虚拟销量 | ||
| 117 | + */ | ||
| 118 | + private Integer virtualSalesCount; | ||
| 119 | + /** | ||
| 120 | + * 商品点击量 | ||
| 121 | + */ | ||
| 122 | + private Integer clickCount; | ||
| 123 | + | ||
| 124 | +} |
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.enums.enums; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.exception.ErrorCode; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * Trade-Order 错误码 Core 枚举类 | ||
| 7 | + * <p> | ||
| 8 | + * Trade-Order 系统,使用 1-011-000-000 段 | ||
| 9 | + * | ||
| 10 | + * @author LeeYan9 | ||
| 11 | + * @since 2022-08-26 | ||
| 12 | + */ | ||
| 13 | +public interface ErrorCodeConstants { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * ========== Order 模块 1-011-000-000 ========== | ||
| 17 | + */ | ||
| 18 | + ErrorCode ORDER_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品不存在"); | ||
| 19 | + ErrorCode ORDER_SPU_NOT_SALE = new ErrorCode(1011000002, "商品不可售卖"); | ||
| 20 | + ErrorCode ORDER_SKU_NOT_SALE = new ErrorCode(1011000003, "商品Sku不可售卖"); | ||
| 21 | + ErrorCode ORDER_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品库存不足"); | ||
| 22 | + | ||
| 23 | +} |
yudao-module-mall/yudao-module-trade-biz/pom.xml
| @@ -23,12 +23,25 @@ | @@ -23,12 +23,25 @@ | ||
| 23 | <artifactId>yudao-module-trade-api</artifactId> | 23 | <artifactId>yudao-module-trade-api</artifactId> |
| 24 | <version>${revision}</version> | 24 | <version>${revision}</version> |
| 25 | </dependency> | 25 | </dependency> |
| 26 | + | ||
| 26 | <dependency> | 27 | <dependency> |
| 27 | <groupId>cn.iocoder.boot</groupId> | 28 | <groupId>cn.iocoder.boot</groupId> |
| 28 | <artifactId>yudao-module-product-api</artifactId> | 29 | <artifactId>yudao-module-product-api</artifactId> |
| 29 | <version>${revision}</version> | 30 | <version>${revision}</version> |
| 30 | </dependency> | 31 | </dependency> |
| 31 | 32 | ||
| 33 | + <dependency> | ||
| 34 | + <groupId>cn.iocoder.boot</groupId> | ||
| 35 | + <artifactId>yudao-module-pay-api</artifactId> | ||
| 36 | + <version>${revision}</version> | ||
| 37 | + </dependency> | ||
| 38 | + | ||
| 39 | + <dependency> | ||
| 40 | + <groupId>cn.iocoder.boot</groupId> | ||
| 41 | + <artifactId>yudao-module-market-api</artifactId> | ||
| 42 | + <version>${revision}</version> | ||
| 43 | + </dependency> | ||
| 44 | + | ||
| 32 | <!-- 业务组件 --> | 45 | <!-- 业务组件 --> |
| 33 | <dependency> | 46 | <dependency> |
| 34 | <groupId>cn.iocoder.boot</groupId> | 47 | <groupId>cn.iocoder.boot</groupId> |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
| 1 | package cn.iocoder.yudao.module.trade.controller.app.order; | 1 | package cn.iocoder.yudao.module.trade.controller.app.order; |
| 2 | 2 | ||
| 3 | +import cn.hutool.extra.servlet.ServletUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; | 6 | import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; |
| 7 | +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 6 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; | 8 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; |
| 7 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderGetCreateInfoRespVO; | 9 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderGetCreateInfoRespVO; |
| 8 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderPageReqVO; | 10 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderPageReqVO; |
| 9 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderRespVO; | 11 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderRespVO; |
| 12 | +import cn.iocoder.yudao.module.trade.service.order.TradeOrderService; | ||
| 10 | import io.swagger.annotations.Api; | 13 | import io.swagger.annotations.Api; |
| 11 | import io.swagger.annotations.ApiImplicitParam; | 14 | import io.swagger.annotations.ApiImplicitParam; |
| 12 | import io.swagger.annotations.ApiOperation; | 15 | import io.swagger.annotations.ApiOperation; |
| 16 | +import lombok.RequiredArgsConstructor; | ||
| 13 | import lombok.extern.slf4j.Slf4j; | 17 | import lombok.extern.slf4j.Slf4j; |
| 14 | import org.springframework.validation.annotation.Validated; | 18 | import org.springframework.validation.annotation.Validated; |
| 15 | import org.springframework.web.bind.annotation.*; | 19 | import org.springframework.web.bind.annotation.*; |
| @@ -19,12 +23,16 @@ import javax.servlet.http.HttpServletRequest; | @@ -19,12 +23,16 @@ import javax.servlet.http.HttpServletRequest; | ||
| 19 | @Api(tags = "用户 App - 交易订单") | 23 | @Api(tags = "用户 App - 交易订单") |
| 20 | @RestController | 24 | @RestController |
| 21 | @RequestMapping("/trade/order") | 25 | @RequestMapping("/trade/order") |
| 26 | +@RequiredArgsConstructor | ||
| 22 | @Validated | 27 | @Validated |
| 23 | @Slf4j | 28 | @Slf4j |
| 24 | public class AppTradeOrderController { | 29 | public class AppTradeOrderController { |
| 25 | 30 | ||
| 26 | // TODO 在思考下; | 31 | // TODO 在思考下; |
| 27 | 32 | ||
| 33 | + private final TradeOrderService tradeOrderService; | ||
| 34 | + | ||
| 35 | + | ||
| 28 | @GetMapping("/get-create-info") | 36 | @GetMapping("/get-create-info") |
| 29 | @ApiOperation("基于商品,确认创建订单") | 37 | @ApiOperation("基于商品,确认创建订单") |
| 30 | @PreAuthenticated | 38 | @PreAuthenticated |
| @@ -36,11 +44,18 @@ public class AppTradeOrderController { | @@ -36,11 +44,18 @@ public class AppTradeOrderController { | ||
| 36 | @PostMapping("/create") | 44 | @PostMapping("/create") |
| 37 | @ApiOperation("创建订单") | 45 | @ApiOperation("创建订单") |
| 38 | @PreAuthenticated | 46 | @PreAuthenticated |
| 39 | - public CommonResult<Integer> createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO, | ||
| 40 | - HttpServletRequest servletRequest) { | 47 | + public CommonResult<Long> createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO, |
| 48 | + HttpServletRequest servletRequest) { | ||
| 41 | // return success(tradeOrderService.createTradeOrder(UserSecurityContextHolder.getUserId(), | 49 | // return success(tradeOrderService.createTradeOrder(UserSecurityContextHolder.getUserId(), |
| 42 | // HttpUtil.getIp(servletRequest), createReqVO)); | 50 | // HttpUtil.getIp(servletRequest), createReqVO)); |
| 43 | - return null; | 51 | + // 获取登录用户 |
| 52 | + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); | ||
| 53 | + // 获取用户ip地址 | ||
| 54 | + String clientIp = ServletUtil.getClientIP(servletRequest); | ||
| 55 | + // 创建交易订单,预支付记录 | ||
| 56 | + Long result = tradeOrderService.createTradeOrder(loginUserId, clientIp, createReqVO); | ||
| 57 | + | ||
| 58 | + return CommonResult.success(result); | ||
| 44 | } | 59 | } |
| 45 | 60 | ||
| 46 | @GetMapping("/get") | 61 | @GetMapping("/get") |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java
| @@ -38,7 +38,7 @@ public class AppTradeOrderCreateReqVO { | @@ -38,7 +38,7 @@ public class AppTradeOrderCreateReqVO { | ||
| 38 | 38 | ||
| 39 | @ApiModelProperty(name = "商品 SKU 编号", required = true, example = "111") | 39 | @ApiModelProperty(name = "商品 SKU 编号", required = true, example = "111") |
| 40 | @NotNull(message = "商品 SKU 编号不能为空") | 40 | @NotNull(message = "商品 SKU 编号不能为空") |
| 41 | - private Integer skuId; | 41 | + private Long skuId; |
| 42 | 42 | ||
| 43 | @ApiModelProperty(name = "商品 SKU 购买数量", required = true, example = "1024") | 43 | @ApiModelProperty(name = "商品 SKU 购买数量", required = true, example = "1024") |
| 44 | @NotNull(message = "商品 SKU 购买数量不能为空") | 44 | @NotNull(message = "商品 SKU 购买数量不能为空") |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.convert.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; | ||
| 4 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; | ||
| 5 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | ||
| 6 | +import org.mapstruct.Mapper; | ||
| 7 | +import org.mapstruct.factory.Mappers; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * @author LeeYan9 | ||
| 11 | + * @since 2022-08-26 | ||
| 12 | + */ | ||
| 13 | +@Mapper | ||
| 14 | +public interface TradeOrderConvert { | ||
| 15 | + | ||
| 16 | + TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class); | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + TradeOrderDO convert(AppTradeOrderCreateReqVO createReqVO, PriceCalculateRespDTO.Order order); | ||
| 20 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.convert.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; | ||
| 4 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | ||
| 5 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | ||
| 6 | +import org.mapstruct.Mapper; | ||
| 7 | +import org.mapstruct.Mapping; | ||
| 8 | +import org.mapstruct.Mappings; | ||
| 9 | +import org.mapstruct.factory.Mappers; | ||
| 10 | + | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * @author LeeYan9 | ||
| 15 | + * @since 2022-08-26 | ||
| 16 | + */ | ||
| 17 | +@Mapper | ||
| 18 | +public interface TradeOrderItemConvert { | ||
| 19 | + | ||
| 20 | + TradeOrderItemConvert INSTANCE = Mappers.getMapper(TradeOrderItemConvert.class); | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * | ||
| 24 | + * @param tradeOrder 交易订单 | ||
| 25 | + * @param items sku列表价格 | ||
| 26 | + * @return 订单项 | ||
| 27 | + */ | ||
| 28 | + @Mappings({ | ||
| 29 | + @Mapping(source = "tradeOrder.userId", target = "userId"), | ||
| 30 | + @Mapping(source = "tradeOrder.orderId", target = "orderId") | ||
| 31 | + }) | ||
| 32 | + List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrder, List<PriceCalculateRespDTO.Item> items); | ||
| 33 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.convert.pay; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; | ||
| 4 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | ||
| 5 | +import org.mapstruct.factory.Mappers; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * @author LeeYan9 | ||
| 9 | + * @since 2022-08-26 | ||
| 10 | + */ | ||
| 11 | +public interface PayOrderConvert { | ||
| 12 | + | ||
| 13 | + PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class); | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + PayOrderDataCreateReqDTO convert(TradeOrderDO tradeOrderDO); | ||
| 17 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/price/PriceConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.convert.price; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; | ||
| 4 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; | ||
| 5 | +import org.mapstruct.Mapper; | ||
| 6 | +import org.mapstruct.Mapping; | ||
| 7 | +import org.mapstruct.Mappings; | ||
| 8 | +import org.mapstruct.factory.Mappers; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * @author LeeYan9 | ||
| 12 | + * @since 2022-08-26 | ||
| 13 | + */ | ||
| 14 | +@Mapper | ||
| 15 | +public interface PriceConvert { | ||
| 16 | + | ||
| 17 | + PriceConvert INSTANCE = Mappers.getMapper(PriceConvert.class); | ||
| 18 | + | ||
| 19 | + @Mappings( | ||
| 20 | + @Mapping(source = "userId" , target = "userId") | ||
| 21 | + ) | ||
| 22 | + PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO , Long userId); | ||
| 23 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.convert.sku; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; | ||
| 4 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | ||
| 5 | +import org.mapstruct.Mapper; | ||
| 6 | +import org.mapstruct.factory.Mappers; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * @author LeeYan9 | ||
| 12 | + * @since 2022-08-26 | ||
| 13 | + */ | ||
| 14 | +@Mapper | ||
| 15 | +public interface ProductSkuConvert { | ||
| 16 | + | ||
| 17 | + ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class); | ||
| 18 | + | ||
| 19 | + List<SkuDecrementStockBatchReqDTO.Item> convert(List<TradeOrderItemDO> tradeOrderItems); | ||
| 20 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.dal.mysql.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @author LeeYan9 | ||
| 8 | + * @since 2022-08-26 | ||
| 9 | + */ | ||
| 10 | +public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> { | ||
| 11 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.dal.mysql.orderitem; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @author LeeYan9 | ||
| 8 | + * @since 2022-08-26 | ||
| 9 | + */ | ||
| 10 | +public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> { | ||
| 11 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.service.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * @author LeeYan9 | ||
| 7 | + * @since 2022-08-26 | ||
| 8 | + */ | ||
| 9 | +public interface TradeOrderService { | ||
| 10 | + | ||
| 11 | + /** | ||
| 12 | + * 创建交易订单 | ||
| 13 | + * @param loginUserId 登录用户 | ||
| 14 | + * @param clientIp 用户ip地址 | ||
| 15 | + * @param createReqVO 创建交易订单请求模型 | ||
| 16 | + * @return 交易订单创建结果 | ||
| 17 | + */ | ||
| 18 | + Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO); | ||
| 19 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.service.order; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.bean.BeanUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 5 | +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | ||
| 6 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 7 | +import cn.iocoder.yudao.module.market.api.price.PriceApi; | ||
| 8 | +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; | ||
| 9 | +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; | ||
| 10 | +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; | ||
| 11 | +import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; | ||
| 12 | +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; | ||
| 13 | +import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; | ||
| 14 | +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; | ||
| 15 | +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | ||
| 16 | +import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; | ||
| 17 | +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||
| 18 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; | ||
| 19 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item; | ||
| 20 | +import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; | ||
| 21 | +import cn.iocoder.yudao.module.trade.convert.order.TradeOrderItemConvert; | ||
| 22 | +import cn.iocoder.yudao.module.trade.convert.pay.PayOrderConvert; | ||
| 23 | +import cn.iocoder.yudao.module.trade.convert.price.PriceConvert; | ||
| 24 | +import cn.iocoder.yudao.module.trade.convert.sku.ProductSkuConvert; | ||
| 25 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | ||
| 26 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | ||
| 27 | +import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; | ||
| 28 | +import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper; | ||
| 29 | +import cn.iocoder.yudao.module.trade.enums.enums.ErrorCodeConstants; | ||
| 30 | +import lombok.RequiredArgsConstructor; | ||
| 31 | +import org.springframework.stereotype.Service; | ||
| 32 | +import org.springframework.transaction.annotation.Transactional; | ||
| 33 | + | ||
| 34 | +import java.util.List; | ||
| 35 | +import java.util.Map; | ||
| 36 | +import java.util.Objects; | ||
| 37 | + | ||
| 38 | +/** | ||
| 39 | + * @author LeeYan9 | ||
| 40 | + * @since 2022-08-26 | ||
| 41 | + */ | ||
| 42 | +@Service | ||
| 43 | +@RequiredArgsConstructor | ||
| 44 | +public class TradeOrderServiceImpl implements TradeOrderService { | ||
| 45 | + | ||
| 46 | + private final TradeOrderMapper tradeOrderMapper; | ||
| 47 | + | ||
| 48 | + private final TradeOrderItemMapper tradeOrderItemMapper; | ||
| 49 | + | ||
| 50 | + private final PriceApi priceApi; | ||
| 51 | + | ||
| 52 | + private final ProductSkuApi productSkuApi; | ||
| 53 | + | ||
| 54 | + private final ProductSpuApi productSpuApi; | ||
| 55 | + | ||
| 56 | + private final PayOrderApi payOrderApi; | ||
| 57 | + | ||
| 58 | + | ||
| 59 | + @Override | ||
| 60 | + @Transactional(rollbackFor = Exception.class) | ||
| 61 | + public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) { | ||
| 62 | + | ||
| 63 | + List<Item> items = createReqVO.getItems(); | ||
| 64 | + // 商品SKU检查 sku可售状态,库存 | ||
| 65 | + List<SkuInfoRespDTO> skuInfos = productSkuApi.getSkusByIds(CollectionUtils.convertSet(items, Item::getSkuId)); | ||
| 66 | + Map<Long, SkuInfoRespDTO> skuInfoMap = CollectionUtils.convertMap(skuInfos, SkuInfoRespDTO::getId); | ||
| 67 | + checkSaleableAndStockFromSpu(skuInfoMap, items); | ||
| 68 | + | ||
| 69 | + // 商品SPU检查 sku可售状态,库存 | ||
| 70 | + List<SpuInfoRespDTO> spuInfos = productSpuApi.getSpusByIds(CollectionUtils.convertSet(skuInfos, SkuInfoRespDTO::getSpuId)); | ||
| 71 | + checkSaleableFromSpu(spuInfos); | ||
| 72 | + | ||
| 73 | + // 价格计算 | ||
| 74 | + PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, loginUserId); | ||
| 75 | + PriceCalculateRespDTO priceResp = priceApi.calculatePrice(priceCalculateReqDTO); | ||
| 76 | + | ||
| 77 | + // 订单信息记录 | ||
| 78 | + TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(createReqVO, priceResp.getOrder()); | ||
| 79 | + tradeOrderMapper.insert(tradeOrderDO); | ||
| 80 | + | ||
| 81 | + // 订单项信息记录 | ||
| 82 | + List<TradeOrderItemDO> tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(tradeOrderDO, priceResp.getItems()); | ||
| 83 | + //-填充订单项-SKU信息 | ||
| 84 | + fillItemsInfoFromSku(tradeOrderItems, skuInfoMap); | ||
| 85 | + tradeOrderItemMapper.insertBatch(tradeOrderItems); | ||
| 86 | + | ||
| 87 | + // 库存扣减 | ||
| 88 | + List<SkuDecrementStockBatchReqDTO.Item> skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems); | ||
| 89 | + productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems)); | ||
| 90 | + // 生成预支付 | ||
| 91 | + | ||
| 92 | + PayOrderDataCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO); | ||
| 93 | + return payOrderApi.createPayOrder(payOrderCreateReqDTO); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + private void fillItemsInfoFromSku(List<TradeOrderItemDO> tradeOrderItems, | ||
| 97 | + Map<Long, SkuInfoRespDTO> spuInfos) { | ||
| 98 | + for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) { | ||
| 99 | + // 填充SKU信息 | ||
| 100 | + SkuInfoRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId()); | ||
| 101 | + tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId()); | ||
| 102 | + tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl()); | ||
| 103 | + tradeOrderItem.setName(skuInfoRespDTO.getName()); | ||
| 104 | + // todo | ||
| 105 | + List<TradeOrderItemDO.Property> property = | ||
| 106 | + BeanUtil.copyToList(skuInfoRespDTO.getProperties(), TradeOrderItemDO.Property.class); | ||
| 107 | + tradeOrderItem.setProperties(property); | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + private void checkSaleableFromSpu(List<SpuInfoRespDTO> spuInfos) { | ||
| 112 | + SpuInfoRespDTO spu = CollectionUtils.findFirst(spuInfos, | ||
| 113 | + spuInfoDTO -> !Objects.equals(ProductSpuStatusEnum.ENABLE.getStyle(), spuInfoDTO.getStatus())); | ||
| 114 | + if (Objects.isNull(spu)) { | ||
| 115 | + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SPU_NOT_SALE); | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + private void checkSaleableAndStockFromSpu(Map<Long, SkuInfoRespDTO> skuInfoMap, | ||
| 120 | + List<Item> items) { | ||
| 121 | + // sku 不存在 | ||
| 122 | + if (items.size() != skuInfoMap.size()) { | ||
| 123 | + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_FOUND); | ||
| 124 | + } | ||
| 125 | + for (Item item : items) { | ||
| 126 | + SkuInfoRespDTO skuInfoDTO = skuInfoMap.get(item.getSkuId()); | ||
| 127 | + // sku禁用 | ||
| 128 | + if (!Objects.equals(CommonStatusEnum.ENABLE.getStatus(), skuInfoDTO.getStatus())) { | ||
| 129 | + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_SALE); | ||
| 130 | + } | ||
| 131 | + // sku库存不足 | ||
| 132 | + if (item.getCount() > skuInfoDTO.getStock()) { | ||
| 133 | + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_STOCK_NOT_ENOUGH); | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + } | ||
| 138 | +} |
yudao-module-pay/yudao-module-pay-api/pom.xml
| @@ -21,6 +21,13 @@ | @@ -21,6 +21,13 @@ | ||
| 21 | <groupId>cn.iocoder.boot</groupId> | 21 | <groupId>cn.iocoder.boot</groupId> |
| 22 | <artifactId>yudao-common</artifactId> | 22 | <artifactId>yudao-common</artifactId> |
| 23 | </dependency> | 23 | </dependency> |
| 24 | + | ||
| 25 | + <!-- 参数校验 --> | ||
| 26 | + <dependency> | ||
| 27 | + <groupId>org.springframework.boot</groupId> | ||
| 28 | + <artifactId>spring-boot-starter-validation</artifactId> | ||
| 29 | + <optional>true</optional> | ||
| 30 | + </dependency> | ||
| 24 | </dependencies> | 31 | </dependencies> |
| 25 | 32 | ||
| 26 | </project> | 33 | </project> |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.order; | ||
| 2 | + | ||
| 3 | +import javax.validation.Valid; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * @author LeeYan9 | ||
| 7 | + * @since 2022-08-26 | ||
| 8 | + */ | ||
| 9 | +public interface PayOrderApi { | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * 创建支付单 | ||
| 14 | + * | ||
| 15 | + * @param reqDTO 创建请求 | ||
| 16 | + * @return 支付单编号 | ||
| 17 | + */ | ||
| 18 | + Long createPayOrder(@Valid PayOrderDataCreateReqDTO reqDTO); | ||
| 19 | + | ||
| 20 | +} |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderDataCreateReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.order; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import org.hibernate.validator.constraints.Length; | ||
| 5 | + | ||
| 6 | +import javax.validation.constraints.DecimalMin; | ||
| 7 | +import javax.validation.constraints.NotEmpty; | ||
| 8 | +import javax.validation.constraints.NotNull; | ||
| 9 | +import java.io.Serializable; | ||
| 10 | +import java.util.Date; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 支付单创建 Request DTO | ||
| 14 | + * @author LeeYan9 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class PayOrderDataCreateReqDTO implements Serializable { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 应用编号 | ||
| 21 | + */ | ||
| 22 | + @NotNull(message = "应用编号不能为空") | ||
| 23 | + private Long appId; | ||
| 24 | + /** | ||
| 25 | + * 用户 IP | ||
| 26 | + */ | ||
| 27 | + @NotEmpty(message = "用户 IP 不能为空") | ||
| 28 | + private String userIp; | ||
| 29 | + | ||
| 30 | + // ========== 商户相关字段 ========== | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 商户订单编号 | ||
| 34 | + */ | ||
| 35 | + @NotEmpty(message = "商户订单编号不能为空") | ||
| 36 | + private String merchantOrderId; | ||
| 37 | + /** | ||
| 38 | + * 商品标题 | ||
| 39 | + */ | ||
| 40 | + @NotEmpty(message = "商品标题不能为空") | ||
| 41 | + @Length(max = 32, message = "商品标题不能超过 32") | ||
| 42 | + private String subject; | ||
| 43 | + /** | ||
| 44 | + * 商品描述 | ||
| 45 | + */ | ||
| 46 | + @NotEmpty(message = "商品描述信息不能为空") | ||
| 47 | + @Length(max = 128, message = "商品描述信息长度不能超过128") | ||
| 48 | + private String body; | ||
| 49 | + | ||
| 50 | + // ========== 订单相关字段 ========== | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 支付金额,单位:分 | ||
| 54 | + */ | ||
| 55 | + @NotNull(message = "支付金额不能为空") | ||
| 56 | + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") | ||
| 57 | + private Integer amount; | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * 支付过期时间 | ||
| 61 | + */ | ||
| 62 | + @NotNull(message = "支付过期时间不能为空") | ||
| 63 | + private Date expireTime; | ||
| 64 | + | ||
| 65 | +} | ||
| 0 | \ No newline at end of file | 66 | \ No newline at end of file |