优惠劵相关逻辑的迁移
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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}}
|
||||
|
||||
###
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
###
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
###
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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 ==========
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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 库存不足!");
|
||||
|
||||
}
|
||||
@@ -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()))
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user