优惠劵相关逻辑的迁移

This commit is contained in:
YunaiV
2020-08-20 19:47:29 +08:00
parent 2c6331eb75
commit 5b3c464faf
106 changed files with 1277 additions and 2621 deletions

View File

@@ -3,7 +3,7 @@ package cn.iocoder.mall.shopweb.controller.order;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.order.vo.cart.CartDetailVO;
import cn.iocoder.mall.shopweb.manager.order.cart.CartManager;
import cn.iocoder.mall.shopweb.manager.order.CartManager;
import cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;

View File

@@ -0,0 +1,11 @@
### /order/confirm-create-order-info 基于商品,确认创建订单
GET {{shop-api-base-url}}/order/confirm-create-order-info?skuId=33&quantity=1
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /order/confirm-create-order-info-from-cart 基于购物车,确认创建订单
GET {{shop-api-base-url}}/order/confirm-create-order-info-from-cart
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
###

View File

@@ -0,0 +1,49 @@
package cn.iocoder.mall.shopweb.controller.order;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.order.vo.order.OrderConfirmCreateInfoRespVO;
import cn.iocoder.mall.shopweb.manager.order.OrderManager;
import cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api(tags = "订单 API")
@RestController
@RequestMapping("/order")
@Validated
public class OrderController {
@Autowired
private OrderManager orderManager;
@GetMapping("confirm-create-order-info")
@ApiOperation("基于商品,确认创建订单")
@ApiImplicitParams({
@ApiImplicitParam(name = "skuId", required = true, value = "商品 SKU 编号", example = "1024"),
@ApiImplicitParam(name = "quantity", required = true, value = "购买数量", example = "2"),
@ApiImplicitParam(name = "couponCardId", value = "优惠劵编号", example = "1"),
})
@RequiresAuthenticate
public CommonResult<OrderConfirmCreateInfoRespVO> getOrderConfirmCreateInfo(@RequestParam("skuId") Integer skuId,
@RequestParam("quantity") Integer quantity,
@RequestParam(value = "couponCardId", required = false) Integer couponCardId) {
return success(orderManager.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId));
}
@GetMapping("confirm-create-order-info-from-cart")
@ApiOperation("基于购物车,确认创建订单")
@ApiImplicitParam(name = "couponCardId", value = "优惠劵编号", example = "1")
@RequiresAuthenticate
public CommonResult<OrderConfirmCreateInfoRespVO> getOrderConfirmCreateInfoFromCart(@RequestParam(value = "couponCardId", required = false) Integer couponCardId) {
return success(orderManager.getOrderConfirmCreateInfoFromCart(UserSecurityContextHolder.getUserId(), couponCardId));
}
}

View File

