From 01d10b518c79932b0e76f16ca163d87b22a85fec Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 8 Nov 2022 22:50:03 +0800 Subject: [PATCH] =?UTF-8?q?trade=EF=BC=9A=E4=BC=98=E5=8C=96=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=AE=A2=E5=8D=95=E6=8E=A5=E5=8F=A3=E7=9A=84=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/product/api/spu/ProductSpuApi.java | 4 +- ...nfoRespDTO.java => ProductSpuRespDTO.java} | 4 +- .../product/api/spu/ProductSpuApiImpl.java | 4 +- .../convert/spu/ProductSpuConvert.java | 4 +- .../promotion/enums/ErrorCodeConstants.java | 6 +- .../service/price/PriceServiceImpl.java | 3 +- .../trade/enums/ErrorCodeConstants.java | 10 +- .../yudao-module-trade-biz/pom.xml | 7 +- .../order/vo/AppTradeOrderCreateReqVO.java | 5 +- .../convert/order/TradeOrderConvert.java | 42 +++- .../convert/order/TradeOrderItemConvert.java | 17 -- .../dataobject/order/TradeOrderItemDO.java | 40 ++-- .../service/order/TradeOrderServiceImpl.java | 194 +++++++++++------- .../module/member/api/address/AddressApi.java | 21 ++ .../api/address/dto/AddressRespDTO.java | 48 +++++ .../member/api/address/AddressApiImpl.java | 28 +++ .../convert/address/AddressConvert.java | 14 +- 17 files changed, 311 insertions(+), 140 deletions(-) rename yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/{SpuInfoRespDTO.java => ProductSpuRespDTO.java} (97%) delete mode 100644 yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java create mode 100644 yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java create mode 100644 yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java create mode 100644 yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java index 461fbe935..8ba0fba7d 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.product.api.spu; -import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import java.util.Collection; import java.util.List; @@ -19,6 +19,6 @@ public interface ProductSpuApi { * @param ids SPU 编号列表 * @return SPU 数组 */ - List getSpuList(Collection ids); + List getSpuList(Collection ids); } diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java similarity index 97% rename from yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java rename to yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java index 92c20a0d7..45d42f41b 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java @@ -9,11 +9,13 @@ import java.util.List; // TODO @LeeYan9: ProductSpuRespDTO /** + * 商品 SPU 信息 Response DTO + * * @author LeeYan9 * @since 2022-08-26 */ @Data -public class SpuInfoRespDTO { +public class ProductSpuRespDTO { /** * 商品 SPU 编号,自增 diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java index 3ee034ce4..4d880e662 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java @@ -1,7 +1,7 @@ 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.ProductSpuRespDTO; 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; @@ -27,7 +27,7 @@ public class ProductSpuApiImpl implements ProductSpuApi { private ProductSpuMapper productSpuMapper; @Override - public List getSpuList(Collection spuIds) { + public List getSpuList(Collection spuIds) { // TODO TODO LeeYan9: AllEmpty? if (CollectionUtils.isAnyEmpty(spuIds)) { return Collections.emptyList(); diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java index de7adde73..98b7d8837 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.product.convert.spu; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 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.AppSpuPageRespVO; @@ -35,7 +35,7 @@ public interface ProductSpuConvert { AppSpuPageRespVO convertAppResp(ProductSpuDO list); - List convertList2(List list); + List convertList2(List list); List convertList02(List list); diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java index b9c4eee45..7e8fadede 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java @@ -22,17 +22,17 @@ public interface ErrorCodeConstants { // ========== Coupon 相关 1003003000 ============ ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1003003000, "优惠劵没有可使用的商品!"); - ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1003003000, "不满足优惠劵使用的最低金额"); + ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1003003000, "所结算的商品中未满足使用的金额"); // ========== 优惠劵模板 1003004000 ========== ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1003004000, "优惠劵模板不存在"); ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1003004001, "发放数量不能小于已领取数量({})"); // ========== 优惠劵模板 1003005000 ========== - ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1003005000, "优惠劵不存在"); + ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1003005000, "优惠券不存在"); ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1003005001, "回收优惠劵失败,优惠劵已被使用"); ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1006003003, "优惠劵不处于待使用状态"); - ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1006003004, "优惠劵不在有效期内"); + ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1006003004, "优惠券不在使用时间范围内"); // ========== 满减送活动 1003006000 ========== ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1003006000, "满减送活动不存在"); diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java index 8d68819e0..e384d1f81 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java @@ -116,7 +116,8 @@ public class PriceServiceImpl implements PriceService { assert originPrice != null; if (originPrice < coupon.getUsePrice()) { return couponMeetRespDTO.setMeet(false) - .setMeetTip(String.format("差 %s 元可用优惠劵", formatPrice(coupon.getUsePrice() - originPrice))); +// .setMeetTip(String.format("差 %s 元可用优惠劵", formatPrice(coupon.getUsePrice() - originPrice))); + .setMeetTip("所结算的商品中未满足使用的金额"); } } catch (ServiceException serviceException) { couponMeetRespDTO.setMeet(false); diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java index bb03bd8d0..8725ac58f 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java @@ -12,10 +12,12 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; 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, "商品库存不足"); + ErrorCode ORDER_CREATE_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品 SKU 不存在"); + ErrorCode ORDER_CREATE_SPU_NOT_SALE = new ErrorCode(1011000002, "商品 SPU 不可售卖"); + ErrorCode ORDER_CREATE_SKU_NOT_SALE = new ErrorCode(1011000003, "商品 SKU 不可售卖"); + ErrorCode ORDER_CREATE_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品 SKU 库存不足"); + ErrorCode ORDER_CREATE_SPU_NOT_FOUND = new ErrorCode(1011000005, "商品 SPU 不可售卖"); + ErrorCode ORDER_CREATE_ADDRESS_NOT_FOUND = new ErrorCode(1011000006, "收货地址不存在"); // ========== Cart 模块 1-011-001-000 ========== ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1001001000, "购物车项不存在"); diff --git a/yudao-module-mall/yudao-module-trade-biz/pom.xml b/yudao-module-mall/yudao-module-trade-biz/pom.xml index 30f108f64..d92ec23cd 100644 --- a/yudao-module-mall/yudao-module-trade-biz/pom.xml +++ b/yudao-module-mall/yudao-module-trade-biz/pom.xml @@ -29,18 +29,21 @@ yudao-module-product-api ${revision} - cn.iocoder.boot yudao-module-pay-api ${revision} - cn.iocoder.boot yudao-module-promotion-api ${revision} + + cn.iocoder.boot + yudao-module-member-api + ${revision} + diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java index 54629e543..5126de4a5 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java @@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.List; @@ -14,7 +15,7 @@ public class AppTradeOrderCreateReqVO { @ApiModelProperty(name = "收件地址编号", required = true, example = "1") @NotNull(message = "收件地址不能为空") - private Integer addressId; + private Long addressId; @ApiModelProperty(name = "优惠劵编号", example = "1024") private Long couponId; @@ -29,7 +30,7 @@ public class AppTradeOrderCreateReqVO { /** * 订单商品项列表 */ - @NotNull(message = "必须选择购买的商品") + @NotEmpty(message = "必须选择购买的商品") private List items; @ApiModel(value = "订单商品项") diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java index 9cdb58398..5b2bd8f22 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java @@ -1,24 +1,58 @@ package cn.iocoder.yudao.module.trade.convert.order; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import cn.iocoder.yudao.module.promotion.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 cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemRefundStatusEnum; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; + @Mapper public interface TradeOrderConvert { TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class); @Mappings({ - @Mapping(source = "order.couponId", target = "couponId"), + @Mapping(source = "createReqVO.couponId", target = "couponId"), @Mapping(target = "remark", ignore = true), - @Mapping(source = "createVO.remark", target = "userRemark"), - @Mapping(source = "createVO.addressId", target = "receiverAreaId") + @Mapping(source = "createReqVO.remark", target = "userRemark"), + @Mapping(source = "address.name", target = "receiverName"), + @Mapping(source = "address.mobile", target = "receiverMobile"), + @Mapping(source = "address.areaId", target = "receiverAreaId"), + @Mapping(source = "address.postCode", target = "receiverPostCode"), + @Mapping(source = "address.detailAddress", target = "receiverDetailAddress"), }) - TradeOrderDO convert(AppTradeOrderCreateReqVO createVO, PriceCalculateRespDTO.Order order); + TradeOrderDO convert(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO, + PriceCalculateRespDTO.Order order, AddressRespDTO address); + + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(source = "sku.spuId", target = "spuId"), + }) + TradeOrderItemDO convert(PriceCalculateRespDTO.OrderItem orderItem, ProductSkuRespDTO sku); + + default List convertList(TradeOrderDO tradeOrderDO, + List orderItems, List skus) { + Map skuMap = convertMap(skus, ProductSkuRespDTO::getId); + return CollectionUtils.convertList(orderItems, orderItem -> { + TradeOrderItemDO tradeOrderItemDO = convert(orderItem, skuMap.get(orderItem.getSkuId())); + tradeOrderItemDO.setOrderId(tradeOrderDO.getId()); + tradeOrderItemDO.setUserId(tradeOrderDO.getUserId()); + tradeOrderItemDO.setRefundStatus(TradeOrderItemRefundStatusEnum.NONE.getStatus()); +// tradeOrderItemDO.setCommented(false); + return tradeOrderItemDO; + }); + } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java deleted file mode 100644 index d65d63314..000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.trade.convert.order; - -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.List; - -@Mapper -public interface TradeOrderItemConvert { - - TradeOrderItemConvert INSTANCE = Mappers.getMapper(TradeOrderItemConvert.class); - - List convertList(List items); - -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java index 4ee710ef0..dfa4ed9af 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java @@ -70,13 +70,13 @@ public class TradeOrderItemDO extends BaseDO { * 购买数量 */ private Integer count; - /** - * 是否评论 TODO - * - * false - 未评论 - * true - 已评论 - */ - private Boolean commented; +// /** +// * 是否评论 TODO +// * +// * false - 未评论 +// * true - 已评论 +// */ +// private Boolean commented; // ========== 价格 + 支付基本信息 ========== @@ -138,19 +138,19 @@ public class TradeOrderItemDO extends BaseDO { * 枚举 {@link TradeOrderItemRefundStatusEnum} */ private Integer refundStatus; // TODO 芋艿:可以考虑去查 - // 如上字段,举个例子: - // 假设购买三个,即 stock = 3 。 - // originPrice = 15 - // 使用限时折扣(单品优惠)8 折,buyPrice = 12 - // 开始算总的价格 - // buyTotal = buyPrice * stock = 12 * 3 = 36 - // discountTotal ,假设有满减送(分组优惠)满 20 减 10 ,并且使用优惠劵满 1.01 减 1 ,则 discountTotal = 10 + 1 = 11 - // presentTotal = buyTotal - discountTotal = 24 - 11 = 13 - // 最终 presentPrice = presentTotal / stock = 13 / 3 = 4.33 - /** - * 退款总金额,单位:分 TODO - */ - private Integer refundTotal; +// // 如上字段,举个例子: +// // 假设购买三个,即 stock = 3 。 +// // originPrice = 15 +// // 使用限时折扣(单品优惠)8 折,buyPrice = 12 +// // 开始算总的价格 +// // buyTotal = buyPrice * stock = 12 * 3 = 36 +// // discountTotal ,假设有满减送(分组优惠)满 20 减 10 ,并且使用优惠劵满 1.01 减 1 ,则 discountTotal = 10 + 1 = 11 +// // presentTotal = buyTotal - discountTotal = 24 - 11 = 13 +// // 最终 presentPrice = presentTotal / stock = 13 / 3 = 4.33 +// /** +// * 退款总金额,单位:分 TODO +// */ +// private Integer refundTotal; /** * 商品属性 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java index 9b7b877fa..59e08987f 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java @@ -3,29 +3,27 @@ package cn.iocoder.yudao.module.trade.service.order; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.text.StrBuilder; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; 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.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.string.StrUtils; -import cn.iocoder.yudao.module.promotion.api.price.PriceApi; -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.member.api.address.AddressApi; +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; 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.dto.ProductSkuRespDTO; -import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; 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.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import cn.iocoder.yudao.module.promotion.api.price.PriceApi; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; 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; @@ -43,6 +41,12 @@ import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_SKU_NOT_SALE; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_SPU_NOT_FOUND; /** * 交易订单 Service 实现类 @@ -70,66 +74,51 @@ public class TradeOrderServiceImpl implements TradeOrderService { private ProductSpuApi productSpuApi; @Resource private PayOrderApi payOrderApi; + @Resource + private AddressApi addressApi; @Resource private TradeOrderProperties tradeOrderProperties; @Override @Transactional(rollbackFor = Exception.class) - public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) { - List items = createReqVO.getItems(); - // 商品SKU检查 sku可售状态,库存 - List skuInfos = productSkuApi.getSkuList(CollectionUtils.convertSet(items, Item::getSkuId)); - Map skuInfoMap = CollectionUtils.convertMap(skuInfos, ProductSkuRespDTO::getId); - checkSaleableAndStockFromSpu(skuInfoMap, items); - - // 商品SPU检查 sku可售状态,库存 - List spuInfos = productSpuApi.getSpuList(CollectionUtils.convertSet(skuInfos, ProductSkuRespDTO::getSpuId)); - checkSaleableFromSpu(spuInfos); + public Long createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO) { + // 商品 SKU 检查:可售状态、库存 + List skus = validateSkuSaleable(createReqVO.getItems()); + // 商品 SPU 检查:可售状态 + List spus = validateSpuSaleable(convertSet(skus, ProductSkuRespDTO::getSpuId)); + // 用户收件地址的校验 + AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId()); // 价格计算 - PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, loginUserId); + PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, userId); PriceCalculateRespDTO priceResp = priceApi.calculatePrice(priceCalculateReqDTO); - // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! - // 订单信息记录 - TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(createReqVO, priceResp.getOrder()); - fillTradeOrderInfoFromReqInfo(tradeOrderDO,createReqVO,loginUserId, clientIp); // TODO @LeeYan9: tradeOrderDO, createReqVO, loginUserId, clientIp - tradeOrderMapper.insert(tradeOrderDO); + // 插入 TradeOrderDO 订单 + TradeOrderDO tradeOrderDO = createTradeOrder(userId, clientIp, createReqVO, priceResp.getOrder(), address); + // 插入 TradeOrderItemDO 订单项 + createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus); - // 订单项信息记录 - List tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(priceResp.getOrder().getItems()); - // 填充订单项-SKU信息 - fillItemsInfoFromSkuAndOrder(tradeOrderDO, tradeOrderItems, skuInfoMap); - tradeOrderItemMapper.insertBatch(tradeOrderItems); + // 下单时扣减商品库存 TODO +// List skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems); +// productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems)); - // TODO @LeeYan9: 先扣减库存哈; 可能会扣减失败; 毕竟 get 和 update 之间, 会有并发的可能性 - // 库存扣减 - List skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems); - productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems)); + // 删除购物车商品 TODO 芋艿:待实现 + + // 扣减积分,抵扣金额 TODO 芋艿:待实现 + + // 有使用优惠券时更新 + + // 增加订单日志 TODO 芋艿:待实现 // 构建预支付请求参数 // TODO @LeeYan9: 需要更新到订单上 - PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO); - fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems); +// PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO); +// fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems); // 生成预支付 - return payOrderApi.createPayOrder(payOrderCreateReqDTO); - } - // TODO @LeeYan9: 填充就好, 不用 from 哈; - private void fillTradeOrderInfoFromReqInfo(TradeOrderDO tradeOrderDO, AppTradeOrderCreateReqVO createReqVO, - Long loginUserId, String clientIp) { - tradeOrderDO.setUserId(loginUserId); - tradeOrderDO.setUserIp(clientIp); - tradeOrderDO.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的; - 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()); + // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! + return tradeOrderDO.getId(); } private void fillPayOrderInfoFromItems(PayOrderInfoCreateReqDTO payOrderInfoCreateReqDTO, @@ -157,7 +146,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { payOrderInfoCreateReqDTO.setBody(StrUtils.maxLength(body.subString(1), 128)); } - private void fillItemsInfoFromSkuAndOrder(TradeOrderDO tradeOrderDO, List tradeOrderItems, + private void xfillItemsInfoFromSkuAndOrder(TradeOrderDO tradeOrderDO, List tradeOrderItems, Map spuInfos) { for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) { // 填充订单信息 @@ -176,32 +165,87 @@ public class TradeOrderServiceImpl implements TradeOrderService { } } - private void checkSaleableFromSpu(List spuInfos) { - SpuInfoRespDTO spu = CollectionUtils.findFirst(spuInfos, - spuInfoDTO -> !Objects.equals(ProductSpuStatusEnum.ENABLE.getStatus(), spuInfoDTO.getStatus())); + /** + * 校验商品 SKU 是否可出售 + * + * @param items 商品 SKU + * @return 商品 SKU 数组 + */ + private List validateSkuSaleable(List items) { + List skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId)); + // SKU 不存在 + if (items.size() != skus.size()) { + throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_NOT_FOUND); + } + // 校验是否禁用 or 库存不足 + Map skuMap = convertMap(skus, ProductSkuRespDTO::getId); + items.forEach(item -> { + ProductSkuRespDTO sku = skuMap.get(item.getSkuId()); + // SKU 禁用 + if (ObjectUtil.notEqual(CommonStatusEnum.ENABLE.getStatus(), sku.getStatus())) { + throw exception(ORDER_CREATE_SKU_NOT_SALE); + } + // SKU 库存不足 + if (item.getCount() > sku.getStock()) { + throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH); + } + }); + return skus; + } + + /** + * 校验商品 SPU 是否可出售 + * + * @param spuIds 商品 SPU 编号数组 + * @return 商品 SPU 数组 + */ + private List validateSpuSaleable(Set spuIds) { + List spus = productSpuApi.getSpuList(spuIds); + // SPU 不存在 + if (spus.size() != spuIds.size()) { + throw exception(ORDER_CREATE_SPU_NOT_FOUND); + } + // 校验是否存在禁用的 SPU + ProductSpuRespDTO spu = CollectionUtils.findFirst(spus, + spuDTO -> ObjectUtil.notEqual(ProductSpuStatusEnum.ENABLE.getStatus(), spuDTO.getStatus())); if (Objects.isNull(spu)) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SPU_NOT_SALE); + throw exception(ErrorCodeConstants.ORDER_CREATE_SPU_NOT_SALE); } + return spus; } - // TODO @LeeYan9: checkSpuXXX 会不会好点哈? ps: 这个貌似是 sku 哈 - private void checkSaleableAndStockFromSpu(Map skuInfoMap, - List items) { - // sku 不存在 - if (items.size() != skuInfoMap.size()) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_FOUND); + /** + * 校验收件地址是否存在 + * + * @param userId 用户编号 + * @param addressId 收件地址编号 + * @return 收件地址 + */ + private AddressRespDTO validateAddress(Long userId, Long addressId) { + AddressRespDTO address = addressApi.getAddress(userId, addressId); + if (Objects.isNull(address)) { + throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND); } - for (Item item : items) { - ProductSkuRespDTO 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); - } - } - + return address; } + + private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO, + PriceCalculateRespDTO.Order order, AddressRespDTO address) { + TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, order, address); + tradeOrderDO.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的; + tradeOrderDO.setStatus(TradeOrderStatusEnum.WAITING_PAYMENT.getStatus()); + tradeOrderDO.setType(TradeOrderTypeEnum.NORMAL.getType()); + tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); + tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum)); + tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源? + tradeOrderMapper.insert(tradeOrderDO); + return tradeOrderDO; + } + + private void createTradeOrderItems(TradeOrderDO tradeOrderDO, + List orderItems, List skus) { + List tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus); + tradeOrderItemMapper.insertBatch(tradeOrderItemDOs); + } + } diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java new file mode 100644 index 000000000..75ba02563 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.member.api.address; + +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; + +/** + * 用户收件地址 API 接口 + * + * @author 芋道源码 + */ +public interface AddressApi { + + /** + * 获得用户收件地址 + * + * @param id 收件地址编号 + * @param userId 用户编号 + * @return 用户收件地址 + */ + AddressRespDTO getAddress(Long id, Long userId); + +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java new file mode 100644 index 000000000..cc8eb4701 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.member.api.address.dto; + +import lombok.Data; + +/** + * 用户收件地址 Response DTO + * + * @author 芋道源码 + */ +@Data +public class AddressRespDTO { + + /** + * 编号 + */ + private Long id; + /** + * 用户编号 + */ + private Long userId; + /** + * 收件人名称 + */ + private String name; + /** + * 手机号 + */ + private String mobile; + /** + * 地区编号 + */ + private Long areaId; + /** + * 邮编 + */ + private String postCode; + /** + * 收件详细地址 + */ + private String detailAddress; + /** + * 是否默认 + * + * true - 默认收件地址 + */ + private Boolean defaulted; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java new file mode 100644 index 000000000..fd0f4843d --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.member.api.address; + +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.service.address.AddressService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +/** + * 用户收件地址 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AddressApiImpl implements AddressApi { + + @Resource + private AddressService addressService; + + @Override + public AddressRespDTO getAddress(Long id, Long userId) { + return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java index fdc01e5a8..49a01805d 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java @@ -1,13 +1,15 @@ package cn.iocoder.yudao.module.member.convert.address; -import java.util.*; - import cn.iocoder.yudao.framework.common.pojo.PageResult; - +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressRespVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.member.controller.app.address.vo.*; -import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; + +import java.util.List; /** * 用户收件地址 Convert @@ -29,4 +31,6 @@ public interface AddressConvert { PageResult convertPage(PageResult page); + AddressRespDTO convert02(AddressDO bean); + }