From ed71f5e9c84d0f6556a576a83f47e42e0132d387 Mon Sep 17 00:00:00 2001 From: YunaiV <> Date: Wed, 12 Aug 2020 20:05:48 +0800 Subject: [PATCH] =?UTF-8?q?Price=20=E4=BB=B7=E6=A0=BC=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=9A=84=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../convert/admin/AdminConvert.java | 2 +- .../manager/admin/AdminManager.java | 2 +- .../mall/order/api/bo/CalcOrderPriceBO.java | 1 + .../api/constant/OrderErrorCodeEnum.java | 1 - .../mall/order/api/dto/CalcOrderPriceDTO.java | 1 + .../order/biz/service/CartServiceImpl.java | 64 +------ .../sku/dto/ProductSkuListQueryReqDTO.java | 6 + .../mysql/mapper/sku/ProductSkuMapper.java | 1 + .../service/sku/bo/ProductSkuListQueryBO.java | 6 + .../enums/PromotionActivityStatusEnum.java | 2 +- .../enums/PromotionErrorCodeConstants.java | 3 + .../dto/PromotionActivityRespDTO.java | 3 + .../promotion/api/rpc/price/PriceRpc.java | 14 ++ .../rpc/price/dto/PriceProductCalcReqDTO.java | 60 ++++++ .../price/dto/PriceProductCalcRespDTO.java | 175 ++++++++++++++++++ .../promotion-service-app/pom.xml | 7 + .../activity/PromotionActivityConvert.java | 2 + .../activity/PromotionActivityDO.java | 11 +- .../activity/PromotionActivityMapper.java | 12 +- .../manager/package-info.java | 1 + .../manager/price/PriceManager.java | 110 +++++++++++ .../activity/PromotionActivityService.java | 25 ++- .../activity/PromotionActivityMapperTest.java | 28 +++ .../mall/promotionservice/package-info.java | 1 + 24 files changed, 457 insertions(+), 81 deletions(-) create mode 100644 promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/PriceRpc.java create mode 100644 promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcReqDTO.java create mode 100644 promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcRespDTO.java create mode 100644 promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/package-info.java create mode 100644 promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/price/PriceManager.java create mode 100644 promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapperTest.java create mode 100644 promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/package-info.java diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/admin/AdminConvert.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/admin/AdminConvert.java index a79613147..1b5ce7db5 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/admin/AdminConvert.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/admin/AdminConvert.java @@ -36,6 +36,6 @@ public interface AdminConvert { AdminPageItemVO convert02(AdminVO adminVO); AdminPageItemVO.Department convert(DepartmentVO bean); - List convert(List list); + List convertList(List list); } diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java index 2d6d2bae5..363b3674b 100644 --- a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java +++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminManager.java @@ -62,7 +62,7 @@ public class AdminManager { // 拼接部门 adminPageItemVO.setDepartment(AdminConvert.INSTANCE.convert(departmentMap.get(adminVO.getDepartmentId()))); // 拼接角色 - adminPageItemVO.setRoles( AdminConvert.INSTANCE.convert(adminRoleMap.get(adminVO.getId()))); + adminPageItemVO.setRoles( AdminConvert.INSTANCE.convertList(adminRoleMap.get(adminVO.getId()))); } } else { adminPageVO.setList(Collections.emptyList()); diff --git a/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/bo/CalcOrderPriceBO.java b/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/bo/CalcOrderPriceBO.java index 2dd378cfd..630785561 100644 --- a/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/bo/CalcOrderPriceBO.java +++ b/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/bo/CalcOrderPriceBO.java @@ -12,6 +12,7 @@ import java.util.List; */ @Data @Accessors(chain = true) +@Deprecated public class CalcOrderPriceBO { /** diff --git a/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/constant/OrderErrorCodeEnum.java b/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/constant/OrderErrorCodeEnum.java index c6806757a..2e98abd34 100644 --- a/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/constant/OrderErrorCodeEnum.java +++ b/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/constant/OrderErrorCodeEnum.java @@ -30,7 +30,6 @@ public enum OrderErrorCodeEnum { // order item ORDER_ITEM_ONLY_ONE(1008000200, "订单Item只有一个!"), - ORDER_ITEM_SOME_NOT_EXISTS(1008000201, "有不存在的商品!"), // 订单退货 ORDER_RETURN_NO_RETURN_APPLY(1008000400, "未退货申请"), diff --git a/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/dto/CalcOrderPriceDTO.java b/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/dto/CalcOrderPriceDTO.java index a40e35de4..80ea92859 100644 --- a/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/dto/CalcOrderPriceDTO.java +++ b/order/order-service-api02/src/main/java/cn/iocoder/mall/order/api/dto/CalcOrderPriceDTO.java @@ -11,6 +11,7 @@ import java.util.List; */ @Data @Accessors(chain = true) +@Deprecated public class CalcOrderPriceDTO { @NotNull(message = "用户编号不能为空") diff --git a/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/CartServiceImpl.java b/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/CartServiceImpl.java index 4dba7aa9a..fdaae5d32 100644 --- a/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/CartServiceImpl.java +++ b/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/CartServiceImpl.java @@ -47,25 +47,8 @@ public class CartServiceImpl implements CartService { @Override public CalcOrderPriceBO calcOrderPrice(CalcOrderPriceDTO calcOrderPriceDTO) { - // TODO 芋艿,补充一些表单校验。例如说,需要传入用户编号。 - // 校验商品都存在 - Map calcOrderItemMap = calcOrderPriceDTO.getItems().stream() - .collect(Collectors.toMap(CalcOrderPriceDTO.Item::getSkuId, item -> item)); // KEY:skuId - List skus = productSpuService.getProductSkuDetailList(calcOrderItemMap.keySet()); - if (skus.size() != calcOrderPriceDTO.getItems().size()) { - throw ServiceExceptionUtil.exception(OrderErrorCodeEnum.ORDER_ITEM_SOME_NOT_EXISTS.getCode()); - } - // TODO 库存相关 - // 查询促销活动 - List activityList = promotionActivityService.getPromotionActivityListBySpuIds( - skus.stream().map(sku -> sku.getSpu().getId()).collect(Collectors.toSet()), - Collections.singletonList(PromotionActivityStatusEnum.RUN.getValue())); - // 拼装结果(主要是计算价格) - CalcOrderPriceBO calcOrderPriceBO = new CalcOrderPriceBO(); - // 1. 创建初始的每一项的数组 - List items = initCalcOrderPriceItems(skus, calcOrderItemMap); // 2. 计算【限时折扣】促销 - modifyPriceByTimeLimitDiscount(items, activityList); + // 3. 计算【满减送】促销 List itemGroups = groupByFullPrivilege(items, activityList); calcOrderPriceBO.setItemGroups(itemGroups); @@ -114,52 +97,7 @@ public class CartServiceImpl implements CartService { .setOriginalPrice(sku.getPrice()).setBuyPrice(presentPrice); } - private List initCalcOrderPriceItems(List skus, - Map calcOrderItemMap) { - List items = new ArrayList<>(); - for (ProductSkuDetailBO sku : skus) { - CalcOrderPriceBO.Item item = CartConvert.INSTANCE.convert(sku); - items.add(item); - // 将是否选中,购物数量,复制到 item 中 - CalcOrderPriceDTO.Item calcOrderItem = calcOrderItemMap.get(sku.getId()); - item.setSelected(calcOrderItem.getSelected()); - item.setBuyQuantity(calcOrderItem.getQuantity()); - // 计算初始价格 - item.setOriginPrice(sku.getPrice()); - item.setBuyPrice(sku.getPrice()); - item.setPresentPrice(sku.getPrice()); - item.setBuyTotal(sku.getPrice() * calcOrderItem.getQuantity()); - item.setDiscountTotal(0); - item.setPresentTotal(item.getBuyTotal()); - } - return items; - } - private void modifyPriceByTimeLimitDiscount(List items, List activityList) { - for (CalcOrderPriceBO.Item item : items) { - // 获得符合条件的限时折扣 - PromotionActivityBO timeLimitedDiscount = activityList.stream() - .filter(activity -> PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType()) - && activity.getTimeLimitedDiscount().getItems().stream().anyMatch(item0 -> item0.getSpuId().equals(item.getSpu().getId()))) - .findFirst().orElse(null); - if (timeLimitedDiscount == null) { - continue; - } - // 计算价格 - ProductSkuBO sku = new ProductSkuBO().setId(item.getId()).setSpuId(item.getSpu().getId()).setPrice(item.getPrice()); - Integer newPrice = calcSkuPriceByTimeLimitDiscount(sku, timeLimitedDiscount); - if (newPrice.equals(item.getPrice())) { - continue; - } - // 设置优惠 - item.setActivity(timeLimitedDiscount); - // 设置价格 - item.setBuyPrice(newPrice); - item.setBuyTotal(newPrice * item.getBuyQuantity()); - item.setPresentTotal(item.getBuyTotal() - item.getDiscountTotal()); - item.setPresentPrice(item.getPresentTotal() / item.getBuyQuantity()); - } - } private List groupByFullPrivilege(List items, List activityList) { List itemGroups = new ArrayList<>(); diff --git a/product-service-project/product-service-api/src/main/java/cn/iocoder/mall/productservice/rpc/sku/dto/ProductSkuListQueryReqDTO.java b/product-service-project/product-service-api/src/main/java/cn/iocoder/mall/productservice/rpc/sku/dto/ProductSkuListQueryReqDTO.java index 9243f8ecd..401a1d125 100644 --- a/product-service-project/product-service-api/src/main/java/cn/iocoder/mall/productservice/rpc/sku/dto/ProductSkuListQueryReqDTO.java +++ b/product-service-project/product-service-api/src/main/java/cn/iocoder/mall/productservice/rpc/sku/dto/ProductSkuListQueryReqDTO.java @@ -4,6 +4,7 @@ import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; +import java.util.Collection; /** * 商品 SKU 列表查询 DTO @@ -16,9 +17,14 @@ public class ProductSkuListQueryReqDTO implements Serializable { * 商品 SKU 编号 */ private Integer productSkuId; + /** + * 商品 SKU 编号数组 + */ + private Collection productSkuIds; /** * 商品 SPU 编号 */ private Integer productSpuId; + } diff --git a/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/dal/mysql/mapper/sku/ProductSkuMapper.java b/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/dal/mysql/mapper/sku/ProductSkuMapper.java index 87989a62c..a688109e2 100644 --- a/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/dal/mysql/mapper/sku/ProductSkuMapper.java +++ b/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/dal/mysql/mapper/sku/ProductSkuMapper.java @@ -21,6 +21,7 @@ public interface ProductSkuMapper extends BaseMapper { default List selectList(ProductSkuListQueryBO queryBO) { return selectList(new QueryWrapperX().eqIfPresent("id", queryBO.getProductSkuId()) + .inIfPresent("id", queryBO.getProductSkuIds()) .eqIfPresent("spu_id", queryBO.getProductSpuId()) .eqIfPresent("status", queryBO.getProductSkuStatus())); } diff --git a/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/service/sku/bo/ProductSkuListQueryBO.java b/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/service/sku/bo/ProductSkuListQueryBO.java index 076a39abd..c7e2c67ce 100644 --- a/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/service/sku/bo/ProductSkuListQueryBO.java +++ b/product-service-project/product-service-app/src/main/java/cn/iocoder/mall/productservice/service/sku/bo/ProductSkuListQueryBO.java @@ -3,6 +3,8 @@ package cn.iocoder.mall.productservice.service.sku.bo; import lombok.Data; import lombok.experimental.Accessors; +import java.util.Collection; + /** * 商品 SKU 列表查询 BO */ @@ -14,6 +16,10 @@ public class ProductSkuListQueryBO { * 商品 SKU 编号 */ private Integer productSkuId; + /** + * 商品 SKU 编号数组 + */ + private Collection productSkuIds; /** * 商品 SPU 编号 */ diff --git a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionActivityStatusEnum.java b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionActivityStatusEnum.java index 25786a186..2b22fb7a0 100644 --- a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionActivityStatusEnum.java +++ b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionActivityStatusEnum.java @@ -3,7 +3,7 @@ package cn.iocoder.mall.promotion.api.enums; /** * 促销活动状态枚举 */ -public enum PromotionActivityStatusEnum { +public enum PromotionActivityStatusEnum { WAIT(10, "未开始"), RUN(20, "进行中"), diff --git a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionErrorCodeConstants.java b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionErrorCodeConstants.java index 162cc0d07..813a56432 100644 --- a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionErrorCodeConstants.java +++ b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/enums/PromotionErrorCodeConstants.java @@ -34,4 +34,7 @@ public interface PromotionErrorCodeConstants { ErrorCode COUPON_CARD_STATUS_NOT_UNUSED = new ErrorCode(1006003003, "优惠劵不处于待使用状态"); ErrorCode COUPON_CARD_STATUS_NOT_USED = new ErrorCode( 1006003004, "优惠劵不处于已使用状态"); + // ========== PRICE 模块 ========== + ErrorCode PRICE_PRODUCT_SKU_NOT_EXISTS = new ErrorCode(1006004000, "有不存在的商品!"); + } diff --git a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/activity/dto/PromotionActivityRespDTO.java b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/activity/dto/PromotionActivityRespDTO.java index d268ce73f..6d2978fd5 100644 --- a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/activity/dto/PromotionActivityRespDTO.java +++ b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/activity/dto/PromotionActivityRespDTO.java @@ -7,6 +7,9 @@ import java.io.Serializable; import java.util.Date; import java.util.List; +/** + * 促销伙伴 Response DTO + */ @Data @Accessors(chain = true) public class PromotionActivityRespDTO implements Serializable { diff --git a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/PriceRpc.java b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/PriceRpc.java new file mode 100644 index 000000000..1bcd9d055 --- /dev/null +++ b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/PriceRpc.java @@ -0,0 +1,14 @@ +package cn.iocoder.mall.promotion.api.rpc.price; + +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 价格 Rpc 接口,提供价格计算的功能 + */ +public class PriceRpc { + +} diff --git a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcReqDTO.java b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcReqDTO.java new file mode 100644 index 000000000..1809ce568 --- /dev/null +++ b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcReqDTO.java @@ -0,0 +1,60 @@ +package cn.iocoder.mall.promotion.api.rpc.price.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +/** + * 商品价格计算 Request DTO + */ +@Data +@Accessors +public class PriceProductCalcReqDTO implements Serializable { + + /** + * 用户编号 + */ + @NotNull(message = "用户编号不能为空") + private Integer userId; + + /** + * 优惠劵编号 + */ + private Integer couponCardId; + + /** + * 商品 SKU 数组 + */ + @NotNull(message = "商品数组不能为空") + private List items; + + /** + * 商品 SKU + */ + @Data + @Accessors(chain = true) + public static class Item { + + /** + * SKU 编号 + */ + private Integer skuId; + /** + * 数量 + */ + private Integer quantity; + + public Item() { + } + + public Item(Integer skuId, Integer quantity) { + this.skuId = skuId; + this.quantity = quantity; + } + + } + +} diff --git a/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcRespDTO.java b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcRespDTO.java new file mode 100644 index 000000000..10e51d387 --- /dev/null +++ b/promotion-service-project/promotion-service-api/src/main/java/cn/iocoder/mall/promotion/api/rpc/price/dto/PriceProductCalcRespDTO.java @@ -0,0 +1,175 @@ +package cn.iocoder.mall.promotion.api.rpc.price.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 商品价格计算 Request DTO + */ +@Data +@Accessors(chain = true) +public class PriceProductCalcRespDTO implements Serializable { + + /** + * 商品分组数组 + */ + private List itemGroups; + /** + * 优惠劵编号 + */ + private Integer couponCardId; + /** + * 优惠劵减少的金额 + * + * 1. 若未使用优惠劵,返回 null + * 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。 + */ + private Integer couponCardDiscountTotal; + /** + * 邮费信息 + * + * TODO 芋艿,暂时未弄 + */ + private Postage postage; + /** + * 费用 + */ + private Fee fee; + + /** + * 商品分组 + * + * 多个商品,参加同一个活动,从而形成分组。 + */ + @Data + @Accessors(chain = true) + public static class ItemGroup { + +// /** +// * 优惠活动 +// */ +// // TODO 芋艿,目前只会有【满减送】的情况,未来有新的促销方式,可能需要改成数组 +// private PromotionActivityBO activity; + /** + * 促销减少的金额 + * + * 1. 若未参与促销活动,或不满足促销条件,返回 null + * 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。 + */ + private Integer activityDiscountTotal; + /** + * 商品数组 + */ + private List items; +// /** +// * 费用 +// * +// * TODO 芋艿,这里先偷懒,postageTotal 字段用不到。 +// */ +// private Fee fee; // 注释原因,不用这里了 + + } + + @Data + @Accessors(chain = true) + public static class Item { + + /** + * 购买数量 + */ + private Integer buyQuantity; +// /** +// * 优惠活动 +// */ +// private PromotionActivityBO activity; + /** + * 原始单价,单位:分。 + */ + private Integer originPrice; + /** + * 购买单价,单位:分 + */ + private Integer buyPrice; + /** + * 最终价格,单位:分。 + */ + private Integer presentPrice; + /** + * 购买总金额,单位:分 + * + * 用途类似 {@link #presentTotal} + */ + private Integer buyTotal; + /** + * 优惠总金额,单位:分。 + */ + private Integer discountTotal; + /** + * 最终总金额,单位:分。 + * + * 注意,presentPrice * quantity 不一定等于 presentTotal 。 + * 因为,存在无法整除的情况。 + * 举个例子,presentPrice = 8.33 ,quantity = 3 的情况,presentTotal 有可能是 24.99 ,也可能是 25 。 + * 所以,需要存储一个该字段。 + */ + private Integer presentTotal; + + } + + /** + * 费用(合计) + */ + @Data + @Accessors(chain = true) + public static class Fee { + + /** + * 购买总价 + */ + private Integer buyTotal; + /** + * 优惠总价 + * + * 注意,满多少元包邮,不算在优惠中。 + */ + private Integer discountTotal; + /** + * 邮费 TODO 芋艿,将 postage 改成 logistics + */ + private Integer postageTotal; + /** + * 最终价格 + * + * 计算公式 = 总价 - 优惠总价 + 邮费 + */ + private Integer presentTotal; + + public Fee() { + } + + public Fee(Integer buyTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) { + this.buyTotal = buyTotal; + this.discountTotal = discountTotal; + this.postageTotal = postageTotal; + this.presentTotal = presentTotal; + } + } + + /** + * 邮费信息 + */ + @Data + @Accessors(chain = true) + public static class Postage { + + /** + * 需要满足多少钱,可以包邮。单位:分 + */ + private Integer threshold; + + } + +} diff --git a/promotion-service-project/promotion-service-app/pom.xml b/promotion-service-project/promotion-service-app/pom.xml index 3866bfaa3..b461ab7a0 100644 --- a/promotion-service-project/promotion-service-app/pom.xml +++ b/promotion-service-project/promotion-service-app/pom.xml @@ -66,6 +66,13 @@ mall-spring-boot-starter-mybatis + + + org.springframework.boot + spring-boot-starter-test + test + + org.springframework.boot diff --git a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/convert/activity/PromotionActivityConvert.java b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/convert/activity/PromotionActivityConvert.java index 3e0f5b053..e9594c43a 100644 --- a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/convert/activity/PromotionActivityConvert.java +++ b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/convert/activity/PromotionActivityConvert.java @@ -26,6 +26,8 @@ public interface PromotionActivityConvert { @Mappings({}) List convertToRespDTO(List activityList); + List convertList(List list); + // @Mappings({}) // PromotionActivityDO convert(PromotionActivityAddDTO activityAddDTO); // diff --git a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/dataobject/activity/PromotionActivityDO.java b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/dataobject/activity/PromotionActivityDO.java index 2cca869a1..2096e94a6 100644 --- a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/dataobject/activity/PromotionActivityDO.java +++ b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/dataobject/activity/PromotionActivityDO.java @@ -1,8 +1,11 @@ package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity; import cn.iocoder.mall.mybatis.core.dataobject.BaseDO; -import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import java.util.Date; @@ -11,9 +14,11 @@ import java.util.List; /** * 促销活动 DO */ +@TableName(value = "promotion_activity", autoResultMap = true) @Data +@EqualsAndHashCode(callSuper = true) @Accessors(chain = true) -public class PromotionActivityDO extends DeletableDO { +public class PromotionActivityDO extends BaseDO { /** * 活动编号 @@ -59,10 +64,12 @@ public class PromotionActivityDO extends DeletableDO { /** * 限制折扣字符串,使用 JSON 序列化成字符串存储 */ + @TableField(typeHandler = FastjsonTypeHandler.class) private TimeLimitedDiscount timeLimitedDiscount; /** * 满减送字符串,使用 JSON 序列化成字符串存储 */ + @TableField(typeHandler = FastjsonTypeHandler.class) private FullPrivilege fullPrivilege; /** diff --git a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapper.java b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapper.java index 0f0cfcdd2..e987a11de 100644 --- a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapper.java +++ b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapper.java @@ -1,6 +1,8 @@ package cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity; import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @@ -8,13 +10,11 @@ import java.util.Collection; import java.util.List; @Repository -public interface PromotionActivityMapper { +public interface PromotionActivityMapper extends BaseMapper { - PromotionActivityDO selectById(@Param("id") Integer id); - - List selectListByStatus(@Param("statuses") Collection statuses); - - void insert(PromotionActivityDO activity); + default List selectListByStatus(@Param("statuses") Collection statuses) { + return selectList(new QueryWrapper().in("status", statuses)); + } List selectListByPage(@Param("title") String title, @Param("activityType") Integer activityType, diff --git a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/package-info.java b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/package-info.java new file mode 100644 index 000000000..d21a6c1f3 --- /dev/null +++ b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.promotionservice.manager; diff --git a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/price/PriceManager.java b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/price/PriceManager.java new file mode 100644 index 000000000..198068f0b --- /dev/null +++ b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/manager/price/PriceManager.java @@ -0,0 +1,110 @@ +package cn.iocoder.mall.promotionservice.manager.price; + +import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil; +import cn.iocoder.common.framework.util.CollectionUtils; +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.productservice.rpc.sku.ProductSkuRpc; +import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuListQueryReqDTO; +import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO; +import cn.iocoder.mall.promotion.api.enums.PromotionActivityStatusEnum; +import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO; +import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO; +import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO; +import cn.iocoder.mall.promotionservice.service.activity.PromotionActivityService; +import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants.PRICE_PRODUCT_SKU_NOT_EXISTS; + +@Service +@Validated +public class PriceManager { + + @DubboReference(version = "${dubbo.consumer.ProductSkuRpc.version}") + private ProductSkuRpc productSkuRpc; + + @Autowired + private PromotionActivityService promotionActivityService; + + public PriceProductCalcRespDTO calcProductPrice(PriceProductCalcReqDTO calcReqDTO) { + // TODO 芋艿,补充一些表单校验。例如说,需要传入用户编号。 + // 校验商品都存在 + Map calcProductItemDTOMap = CollectionUtils.convertMap( + calcReqDTO.getItems(), PriceProductCalcReqDTO.Item::getSkuId); + CommonResult> listProductSkusResult = productSkuRpc.listProductSkus( + new ProductSkuListQueryReqDTO().setProductSkuIds(calcProductItemDTOMap.keySet())); + listProductSkusResult.checkError(); + if (calcReqDTO.getItems().size() != listProductSkusResult.getData().size()) { + throw ServiceExceptionUtil.exception(PRICE_PRODUCT_SKU_NOT_EXISTS); + } + // TODO 库存相关 + // 查询促销活动 + List activityRespDTOs = promotionActivityService.listPromotionActivitiesBySpuIds( + calcProductItemDTOMap.keySet(), Collections.singleton(PromotionActivityStatusEnum.RUN.getValue())); + // 拼装结果(主要是计算价格) + PriceProductCalcRespDTO calcRespDTO = new PriceProductCalcRespDTO(); + // 1. 创建初始的每一项的数组 + List calcItemRespDTOs = initCalcOrderPriceItems( + listProductSkusResult.getData(), calcProductItemDTOMap); + // 2. 计算【限时折扣】促销 +// modifyPriceByTimeLimitDiscount(items, activityList); + // 3. 计算【满减送】促销 + // 4. 计算优惠劵 + // 5. 计算最终的价格 + return null; + } + + private List initCalcOrderPriceItems(List skus, + Map calcProductItemDTOMap) { + List items = new ArrayList<>(); + for (ProductSkuRespDTO sku : skus) { + PriceProductCalcRespDTO.Item item = new PriceProductCalcRespDTO.Item(); + items.add(item); + // 将是否选中,购物数量,复制到 item 中 + PriceProductCalcReqDTO.Item calcOrderItem = calcProductItemDTOMap.get(sku.getId()); + item.setBuyQuantity(calcOrderItem.getQuantity()); + // 计算初始价格 + item.setOriginPrice(sku.getPrice()); + item.setBuyPrice(sku.getPrice()); + item.setPresentPrice(sku.getPrice()); + item.setBuyTotal(sku.getPrice() * calcOrderItem.getQuantity()); + item.setDiscountTotal(0); + item.setPresentTotal(item.getBuyTotal()); + } + return items; + } + +// private void modifyPriceByTimeLimitDiscount(List items, List activityList) { +// for (CalcOrderPriceBO.Item item : items) { +// // 获得符合条件的限时折扣 +// PromotionActivityBO timeLimitedDiscount = activityList.stream() +// .filter(activity -> PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType()) +// && activity.getTimeLimitedDiscount().getItems().stream().anyMatch(item0 -> item0.getSpuId().equals(item.getSpu().getId()))) +// .findFirst().orElse(null); +// if (timeLimitedDiscount == null) { +// continue; +// } +// // 计算价格 +// ProductSkuBO sku = new ProductSkuBO().setId(item.getId()).setSpuId(item.getSpu().getId()).setPrice(item.getPrice()); +// Integer newPrice = calcSkuPriceByTimeLimitDiscount(sku, timeLimitedDiscount); +// if (newPrice.equals(item.getPrice())) { +// continue; +// } +// // 设置优惠 +// item.setActivity(timeLimitedDiscount); +// // 设置价格 +// item.setBuyPrice(newPrice); +// item.setBuyTotal(newPrice * item.getBuyQuantity()); +// item.setPresentTotal(item.getBuyTotal() - item.getDiscountTotal()); +// item.setPresentPrice(item.getPresentTotal() / item.getBuyQuantity()); +// } +// } + +} diff --git a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/service/activity/PromotionActivityService.java b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/service/activity/PromotionActivityService.java index df8c99e27..5f5f59795 100644 --- a/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/service/activity/PromotionActivityService.java +++ b/promotion-service-project/promotion-service-app/src/main/java/cn/iocoder/mall/promotionservice/service/activity/PromotionActivityService.java @@ -1,8 +1,8 @@ package cn.iocoder.mall.promotionservice.service.activity; -import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil; import cn.iocoder.mall.promotion.api.enums.PromotionActivityTypeEnum; import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum; +import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO; import cn.iocoder.mall.promotionservice.convert.activity.PromotionActivityConvert; import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO; import cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity.PromotionActivityMapper; @@ -12,7 +12,6 @@ import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; -import javax.validation.Valid; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -26,11 +25,25 @@ public class PromotionActivityService { @Autowired private PromotionActivityMapper promotionActivityMapper; - public List getPromotionActivityListBySpuId(Integer spuId, Collection activityStatuses) { - return this.getPromotionActivityListBySpuIds(Collections.singleton(spuId), activityStatuses); + /** + * 获取指定商品 + * + * @param spuId + * @param activityStatuses + * @return + */ + public List listPromotionActivitiesBySpuId(Integer spuId, Collection activityStatuses) { + return this.listPromotionActivitiesBySpuIds(Collections.singleton(spuId), activityStatuses); } - public List getPromotionActivityListBySpuIds(Collection spuIds, Collection activityStatuses) { + /** + * + * + * @param spuIds + * @param activityStatuses + * @return + */ + public List listPromotionActivitiesBySpuIds(Collection spuIds, Collection activityStatuses) { if (spuIds.isEmpty() || activityStatuses.isEmpty()) { return Collections.emptyList(); } @@ -64,7 +77,7 @@ public class PromotionActivityService { } } // 返回最终结果 - return activityList; + return PromotionActivityConvert.INSTANCE.convertList(activityList); } public PromotionActivityPageBO getPromotionActivityPage(Integer pageNo,Integer pageSize,String title,Integer activityType,Collection statuses) { diff --git a/promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapperTest.java b/promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapperTest.java new file mode 100644 index 000000000..ef897ad94 --- /dev/null +++ b/promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/dal/mysql/mapper/activity/PromotionActivityMapperTest.java @@ -0,0 +1,28 @@ +package cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity; + +import cn.iocoder.mall.promotion.api.enums.PromotionActivityStatusEnum; +import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Collections; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class PromotionActivityMapperTest { + + @Autowired + private PromotionActivityMapper promotionActivityMapper; + + @Test + public void testSelectListByStatus() { + List result = promotionActivityMapper.selectListByStatus( + Collections.singleton(PromotionActivityStatusEnum.RUN.getValue())); + System.out.println(result.size()); + } + +} diff --git a/promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/package-info.java b/promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/package-info.java new file mode 100644 index 000000000..ab4cb2d4b --- /dev/null +++ b/promotion-service-project/promotion-service-app/src/test/java/cn/iocoder/mall/promotionservice/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.promotionservice;