@@ -8,7 +8,7 @@ import lombok.experimental.Accessors;
import java.util.List;
@ApiModel(value = "用户的购物车明细 VO")
@ApiModel(value = "用户的购物车明细 Response VO") // TODO 芋艿swagger 文档完善
@Data
@Accessors(chain = true)
public class CartDetailVO {

View File

@@ -0,0 +1,216 @@
package cn.iocoder.mall.shopweb.controller.order.vo.order;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableRespDTO;
import cn.iocoder.mall.shopweb.controller.order.vo.cart.CartDetailVO;
import cn.iocoder.mall.shopweb.controller.product.vo.attr.ProductAttrKeyValueRespVO;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel(value = "订单确认创建信息 Response VO") // TODO 芋艿swagger 文档完善
@Data
@Accessors(chain = true) // TODO 芋艿:和 CartDetailVO、ProductSkuCalcPriceRespVO 有点重复,后续要优化下;
public class OrderConfirmCreateInfoRespVO {
/**
* 商品分组数组
*/
private List<ItemGroup> itemGroups;
/**
* 费用
*/
private Fee fee;
/**
* 优惠劵列表 TODO 芋艿,后续改改
*/
private List<CouponCardAvailableRespDTO> couponCards;
/**
* 商品分组
*
* 多个商品,参加同一个活动,从而形成分组。
*/
@Data
@Accessors(chain = true)
public static class ItemGroup {
/**
* 优惠活动
*/
private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
/**
* 促销减少的金额
*
* 1. 若未参与促销活动,或不满足促销条件,返回 null
* 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
*/
private Integer activityDiscountTotal;
/**
* 商品数组
*/
private List<CartDetailVO.Sku> items;
}
@Data
@Accessors(chain = true)
public static class Sku {
// SKU 自带信息
/**
* sku 编号
*/
private Integer id;
/**
* SPU 信息
*/
private Spu spu;
/**
* 图片地址
*/
private String picURL;
/**
* 规格值数组
*/
private List<ProductAttrKeyValueRespVO> attrs; // TODO 后面改下
/**
* 价格,单位:分
*/
private Integer price;
/**
* 库存数量
*/
private Integer quantity;
// 非 SKU 自带信息
/**
* 购买数量
*/
private Integer buyQuantity;
/**
* 优惠活动
*/
private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
/**
* 原始单价,单位:分。
*/
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 Spu {
/**
* SPU 编号
*/
private Integer id;
// ========== 基本信息 =========
/**
* SPU 名字
*/
private String name;
/**
* 分类编号
*/
private Integer cid;
/**
* 商品主图地址
*
* 数组,以逗号分隔
*
* 建议尺寸800*800像素你可以拖拽图片调整顺序最多上传15张
*/
private List<String> picUrls;
}
/**
* 费用(合计)
*/
@Data
@Accessors(chain = true)
public static class Fee {
/**
* 购买总价
*/
private Integer buyTotal;
/**
* 优惠总价
*
* 注意,满多少元包邮,不算在优惠中。
*/
private Integer discountTotal;
/**
* 邮费
*/
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;
}
}
/**
* 邮费信息 TODO 芋艿,未完成
*/
@Data
@Accessors(chain = true)
public static class Postage {
/**
* 需要满足多少钱,可以包邮。单位:分
*/
private Integer threshold;
}
}

View File

@@ -0,0 +1,5 @@
### /product-spu/page 计算商品 SKU 价格
GET {{shop-api-base-url}}/product-sku/cal-price?id=33
Content-Type: application/x-www-form-urlencoded
###

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.shopweb.controller.product;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.product.vo.sku.ProductSkuCalcPriceRespVO;
import cn.iocoder.mall.shopweb.manager.product.ProductSkuManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "商品 SKU API")
@RestController
@RequestMapping("/product-sku")
@Validated
public class ProductSkuController {
@Autowired
private ProductSkuManager productSkuManager;
@GetMapping("/cal-price")
@ApiOperation("计算商品 SKU 价格")
@ApiImplicitParam(name = "id", required = true, value = "商品 SKU 编号", example = "1024")
public CommonResult<ProductSkuCalcPriceRespVO> calcProductSkuPrice(@RequestParam("id") Integer id) {
return CommonResult.success(productSkuManager.calcProductSkuPrice(UserSecurityContextHolder.getUserId(), id));
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.mall.shopweb.controller.product.vo.sku;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("计算商品 SKU 价格结果 VO")
@Data
@Accessors(chain = true)
// TODO 芋艿swagger 注解
public class ProductSkuCalcPriceRespVO {
/**
* 原价格,单位:分。
*/
private Integer originalPrice;
/**
* 最终价格,单位:分。
*/
private Integer buyPrice;
/**
* 满减送促销活动
*
* TODO 芋艿,后续改成 VO
*/
private PromotionActivityRespDTO fullPrivilege;
/**
* 限时折扣促销活动
*
* TODO 芋艿,后续改成 VO
*/
private PromotionActivityRespDTO timeLimitedDiscount;
}

View File

@@ -0,0 +1,23 @@
### /promotion/coupon-card/page 优惠劵分页(未使用)
GET {{shop-api-base-url}}/promotion/coupon-card/page?pageNo=1&pageSize=10&status=1
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /promotion/coupon-card/page 优惠劵分页(已使用)
GET {{shop-api-base-url}}/promotion/coupon-card/page?pageNo=1&pageSize=10&status=2
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /promotion/coupon-card/page 优惠劵分页(已过期)
GET {{shop-api-base-url}}/promotion/coupon-card/page?pageNo=1&pageSize=10&status=3
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /promotion/coupon-card/create 用户领取优惠劵(成功)
POST {{shop-api-base-url}}/promotion/coupon-card/create
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
couponTemplateId=1
###

View File

@@ -0,0 +1,43 @@
package cn.iocoder.mall.shopweb.controller.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardPageReqVO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardRespVO;
import cn.iocoder.mall.shopweb.manager.promotion.CouponCardManager;
import cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@RestController
@RequestMapping("/promotion/coupon-card")
@Api(tags = "优惠劵 API")
@Validated
public class CouponCardController {
@Autowired
private CouponCardManager couponCardManager;
@GetMapping("/page")
@ApiOperation("获得优惠劵分页")
@RequiresAuthenticate
public CommonResult<PageResult<CouponCardRespVO>> pageCouponCard(CouponCardPageReqVO pageVO) {
return success(couponCardManager.pageCouponCard(UserSecurityContextHolder.getUserId(),pageVO));
}
@PostMapping("/create")
@ApiOperation("用户领取优惠劵")
@ApiImplicitParam(name = "couponTemplateId", value = "优惠劵模板编号", required = true, example = "1024")
@RequiresAuthenticate
public CommonResult<Integer> createCouponCard(@RequestParam("couponTemplateId") Integer couponTemplateId) {
return success(couponCardManager.createCouponCard(UserSecurityContextHolder.getUserId(), couponTemplateId));
}
}

View File

@@ -0,0 +1,17 @@
package cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card;
import cn.iocoder.common.framework.vo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ApiModel("优惠劵分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class CouponCardPageReqVO extends PageParam {
@ApiModelProperty(value = "优惠码状态", required = true, example = "1", notes = "对应 CouponCardStatusEnum 枚举")
private Integer status;
}

View File

@@ -0,0 +1,51 @@
package cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@ApiModel("优惠劵 Response VO")
@Data
@Accessors(chain = true)
public class CouponCardRespVO {
// ========== 基本信息 BEGIN ==========
@ApiModelProperty(value = "优惠劵编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "模板编号,自增唯一", required = true, example = "1")
private Integer templateId;
@ApiModelProperty(value = "优惠劵名", required = true, example = "大保剑")
private String title;
@ApiModelProperty(value = "优惠码状态", required = true, example = "参见 CouponCardStatusEnum 枚举")
private Integer status;
// ========== 基本信息 END ==========
// ========== 使用规则 BEGIN ==========
@ApiModelProperty(value = "是否设置满多少金额可用,单位:分", required = true)
private Integer priceAvailable;
@ApiModelProperty(value = "固定日期-生效开始时间", required = true)
private Date validStartTime;
@ApiModelProperty(value = "固定日期-生效结束时间", required = true)
private Date validEndTime;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
@ApiModelProperty(value = "优惠类型", required = true, example = "参见 CouponTemplatePreferentialTypeEnum 枚举")
private Integer preferentialType;
@ApiModelProperty(value = "折扣百分比")
private Integer percentOff;
@ApiModelProperty(value = "优惠金额,单位:分")
private Integer priceOff;
@ApiModelProperty(value = "折扣上限")
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 使用情况 BEGIN ==========
// ========== 使用情况 END ==========
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.shopweb.convert.order;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.order.vo.order.OrderConfirmCreateInfoRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Mapper
public interface OrderConvert {
OrderConvert INSTANCE = Mappers.getMapper(OrderConvert.class);
OrderConfirmCreateInfoRespVO.Fee convert(PriceProductCalcRespDTO.Fee bean);
default List<CouponCardAvailableListReqDTO.Item> convertList(List<PriceProductCalcRespDTO.ItemGroup> itemGroups) {
List<CouponCardAvailableListReqDTO.Item> items = new ArrayList<>();
itemGroups.forEach(itemGroup -> items.addAll(itemGroup.getItems().stream().map(
item -> new CouponCardAvailableListReqDTO.Item()
.setSpuId(item.getSpuId())
.setSkuId(item.getSkuId())
.setCid(item.getCid())
.setPrice(item.getBuyPrice())
.setQuantity(item.getBuyQuantity()))
.collect(Collectors.toList())));
return items;
}
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.shopweb.convert.product;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.sku.ProductSkuCalcPriceRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ProductSkuConvert {
ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);
default ProductSkuCalcPriceRespVO convert(PriceProductCalcRespDTO.Item item,
PromotionActivityRespDTO fullPrivilege, PromotionActivityRespDTO timeLimitedDiscount) {
return new ProductSkuCalcPriceRespVO().setOriginalPrice(item.getOriginPrice()).setBuyPrice(item.getBuyPrice())
.setFullPrivilege(fullPrivilege).setTimeLimitedDiscount(timeLimitedDiscount);
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.shopweb.convert.promotion;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardPageReqVO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 优惠劵 Convert
*/
@Mapper
public interface CouponCardConvert {
CouponCardConvert INSTANCE = Mappers.getMapper(CouponCardConvert.class);
PageResult<CouponCardRespVO> convertPage(PageResult<CouponCardRespDTO> page);
CouponCardPageReqDTO convert(CouponCardPageReqVO bean);
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.mall.shopweb.enums;
import cn.iocoder.common.framework.exception.ErrorCode;
/**
* 错误码枚举类
*
* shop web 系统,使用 1-007-000-000 段
*/
public interface ShopWebErrorCodeConstants {
// ========== ORDER 模块 ==========
ErrorCode ORDER_PRODUCT_SKU_NOT_EXISTS = new ErrorCode(1007000001, "商品 SKU 不存在!");
ErrorCode ORDER_PRODUCT_SKU_QUANTITY_NOT_ENOUGH = new ErrorCode(1007000001, "商品 SKU 库存不足!");
}

View File

@@ -1,4 +1,4 @@
package cn.iocoder.mall.shopweb.manager.order.cart;
package cn.iocoder.mall.shopweb.manager.order;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult;
@@ -94,7 +94,7 @@ public class CartManager {
* @return 商品列表
*/
public CartDetailVO getCartDetail(Integer userId) {
// 获得购物车中选中
// 获得购物车的商品
CommonResult<List<CartItemRespDTO>> listCartItemsResult = cartRpc.listCartItems(new CartItemListReqDTO().setUserId(userId));
listCartItemsResult.checkError();
// 购物车为空时构造空的 UsersOrderConfirmCreateVO 返回
@@ -104,7 +104,7 @@ public class CartManager {
result.setFee(new CartDetailVO.Fee(0, 0, 0, 0));
return result;
}
// 计算选中的商品价格
// 计算商品价格
CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceRpc.calcProductPrice(new PriceProductCalcReqDTO().setUserId(userId)
.setItems(listCartItemsResult.getData().stream()
.map(cartItem -> new PriceProductCalcReqDTO.Item(cartItem.getSkuId(), cartItem.getQuantity(), cartItem.getSelected()))

View File

@@ -0,0 +1,160 @@
package cn.iocoder.mall.shopweb.manager.order;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
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.orderservice.rpc.cart.CartRpc;
import cn.iocoder.mall.orderservice.rpc.cart.dto.CartItemListReqDTO;
import cn.iocoder.mall.orderservice.rpc.cart.dto.CartItemRespDTO;
import cn.iocoder.mall.productservice.enums.sku.ProductSkuDetailFieldEnum;
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.rpc.activity.PromotionActivityRpc;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.CouponCardRpc;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.PriceRpc;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.order.vo.order.OrderConfirmCreateInfoRespVO;
import cn.iocoder.mall.shopweb.convert.order.CartConvert;
import cn.iocoder.mall.shopweb.convert.order.OrderConvert;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.mall.shopweb.enums.ShopWebErrorCodeConstants.ORDER_PRODUCT_SKU_NOT_EXISTS;
import static cn.iocoder.mall.shopweb.enums.ShopWebErrorCodeConstants.ORDER_PRODUCT_SKU_QUANTITY_NOT_ENOUGH;
/**
* Order Manager
*/
@Service
@Validated
public class OrderManager {
@DubboReference(version = "${dubbo.consumer.PriceRpc.version}")
private PriceRpc priceRpc;
@DubboReference(version = "${dubbo.consumer.PromotionActivityRpc.version}")
private PromotionActivityRpc promotionActivityRpc;
@DubboReference(version = "${dubbo.consumer.ProductCategoryRpc.version}")
private CartRpc cartRpc;
@DubboReference(version = "${dubbo.consumer.ProductSkuRpc.version}")
private ProductSkuRpc productSkuRpc;
@DubboReference(version = "${dubbo.consumer.CouponCardRpc.version}")
private CouponCardRpc couponCardRpc;
public OrderConfirmCreateInfoRespVO getOrderConfirmCreateInfo(Integer userId, Integer skuId, Integer quantity, Integer couponCardId) {
Map<Integer, Integer> skuMap = new HashMap<>();
skuMap.put(skuId, quantity);
return this.getOrderConfirmCreateInfo0(userId, skuMap, couponCardId);
}
public OrderConfirmCreateInfoRespVO getOrderConfirmCreateInfoFromCart(Integer userId, Integer couponCardId) {
// 获得购物车的商品
CommonResult<List<CartItemRespDTO>> listCartItemsResult = cartRpc.listCartItems(
new CartItemListReqDTO().setUserId(userId).setSelected(true));
listCartItemsResult.checkError();
// 购物车为空时,构造空的 OrderConfirmCreateInfoRespVO 返回
if (CollectionUtils.isEmpty(listCartItemsResult.getData())) {
OrderConfirmCreateInfoRespVO result = new OrderConfirmCreateInfoRespVO();
result.setItemGroups(Collections.emptyList());
result.setFee(new OrderConfirmCreateInfoRespVO.Fee(0, 0, 0, 0));
return result;
}
// 计算商品价格
Map<Integer, Integer> skuMap = CollectionUtils.convertMap(listCartItemsResult.getData(),
CartItemRespDTO::getSkuId, CartItemRespDTO::getQuantity);
return this.getOrderConfirmCreateInfo0(userId, skuMap, couponCardId);
}
private OrderConfirmCreateInfoRespVO getOrderConfirmCreateInfo0(Integer userId, Map<Integer, Integer> skuMap, Integer couponCardId) {
// 校验商品都存在,并且库存足够
this.checkProductSkus(skuMap);
// 获得商品 SKU 信息
Map<Integer, ProductSkuRespDTO> productSkuMap = this.checkProductSkus(skuMap);
// 计算商品价格
CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceRpc.calcProductPrice(new PriceProductCalcReqDTO()
.setUserId(userId).setCouponCardId(couponCardId)
.setItems(skuMap.entrySet().stream().map(entry -> new PriceProductCalcReqDTO.Item(entry.getKey(), entry.getValue(), true))
.collect(Collectors.toList())));
calcProductPriceResult.checkError();
// 获得促销活动信息
Map<Integer, PromotionActivityRespDTO> promotionActivityMap = this.getPromotionActivityMap(calcProductPriceResult.getData());
// 拼接结果
OrderConfirmCreateInfoRespVO createInfoRespVO = new OrderConfirmCreateInfoRespVO();
createInfoRespVO.setFee(OrderConvert.INSTANCE.convert(calcProductPriceResult.getData().getFee()));
createInfoRespVO.setItemGroups(new ArrayList<>(calcProductPriceResult.getData().getItemGroups().size()));
for (PriceProductCalcRespDTO.ItemGroup itemGroupDTO : calcProductPriceResult.getData().getItemGroups()) {
OrderConfirmCreateInfoRespVO.ItemGroup itemGroupVO = new OrderConfirmCreateInfoRespVO.ItemGroup();
createInfoRespVO.getItemGroups().add(itemGroupVO);
// 活动信息
if (itemGroupDTO.getActivityId() != null) {
itemGroupVO.setActivity(promotionActivityMap.get(itemGroupDTO.getActivityId()))
.setActivityDiscountTotal(itemGroupDTO.getActivityDiscountTotal());
}
// 商品 SKU 信息
itemGroupVO.setItems(new ArrayList<>());
itemGroupDTO.getItems().forEach(item -> itemGroupVO.getItems().add(CartConvert.INSTANCE.convert(item,
productSkuMap.get(item.getSkuId()), promotionActivityMap.get(item.getActivityId()))));
}
// 查询可用优惠劵信息
CommonResult<List<CouponCardAvailableRespDTO>> listAvailableCouponCardsResult = couponCardRpc.listAvailableCouponCards(
new CouponCardAvailableListReqDTO().setUserId(userId)
.setItems(OrderConvert.INSTANCE.convertList(calcProductPriceResult.getData().getItemGroups())));
listAvailableCouponCardsResult.checkError();
createInfoRespVO.setCouponCards(listAvailableCouponCardsResult.getData());
return createInfoRespVO;
}
private Map<Integer, ProductSkuRespDTO> checkProductSkus(Map<Integer, Integer> skuMap) {
// 获得商品 SKU 列表
CommonResult<List<ProductSkuRespDTO>> listProductSkusResult = productSkuRpc.listProductSkus(new ProductSkuListQueryReqDTO()
.setProductSkuIds(skuMap.keySet())
.setFields(Arrays.asList(ProductSkuDetailFieldEnum.SPU.getField(), ProductSkuDetailFieldEnum.ATTR.getField())));
listProductSkusResult.checkError();
Map<Integer, ProductSkuRespDTO> productSkuMap = CollectionUtils.convertMap(listProductSkusResult.getData(), ProductSkuRespDTO::getId);
// 校验商品 SKU 是否合法
for (Map.Entry<Integer, Integer> entry : skuMap.entrySet()) {
ProductSkuRespDTO productSku = productSkuMap.get(entry.getKey());
if (productSku == null || !CommonStatusEnum.ENABLE.getValue().equals(productSku.getStatus())) {
throw ServiceExceptionUtil.exception(ORDER_PRODUCT_SKU_NOT_EXISTS);
}
if (productSku.getQuantity() < entry.getValue()) {
throw ServiceExceptionUtil.exception(ORDER_PRODUCT_SKU_QUANTITY_NOT_ENOUGH);
}
}
return productSkuMap;
}
private Map<Integer, PromotionActivityRespDTO> getPromotionActivityMap(PriceProductCalcRespDTO calcRespDTO) {
// 获得所有促销活动编号
Set<Integer> activeIds = new HashSet<>();
calcRespDTO.getItemGroups().forEach(itemGroup -> {
if (itemGroup.getActivityId() != null) {
activeIds.add(itemGroup.getActivityId());
}
itemGroup.getItems().forEach(item -> {
if (item.getActivityId() != null) {
activeIds.add(item.getActivityId());
}
});
});
if (CollectionUtils.isEmpty(activeIds)) {
return Collections.emptyMap();
}
// 查询促销活动列表
CommonResult<List<PromotionActivityRespDTO>> listPromotionActivitiesResult =
promotionActivityRpc.listPromotionActivities(new PromotionActivityListReqDTO().setActiveIds(activeIds));
listPromotionActivitiesResult.checkError();
return CollectionUtils.convertMap(listPromotionActivitiesResult.getData(), PromotionActivityRespDTO::getId);
}
}

View File

@@ -0,0 +1,60 @@
package cn.iocoder.mall.shopweb.manager.product;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.promotion.api.rpc.activity.PromotionActivityRpc;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.PriceRpc;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.sku.ProductSkuCalcPriceRespVO;
import cn.iocoder.mall.shopweb.convert.product.ProductSkuConvert;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collections;
import java.util.List;
/**
* 商品 SKU Manager
*/
@Service
@Validated
public class ProductSkuManager {
@DubboReference(version = "${dubbo.consumer.PriceRpc.version}")
private PriceRpc priceRpc;
@DubboReference(version = "${dubbo.consumer.PromotionActivityRpc.version}")
private PromotionActivityRpc promotionActivityRpc;
/**
* 计算商品 SKU 价格
*
* @param userId 用户编号
* @param skuId 商品 SKU 编号
* @return SKU 价格明细
*/
public ProductSkuCalcPriceRespVO calcProductSkuPrice(Integer userId, Integer skuId) {
CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceRpc.calcProductPrice(new PriceProductCalcReqDTO().setUserId(userId)
.setItems(Collections.singletonList(new PriceProductCalcReqDTO.Item(skuId, 1, true))));
calcProductPriceResult.checkError();
// 拼接结果
PriceProductCalcRespDTO.ItemGroup itemGroup = calcProductPriceResult.getData().getItemGroups().get(0);
// 1. 加载 满减送 促销活动
PromotionActivityRespDTO fullPrivilege = itemGroup.getActivityId() != null ? this.getPromotionActivity(itemGroup.getActivityId()) : null;
// 2. 加载 限时折扣 促销活动
PriceProductCalcRespDTO.Item item = itemGroup.getItems().get(0);
PromotionActivityRespDTO timeLimitedDiscount = item.getActivityId() != null ? this.getPromotionActivity(item.getActivityId()) : null;
// 3. 最终组装
return ProductSkuConvert.INSTANCE.convert(item, fullPrivilege, timeLimitedDiscount);
}
private PromotionActivityRespDTO getPromotionActivity(Integer activityId) {
CommonResult<List<PromotionActivityRespDTO>> listPromotionActivitiesResult = promotionActivityRpc.listPromotionActivities(
new PromotionActivityListReqDTO().setActiveIds(Collections.singleton(activityId)));
listPromotionActivitiesResult.checkError();
return listPromotionActivitiesResult.getData().get(0);
}
}

View File

@@ -0,0 +1,51 @@
package cn.iocoder.mall.shopweb.manager.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.CouponCardRpc;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardPageReqVO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardRespVO;
import cn.iocoder.mall.shopweb.convert.promotion.CouponCardConvert;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
/**
* 优惠劵 Manager
*/
@Service
public class CouponCardManager {
@DubboReference(version = "${dubbo.consumer.CouponCardRpc.version}")
private CouponCardRpc couponCardRpc;
/**
* 获得优惠劵分页
*
* @param userId 用户编号
* @param pageVO 优惠劵分页查询
* @return 优惠劵分页结果
*/
public PageResult<CouponCardRespVO> pageCouponCard(Integer userId, CouponCardPageReqVO pageVO) {
CommonResult<PageResult<CouponCardRespDTO>> pageCouponCardResult = couponCardRpc.pageCouponCard(
CouponCardConvert.INSTANCE.convert(pageVO).setUserId(userId));
pageCouponCardResult.checkError();
return CouponCardConvert.INSTANCE.convertPage(pageCouponCardResult.getData());
}
/**
* 用户领取优惠劵
*
* @param userId 用户编号
* @param couponTemplateId 优惠劵模板编号
* @return 优惠劵编号
*/
public Integer createCouponCard(Integer userId, Integer couponTemplateId) {
CommonResult<Integer> createCouponCardResult = couponCardRpc.createCouponCard(
new CouponCardCreateReqDTO().setUserId(userId).setCouponTemplateId(couponTemplateId));
createCouponCardResult.checkError();
return createCouponCardResult.getData();
}
}

View File

@@ -45,6 +45,10 @@ dubbo:
version: 1.0.0
PromotionActivityRpc:
version: 1.0.0
CouponCardRpc:
version: 1.0.0
CouponTemplateRpc:
version: 1.0.0
# Swagger 配置项
swagger: