提交 5b3c464f authored 作者: YunaiV's avatar YunaiV

优惠劵相关逻辑的迁移

上级 2c6331eb
package cn.iocoder.mall.system.api.constant;
/**
* 错误码枚举类
*
* 管理员系统,使用 1-002-000-000 段
*/
public enum AdminErrorCodeEnum {
// ========== OAUTH2 模块 ==========
OAUTH2_UNKNOWN(1002001000, "未知错误"), // 预留
// OAUTH2_INVALID_GRANT_BAD_CREDENTIALS(1001001001, "密码不正确"), // 暂时没用到
// OAUTH2_INVALID_GRANT_USERNAME_NOT_FOUND(1001001002, "账号不存在"), // 暂时没用到
// OAUTH2_INVALID_GRANT(1001001010, ""), // 预留
OAUTH2_INVALID_TOKEN_NOT_FOUND(1002001011, "访问令牌不存在"),
OAUTH2_INVALID_TOKEN_EXPIRED(1002001012, "访问令牌已过期"),
OAUTH2_INVALID_TOKEN_INVALID(1002001013, "访问令牌已失效"),
OAUTH2_NOT_LOGIN(1002001015, "账号未登陆"),
OAUTH2_INVALID_TOKEN_ERROR_USER_TYPE(1002001016, "访问令牌用户类型不正确"),
OAUTH_INVALID_REFRESH_TOKEN_NOT_FOUND(1002001017, "刷新令牌不存在"),
OAUTH_INVALID_REFRESH_TOKEN_EXPIRED(1002001018, "访问令牌已过期"),
OAUTH_INVALID_REFRESH_TOKEN_INVALID(1002001019, "刷新令牌已失效"),
// ========== 管理员模块 1002002000 ==========
ADMIN_USERNAME_NOT_REGISTERED(1002002000, "账号不存在"),
ADMIN_PASSWORD_ERROR(1002002001, "密码不正确"),
ADMIN_IS_DISABLE(1002002002, "账号被禁用"),
ADMIN_USERNAME_EXISTS(1002002002, "账号已经存在"),
ADMIN_STATUS_EQUALS(1002002003, "账号已经是该状态"),
ADMIN_DELETE_ONLY_DISABLE(1002002004, "只有关闭的账号才可以删除"),
ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE(1002002005, "管理员的账号状态不允许变更"),
ADMIN_ASSIGN_ROLE_NOT_EXISTS(1002002006, "分配员工角色时,有角色不存在"),
ADMIN_INVALID_PERMISSION(1002002007, "没有该操作权限"),
ADMIN_ADMIN_CAN_NOT_UPDATE(1002002008, "管理员的账号不允许变更"),
ADMIN_DEMO_CAN_NOT_WRITE(1002002009, "演示账号,暂不允许写操作。欢迎加入我们的交流群:http://t.cn/EKEr5WE"),
// ========== 资源模块 1002003000 ==========
RESOURCE_NAME_DUPLICATE(1002003000, "已经存在该名字的资源"),
RESOURCE_PARENT_NOT_EXISTS(1002003001, "父资源不存在"),
RESOURCE_PARENT_ERROR(1002003002, "不能设置自己为父资源"),
RESOURCE_NOT_EXISTS(1002003003, "资源不存在"),
RESOURCE_EXISTS_CHILDREN(1002003004, "存在子资源,无法删除"),
RESOURCE_PARENT_NOT_MENU(1002003005, "父资源的类型必须是菜单"),
// ========== 角色模块 1002004000 ==========
ROLE_NOT_EXISTS(1002004000, "角色不存在"),
ROLE_ASSIGN_RESOURCE_NOT_EXISTS(1002004001, "分配角色资源时,有资源不存在"),
// ========== 数据字典模块 1002005000 ==========
DATA_DICT_EXISTS(1002005000, "该数据字典已经存在"),
DATA_DICT_NOT_EXISTS(1002005001, "该数据字典不存在"),
// ========== 短信模板 1002006000 ==========
SMS_PLATFORM_FAIL(1002006000, "短信平台调用失败【具体错误会动态替换】"),
SMS_SIGN_NOT_EXISTENT(1002006001, "短信签名不存在"),
SMS_SIGN_IS_EXISTENT(1002006002, "短信签名已存在"),
SMS_TEMPLATE_NOT_EXISTENT(1002006020, "短信签名不存在"),
SMS_TEMPLATE_IS_EXISTENT(1002006021, "短信签名不存在"),
SMS_NOT_SEND_CLIENT(1002006030, "短信没有发送的client"),
// ========== 部门模块 1002007000 ==========
DEPT_SAME_LEVEL_NAME_EXITS(1002007001,"当前级别部门名字已存在"),
DEPT_PARENT_NOT_EXITS(1002007002,"父级部门不存在"),
DEPT_NOT_EXITS(1002007003, "当前部门不存在"),
DEPT_EXITS_CHILDREN(1002007004, "当前部门存在子部门"),
DEPT_PARENT_NOT_LEGAL(1002007005, "父级部门不合法"),
;
private final int code;
private final String message;
AdminErrorCodeEnum(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
package cn.iocoder.mall.order.rest.controller.cart;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("users/cart")
public class UsersCartController {
// @Reference(validation = "true", version = "${dubbo.provider.CartService.version}")
// private CartService cartService;
//
// @Reference(validation = "true", version = "${dubbo.provider.OrderService.version}")
// private OrderService orderService;
//
// @Reference(validation = "true", version = "${dubbo.consumer.CouponService.version}")
// private CouponService couponService;
//
//
// @GetMapping("/confirm_create_order")
// public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam(value = "couponCardId", required = false) Integer couponCardId) {
// Integer userId = UserSecurityContextHolder.getContext().getUserId();
// // 获得购物车中选中的
// List<CartItemBO> cartItems = cartService.list(userId, true);
// // 购物车为空时,构造空的 UsersOrderConfirmCreateVO 返回
// if (cartItems.isEmpty()) {
// UsersOrderConfirmCreateVO result = new UsersOrderConfirmCreateVO();
// result.setItemGroups(Collections.emptyList());
// result.setFee(new UsersOrderConfirmCreateVO.Fee(0, 0, 0, 0));
// return success(result);
// }
// // 计算商品价格
// CalcOrderPriceBO calcOrderPrice = list0(cartItems, couponCardId);
// // 获得优惠劵
// List<CouponCardAvailableBO> couponCards = couponService.getCouponCardList(userId,
// CartConvert.INSTANCE.convertList(calcOrderPrice.getItemGroups()));
// // 执行数据拼装
// return success(CartConvert.INSTANCE.convert(calcOrderPrice).setCouponCards(couponCards));
// }
//
// private CalcOrderPriceBO list0(List<CartItemBO> cartItems, Integer couponCardId) {
// // 创建计算的 DTO
// CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
// .setUserId(UserSecurityContextHolder.getContext().getUserId())
// .setItems(new ArrayList<>(cartItems.size()))
// .setCouponCardId(couponCardId);
// for (CartItemBO item : cartItems) {
// calcOrderPriceDTO.getItems().add(new CalcOrderPriceDTO.Item(item.getSkuId(), item.getQuantity(), item.getSelected()));
// }
// // 执行计算
// return cartService.calcOrderPrice(calcOrderPriceDTO);
// }
//
// @GetMapping("/calc_sku_price")
// public CommonResult<UsersCalcSkuPriceVO> calcSkuPrice(@RequestParam("skuId") Integer skuId) {
// // 计算 sku 的价格
// CalcSkuPriceBO calcSkuPrice = cartService.calcSkuPrice(skuId);
// return success(CartConvert.INSTANCE.convert2(calcSkuPrice));
// }
//
// public CommonResult<Object> confirmOrder() {
// // 查询购物车列表(选中的)
//// cartService.list(userId, true);
// // 查询确认订单信息的明细
//
// return null;
// }
}
...@@ -76,25 +76,7 @@ public class UsersOrderController { ...@@ -76,25 +76,7 @@ public class UsersOrderController {
// return createResult; // return createResult;
// } // }
// //
// @GetMapping("confirm_create_order")
// @RequiresLogin
// @ApiOperation("确认创建订单")
// public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("skuId") Integer skuId,
// @RequestParam("quantity") Integer quantity,
// @RequestParam(value = "couponCardId", required = false) Integer couponCardId) {
// Integer userId = UserSecurityContextHolder.getContext().getUserId();
// // 创建 CalcOrderPriceDTO 对象,并执行价格计算
// CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
// .setUserId(userId)
// .setItems(Collections.singletonList(new CalcOrderPriceDTO.Item(skuId, quantity, true)))
// .setCouponCardId(couponCardId);
// CalcOrderPriceBO calcOrderPrice = cartService.calcOrderPrice(calcOrderPriceDTO);
// // 获得优惠劵
// List<CouponCardAvailableBO> couponCards = couponService.getCouponCardList(userId,
// CartConvert.INSTANCE.convertList(calcOrderPrice.getItemGroups()));
// // 执行数据拼装
// return success(CartConvert.INSTANCE.convert(calcOrderPrice).setCouponCards(couponCards));
// }
// //
// @PostMapping("confirm_receiving") // @PostMapping("confirm_receiving")
// @RequiresLogin // @RequiresLogin
......
package cn.iocoder.mall.order.rest.convert;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CartConvert {
CartConvert INSTANCE = Mappers.getMapper(CartConvert.class);
// UsersOrderConfirmCreateVO convert(CalcOrderPriceBO calcOrderPriceBO);
//
// UsersCartDetailVO convert2(CalcOrderPriceBO calcOrderPriceBO);
//
// UsersCalcSkuPriceVO convert2(CalcSkuPriceBO calcSkuPriceBO);
//
// default List<CouponCardSpuDTO> convertList(List<CalcOrderPriceBO.ItemGroup> itemGroups) {
// List<CouponCardSpuDTO> items = new ArrayList<>();
// itemGroups.forEach(itemGroup -> items.addAll(itemGroup.getItems().stream().map(
// item -> new CouponCardSpuDTO()
// .setSpuId(item.getSpu().getId())
// .setSkuId(item.getId())
// .setCategoryId(item.getSpu().getCid())
// .setPrice(item.getBuyPrice())
// .setQuantity(item.getBuyQuantity()))
// .collect(Collectors.toList())));
// return items;
// }
}
package cn.iocoder.mall.order.rest.convert;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* application 订单 convert
*
* TODO 这种方式 文件名不能一样哈!
*
* @author Sin
* @time 2019-03-24 10:45
*/
@Mapper
public interface OrderConvertAPP {
OrderConvertAPP INSTANCE = Mappers.getMapper(OrderConvertAPP.class);
// @Mappings({})
// OrderQueryDTO convert(OrderPageQueryPO orderPageQueryVO);
//
// @Mappings({})
// OrderLogisticsUpdateDTO convert(OrderLogisticsPO orderLogisticsVO);
//
// @Mappings({})
// OrderItemUpdateDTO convert(OrderItemUpdatePO orderItemUpdateVO);
//
// @Mappings({})
// OrderCreateDTO convert(OrderCreatePO orderCreatePO);
//
// @Mappings({})
// List<OrderCreateDTO.OrderItem> convert(List<CartItemBO> cartItems);
//
// default OrderCreateDTO createOrderCreateDTO(Integer userId, Integer userAddressId,
// String remark, String ip,
// List<CartItemBO> cartItems, Integer couponCardId) {
// return new OrderCreateDTO()
// .setUserId(userId)
// .setUserAddressId(userAddressId)
// .setRemark(remark)
// .setIp(ip)
// .setOrderItems(this.convert(cartItems))
// .setCouponCardId(couponCardId);
// }
}
package cn.iocoder.mall.order.rest.convert;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Sin
* @time 2019-04-05 17:00
*/
@Mapper
public interface OrderDeliveryConvert {
OrderDeliveryConvert INSTANCE = Mappers.getMapper(OrderDeliveryConvert.class);
// @Mappings({})
// OrderDeliveryDTO convert(OrderDeliverPO orderDeliverPO);
}
package cn.iocoder.mall.order.rest.convert;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 订单退货
*
* @author Sin
* @time 2019-04-25 21:54
*/
@Mapper
public interface OrderReturnConvert {
OrderReturnConvert INSTANCE = Mappers.getMapper(OrderReturnConvert.class);
// @Mappings({})
// OrderReturnApplyDTO convert(OrderReturnApplyPO orderReturnApplyPO);
//
// @Mappings({})
// OrderReturnQueryDTO convert(OrderReturnQueryPO orderReturnQueryPO);
}
# 服务器的配置项
server:
port: 18088
servlet:
context-path: /order-api/
# Swagger 配置项
swagger:
title: 订单子系统
description: 订单子系统
version: 1.0.0
base-package: cn.iocoder.mall.order.rest.controller
package cn.iocoder.mall.order.api;
import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO;
public interface CartService {
/**
* 计算指定商品 SKU 的金额,并返回计算结果
*
* TODO 芋艿,此处只会计算,限时折扣带来的价格变化。
*
* @param skuId 商品 SKU 编号
* @return 计算订单金额结果
*/
CalcSkuPriceBO calcSkuPrice(Integer skuId);
}
package cn.iocoder.mall.order.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 购物车的商品信息 DO
*/
@Data
@Accessors(chain = true)
public class CartItemBO {
// ========= 基础字段 BEGIN =========
/**
* 编号,唯一自增。
*/
private Integer id;
/**
* 状态
*
* 1-正常
* 2-主动删除
* 3-下单删除
*/
private Integer status;
/**
* 是否选中
*/
private Boolean selected;
// ========= 基础字段 END =========
// ========= 买家信息 BEGIN =========
/**
* 用户编号
*/
private Integer userId;
// /**
// * 会话 key
// */
// private String nobody;
// ========= 买家信息 END =========
// ========= 商品信息 BEGIN =========
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 商品 SKU 编号
*/
private Integer skuId;
/**
* 商品购买数量
*/
private Integer quantity;
// TODO 冗余字段
// ========= 商品信息 END =========
// ========= 交易信息 BEGIN =========
/**
* 订单编号
*/
private Integer orderId;
/**
* 订单创建时间
*/
private Date orderCreateTime;
// ========= 交易信息 BEGIN =========
// ========= 优惠信息 BEGIN =========
// /**
// * 商品营销活动编号
// */
// private Integer activityId;
// /**
// * 商品营销活动类型
// */
// private Integer activityType;
// ========= 优惠信息 END =========
/**
* 创建时间
*/
private Date createTime;
}
package cn.iocoder.mall.order.api.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 计算订单价格 DTO
*/
@Data
@Accessors(chain = true)
@Deprecated
public class CalcOrderPriceDTO {
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 优惠劵编号
*/
private Integer couponCardId;
@NotNull(message = "商品数组不能为空")
private List<Item> items;
@Data
@Accessors(chain = true)
public static class Item {
/**
* SKU 编号
*/
private Integer skuId;
/**
* 数量
*/
private Integer quantity;
/**
* 是否选中
*
* 注意下,目前只有在购物车的时候,才可能出现该属性为 false 。其它情况下,都会为 true 为主。
*/
private Boolean selected;
public Item() {
}
public Item(Integer skuId, Integer quantity, Boolean selected) {
this.skuId = skuId;
this.quantity = quantity;
this.selected = selected;
}
}
}
package cn.iocoder.mall.order.biz.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("cn.iocoder.mall.order.biz.dao") // 扫描对应的 Mapper 接口
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理。为什么使用 proxyTargetClass 参数,参见 https://blog.csdn.net/huang_550/article/details/76492600
public class DatabaseConfiguration {
}
package cn.iocoder.mall.order.biz.config;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
@Configuration
public class ServiceExceptionConfiguration {
@EventListener(ApplicationReadyEvent.class) // 可参考 https://www.cnblogs.com/ssslinppp/p/7607509.html
public void initMessages() {
// 从 service_exception_message.properties 加载错误码的方案
// Properties properties;
// try {
// properties = PropertiesLoaderUtils.loadAllProperties("classpath:service_exception_message.properties");
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
} // TODO FROM 芋艿 to 小范,这里记得配置下,不然错误提示不出去呀。
}
package cn.iocoder.mall.order.biz.convert;
import cn.iocoder.mall.order.api.bo.CalcOrderPriceBO;
import cn.iocoder.mall.order.api.bo.CartItemBO;
import cn.iocoder.mall.order.biz.dataobject.CartItemDO;
import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface CartConvert {
CartConvert INSTANCE = Mappers.getMapper(CartConvert.class);
CalcOrderPriceBO.Item convert(ProductSkuDetailBO sku);
List<CartItemBO> convert(List<CartItemDO> items);
}
package cn.iocoder.mall.order.biz.service;
import org.springframework.stereotype.Service;
/**
* 购物车服务 Service 实现类
*/
@Service
@org.apache.dubbo.config.annotation.Service(validation = "true", version = "${dubbo.provider.CartService.version}")
public class CartServiceImpl {
// @Override
// @SuppressWarnings("Duplicates")
// public CalcSkuPriceBO calcSkuPrice(Integer skuId) {
// // 查询 SKU 是否合法
// ProductSkuBO sku = productSpuService.getProductSku(skuId);
// if (sku == null
// || CommonStatusEnum.DISABLE.getValue().equals(sku.getStatus())) { // sku 被禁用
// throw ServiceExceptionUtil.exception(OrderErrorCodeEnum.CARD_ITEM_SKU_NOT_FOUND.getCode());
// }
// // 查询促销活动
// List<PromotionActivityBO> activityList = promotionActivityService.getPromotionActivityListBySpuId(sku.getSpuId(),
// Arrays.asList(PromotionActivityStatusEnum.WAIT.getValue(), PromotionActivityStatusEnum.RUN.getValue()));
// if (activityList.isEmpty()) { // 如果无促销活动,则直接返回默认结果即可
// return new CalcSkuPriceBO().setOriginalPrice(sku.getPrice()).setBuyPrice(sku.getPrice());
// }
// // 如果有促销活动,则开始做计算 TODO 芋艿,因为现在暂时只有限时折扣 + 满减送。所以写的比较简单先
// PromotionActivityBO fullPrivilege = findPromotionActivityByType(activityList, PromotionActivityTypeEnum.FULL_PRIVILEGE);
// PromotionActivityBO timeLimitedDiscount = findPromotionActivityByType(activityList, PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT);
// Integer presentPrice = calcSkuPriceByTimeLimitDiscount(sku, timeLimitedDiscount);
// // 返回结果
// return new CalcSkuPriceBO().setFullPrivilege(fullPrivilege).setTimeLimitedDiscount(timeLimitedDiscount)
// .setOriginalPrice(sku.getPrice()).setBuyPrice(presentPrice);
// }
//
//
//
//
// private PromotionActivityBO findPromotionActivityByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) {
// return activityList.stream()
// .filter(activity -> type.getValue().equals(activity.getActivityType()))
// .findFirst().orElse(null);
// }
//
// private List<PromotionActivityBO> findPromotionActivityListByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) {
// return activityList.stream()
// .filter(activity -> type.getValue().equals(activity.getActivityType()))
// .collect(Collectors.toList());
// }
}
package cn.iocoder.mall.promotion.api.enums; package cn.iocoder.mall.promotion.api.enums.coupon.card;
import cn.iocoder.common.framework.core.IntArrayValuable; import cn.iocoder.common.framework.core.IntArrayValuable;
......
package cn.iocoder.mall.promotion.api.enums; package cn.iocoder.mall.promotion.api.enums.coupon.card;
import cn.iocoder.common.framework.core.IntArrayValuable; import cn.iocoder.common.framework.core.IntArrayValuable;
......
package cn.iocoder.mall.promotion.api.enums; package cn.iocoder.mall.promotion.api.enums.coupon.template;
import cn.iocoder.common.framework.core.IntArrayValuable; import cn.iocoder.common.framework.core.IntArrayValuable;
......
package cn.iocoder.mall.promotion.api.enums; package cn.iocoder.mall.promotion.api.enums.coupon.template;
import cn.iocoder.common.framework.core.IntArrayValuable; import cn.iocoder.common.framework.core.IntArrayValuable;
......
package cn.iocoder.mall.promotion.api.enums; package cn.iocoder.mall.promotion.api.enums.recommend;
import cn.iocoder.common.framework.core.IntArrayValuable; import cn.iocoder.common.framework.core.IntArrayValuable;
......
package cn.iocoder.mall.promotion.api.rpc.activity.dto; package cn.iocoder.mall.promotion.api.rpc.activity.dto;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
...@@ -25,13 +27,13 @@ public class PromotionActivityRespDTO implements Serializable { ...@@ -25,13 +27,13 @@ public class PromotionActivityRespDTO implements Serializable {
/** /**
* 活动类型 * 活动类型
* *
* 参见 {@link cn.iocoder.mall.promotion.api.enums.PromotionActivityTypeEnum} 枚举 * 参见 {@link PromotionActivityTypeEnum} 枚举
*/ */
private Integer activityType; private Integer activityType;
/** /**
* 活动状态 * 活动状态
* *
* 参见 {@link cn.iocoder.mall.promotion.api.enums.PromotionActivityStatusEnum} 枚举 * 参见 {@link PromotionActivityStatusEnum} 枚举
*/ */
private Integer status; private Integer status;
/** /**
......
package cn.iocoder.mall.promotion.api.rpc.coupon;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.*;
import java.util.List;
/**
* 优惠劵 Rpc 接口
*/
public interface CouponCardRpc {
/**
* 获得优惠劵分页
*
* @param pageReqDTO 优惠劵分页查询
* @return 优惠劵分页结果
*/
CommonResult<PageResult<CouponCardRespDTO>> pageCouponCard(CouponCardPageReqDTO pageReqDTO);
/**
* 给用户添加优惠劵
*
* @param createReqDTO 创建信息
* @return 优惠劵编号
*/
CommonResult<Integer> createCouponCard(CouponCardCreateReqDTO createReqDTO);
/**
* 用户使用优惠劵
*
* @param useReqDTO 使用信息
* @return 成功
*/
CommonResult<Boolean> useCouponCard(CouponCardUseReqDTO useReqDTO);
/**
* 用户取消使用优惠劵
*
* @param cancelUseReqDTO 取消使用信息
* @return 成功
*/
CommonResult<Boolean> cancelUseCouponCard(CouponCardCancelUseReqDTO cancelUseReqDTO);
/**
* 获得用户优惠劵的可用信息列表
*
* @param listReqDTO 查询信息
* @return 优惠劵的可用信息列表
*/
CommonResult<List<CouponCardAvailableRespDTO>> listAvailableCouponCards(CouponCardAvailableListReqDTO listReqDTO);
}
package cn.iocoder.mall.promotion.api.rpc.coupon; package cn.iocoder.mall.promotion.api.rpc.coupon;
import cn.iocoder.common.framework.validator.InEnum; import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.CouponTemplateStatusEnum; import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateStatusEnum;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.*; import cn.iocoder.mall.promotion.api.rpc.coupon.dto.*;
import javax.validation.constraints.NotNull;
import java.util.List;
public interface CouponRpc { public interface CouponRpc {
// ========== 优惠劵(码)模板 ========== // ========== 优惠劵(码)模板 ==========
...@@ -58,67 +55,4 @@ public interface CouponRpc { ...@@ -58,67 +55,4 @@ public interface CouponRpc {
Boolean updateCouponTemplateStatus(Integer adminId, Integer couponTemplateId, Boolean updateCouponTemplateStatus(Integer adminId, Integer couponTemplateId,
@InEnum(value = CouponTemplateStatusEnum.class, message = "修改状态必须是 {value}") Integer status); @InEnum(value = CouponTemplateStatusEnum.class, message = "修改状态必须是 {value}") Integer status);
// ========== 优惠劵 ==========
CouponCardPageRespDTO getCouponCardPage(CouponCardPageReqDTO couponCardPageDTO);
/**
* 基于优惠劵模板,领取优惠劵
*
* @param userId 用户编号
* @param couponTemplateId 优惠劵模板
* @return 优惠劵
*/
CouponCardReqDTO addCouponCard(Integer userId, Integer couponTemplateId);
/**
* 使用优惠劵下单
*
* @param userId 用户编号
* @param couponCardId 优惠劵编号
* @return 是否成功
*/
Boolean useCouponCard(Integer userId,
@NotNull(message = "优惠劵编号不能为空") Integer couponCardId);
/**
* 取消优惠劵的使用
*
* @param userId 用户编号
* @param couponCardId 优惠劵编号
* @return 是否成功
*/
Boolean cancelUseCouponCard(Integer userId, Integer couponCardId);
/**
* 获得指定优惠劵
*
* @param userId 用户编号
* @param couponCardId 优惠劵编号
* @return 优惠劵
*/
CouponCardDetailRespDTO getCouponCardDetail(Integer userId, Integer couponCardId);
/**
* 获得用户所有优惠劵,并标明是否可用
* <p>
* 注意,spus 是作为条件,判断优惠劵是否可用
*
* @param userId 用户编号
* @param spus 匹配的商品/分类
* @return 优惠劵列表
*/
List<CouponCardAvailableRespDTO> getCouponCardList(Integer userId, List<CouponCardSpuRespDTO> spus);
// ========== 优惠码 ==========
/**
* 使用优惠码,兑换优惠劵
*
* @param userId 用户编号
* @param code 优惠码
* @return 优惠劵
*/
CouponCardReqDTO useCouponCode(Integer userId, String code);
} }
package cn.iocoder.mall.promotion.api.rpc.coupon.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 优惠劵明细 BO 。
*
* 主要是,会带上 {@link CouponTemplateReqDTO} 的信息
*/
@Data
@Accessors(chain = true)
public class CouponCardDetailRespDTO implements Serializable {
// ========== 基本信息 BEGIN ==========
/**
* 优惠劵编号
*/
private Integer id;
/**
* 优惠劵(码)分组编号
*/
private Integer templateId;
/**
* 优惠劵名
*/
private String title;
// /**
// * 核销码
// */
// private String verifyCode;
/**
* 优惠码状态
*
* 1-未使用
* 2-已使用
* 3-已失效
*/
private Integer status;
// ========== 基本信息 END ==========
// ========== 领取情况 BEGIN ==========
/**
* 用户编号
*/
private Integer userId;
/**
* 领取类型
*
* 1 - 用户主动领取
* 2 - 后台自动发放
*/
private Integer takeType;
// ========== 领取情况 END ==========
// ========== 使用规则 BEGIN ==========
/**
* 是否设置满多少金额可用,单位:分
*/
private Integer priceAvailable;
/**
* 生效开始时间
*/
private Date validStartTime;
/**
* 生效结束时间
*/
private Date validEndTime;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
/**
* 优惠类型
*
* 1-代金卷
* 2-折扣卷
*/
private Integer preferentialType;
/**
* 折扣
*/
private Integer percentOff;
/**
* 优惠金额,单位:分。
*/
private Integer priceOff;
/**
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
*
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
*/
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 使用情况 BEGIN ==========
/**
* 是否使用
*/
private Boolean used;
/**
* 使用订单号
*/
private Integer usedOrderId;
/**
* 订单中优惠面值,单位:分
*/
private Integer usedPrice;
/**
* 使用时间
*/
private Date usedTime;
// TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
// ========== 使用情况 END ==========
/**
* 创建时间
*/
private Date createTime;
// ========== FROM template 使用规则 BEGIN ==========
/**
* 可用范围的类型
*
* 10-全部(ALL):所有可用
* 20-部分(PART):部分商品可用,或指定商品可用
* 21-部分(PART):部分商品不可用,或指定商品可用
* 30-部分(PART):部分分类可用,或指定商品可用
* 31-部分(PART):部分分类不可用,或指定商品可用
*/
private Integer rangeType;
/**
* 指定商品 / 分类列表,使用逗号分隔商品编号
*/
private List<Integer> rangeValues;
}
package cn.iocoder.mall.promotion.api.rpc.coupon.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 优惠劵分页 BO
*/
@Data
@Accessors(chain = true)
public class CouponCardPageRespDTO implements Serializable {
/**
* 优惠劵数组
*/
private List<CouponCardReqDTO> list;
/**
* 总量
*/
private Integer total;
}
package cn.iocoder.mall.promotion.api.rpc.coupon.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 优惠劵 BO
*/
@Data
@Accessors(chain = true)
public class CouponCardReqDTO implements Serializable {
// ========== 基本信息 BEGIN ==========
/**
* 优惠劵编号
*/
private Integer id;
/**
* 优惠劵(码)分组编号
*/
private Integer templateId;
/**
* 优惠劵名
*/
private String title;
// /**
// * 核销码
// */
// private String verifyCode;
/**
* 优惠码状态
*
* 1-未使用
* 2-已使用
* 3-已失效
*/
private Integer status;
// ========== 基本信息 END ==========
// ========== 领取情况 BEGIN ==========
/**
* 用户编号
*/
private Integer userId;
/**
* 领取类型
*
* 1 - 用户主动领取
* 2 - 后台自动发放
*/
private Integer takeType;
// ========== 领取情况 END ==========
// ========== 使用规则 BEGIN ==========
/**
* 是否设置满多少金额可用,单位:分
*/
private Integer priceAvailable;
/**
* 生效开始时间
*/
private Date validStartTime;
/**
* 生效结束时间
*/
private Date validEndTime;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
/**
* 优惠类型
*
* 1-代金卷
* 2-折扣卷
*/
private Integer preferentialType;
/**
* 折扣
*/
private Integer percentOff;
/**
* 优惠金额,单位:分。
*/
private Integer priceOff;
/**
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
*
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
*/
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 使用情况 BEGIN ==========
/**
* 使用时间
*/
private Date usedTime;
// TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
// ========== 使用情况 END ==========
/**
* 创建时间
*/
private Date createTime;
}
package cn.iocoder.mall.promotion.api.rpc.coupon.dto; package cn.iocoder.mall.promotion.api.rpc.coupon.dto;
import cn.iocoder.common.framework.validator.InEnum; import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.CouponTemplateDateTypeEnum; import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateDateTypeEnum;
import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum; import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum; import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import lombok.Data; import lombok.Data;
......
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
/**
* 优惠劵可用信息列表 Request DTO
*/
@Data
@Accessors(chain = true)
public class CouponCardAvailableListReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 商品数组
*/
@NotEmpty(message = "商品数组不能为空")
private List<Item> items;
/**
* 商品
*/
@Data
@Accessors(chain = true)
public static class Item implements Serializable {
/**
* 商品 SPU 编号
*/
@NotNull(message = "商品 SPU 编号不能为空")
private Integer spuId;
/**
* 商品 SKU 编号
*/
@NotNull(message = "商品 SKU 编号不能为空")
private Integer skuId;
/**
* 商品 SKU 编号
*/
@NotNull(message = "商品 Category 编号不能为空")
private Integer cid;
/**
* 商品数量
*/
@NotNull(message = "商品数量不能为空")
@Min(value = 1L, message = "最小商品数量 1")
private Integer quantity;
/**
* 商品价格,单位:分
*
* 为什么需要传递价格?因为商品的价格是经过计算,部署商品原始价格
*/
@NotNull(message = "商品价格不能为空")
private Integer price;
}
}
package cn.iocoder.mall.promotion.api.rpc.coupon.dto; package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
/** /**
...@@ -9,8 +10,9 @@ import lombok.experimental.Accessors; ...@@ -9,8 +10,9 @@ import lombok.experimental.Accessors;
* 注意,如果优惠劵不可用,标记 available = false ,并写明 unavailableReason 原因 * 注意,如果优惠劵不可用,标记 available = false ,并写明 unavailableReason 原因
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true) @Accessors(chain = true)
public class CouponCardAvailableRespDTO extends CouponCardReqDTO { public class CouponCardAvailableRespDTO extends CouponCardRespDTO {
/** /**
* 是否可用 * 是否可用
......
package cn.iocoder.mall.promotion.api.rpc.coupon.dto; package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
...@@ -7,24 +7,21 @@ import javax.validation.constraints.NotNull; ...@@ -7,24 +7,21 @@ import javax.validation.constraints.NotNull;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 优惠劵分页 DTO * 优惠劵取消使用 Request DTO
*/ */
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class CouponCardPageReqDTO implements Serializable { public class CouponCardCancelUseReqDTO implements Serializable {
/** /**
* 用户编号 * 用户编号
*/ */
@NotNull(message = "用户编号不能为空")
private Integer userId; private Integer userId;
/** /**
* 状态 * 优惠劵编号
*/ */
private Integer status; @NotNull(message = "优惠劵编号不能为空")
private Integer couponCardId;
@NotNull(message = "页码不能为空")
private Integer pageNo;
@NotNull(message = "每页条数不能为空")
private Integer pageSize;
} }
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 优惠劵创建 Request DTO
*/
@Data
@Accessors(chain = true)
public class CouponCardCreateReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 优惠劵模板编号
*/
@NotNull(message = "优惠劵模板编号不能为空")
private Integer couponTemplateId;
}
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import cn.iocoder.common.framework.vo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 优惠劵分页 DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class CouponCardPageReqDTO extends PageParam {
/**
* 用户编号
*/
private Integer userId;
/**
* 状态
*/
private Integer status;
}
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 优惠劵使用 Request DTO
*/
@Data
@Accessors(chain = true)
public class CouponCardUseReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 优惠劵编号
*/
@NotNull(message = "优惠劵编号不能为空")
private Integer couponCardId;
}
...@@ -17,7 +17,6 @@ public class PriceProductCalcReqDTO implements Serializable { ...@@ -17,7 +17,6 @@ public class PriceProductCalcReqDTO implements Serializable {
/** /**
* 用户编号 * 用户编号
*/ */
@NotNull(message = "用户编号不能为空")
private Integer userId; private Integer userId;
/** /**
......
package cn.iocoder.mall.promotion.api.rpc.price.dto; package cn.iocoder.mall.promotion.api.rpc.price.dto;
import cn.iocoder.mall.promotion.api.enums.PromotionActivityTypeEnum; import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
......
package cn.iocoder.mall.promotion.api.rpc.recommend.dto; package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.common.framework.validator.InEnum; import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.ProductRecommendTypeEnum; import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
......
package cn.iocoder.mall.promotion.api.rpc.recommend.dto; package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.mall.promotion.api.enums.ProductRecommendTypeEnum; import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
......
package cn.iocoder.mall.promotion.api.rpc.recommend.dto; package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.common.framework.validator.InEnum; import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.ProductRecommendTypeEnum; import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
......
package cn.iocoder.mall.promotionservice.convert.coupon;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.CouponCardDetailRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.CouponCardReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponCardDO;
import cn.iocoder.mall.promotionservice.service.coupon.bo.CouponCardAvailableBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface CouponCardConvert {
CouponCardConvert INSTANCE = Mappers.getMapper(CouponCardConvert.class);
// @Mappings({})
// CouponCardBO convertToBO(CouponCardDO banner);
List<CouponCardReqDTO> convertToDTO(List<CouponCardDO> cardList);
CouponCardReqDTO convertToSingleDTO(CouponCardDO card);
@Mappings({})
CouponCardDetailRespDTO convert2(CouponCardDO card);
CouponCardAvailableBO convertAvailBO(CouponCardDO card,boolean x);
//@Mappings({})
//CouponCardAvailableBO convert2(CouponCardDO card, boolean x); // TODO 芋艿,临时用来解决 mapstruct 无法正确匹配方法的问题
}
package cn.iocoder.mall.promotionservice.convert.coupon.card; package cn.iocoder.mall.promotionservice.convert.coupon.card;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO; import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponCardDO; import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponCardDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
@Mapper @Mapper
...@@ -12,4 +17,10 @@ public interface CouponCardConvert { ...@@ -12,4 +17,10 @@ public interface CouponCardConvert {
CouponCardRespDTO convert(CouponCardDO bean); CouponCardRespDTO convert(CouponCardDO bean);
@Mapping(source = "records", target = "list")
PageResult<CouponCardRespDTO> convertPage(IPage<CouponCardDO> page);
@Named("convertCouponCardDOToCouponCardAvailableRespDTO") // 避免生成的方法名的冲突
CouponCardAvailableRespDTO convert01(CouponCardDO bean);
} }
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity; package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO; import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
...@@ -31,7 +33,7 @@ public class PromotionActivityDO extends BaseDO { ...@@ -31,7 +33,7 @@ public class PromotionActivityDO extends BaseDO {
/** /**
* 活动类型 * 活动类型
* *
* 参见 {@link cn.iocoder.mall.promotion.api.enums.PromotionActivityTypeEnum} 枚举 * 参见 {@link PromotionActivityTypeEnum} 枚举
*/ */
private Integer activityType; private Integer activityType;
// /** // /**
...@@ -42,7 +44,7 @@ public class PromotionActivityDO extends BaseDO { ...@@ -42,7 +44,7 @@ public class PromotionActivityDO extends BaseDO {
/** /**
* 活动状态 * 活动状态
* *
* 参见 {@link cn.iocoder.mall.promotion.api.enums.PromotionActivityStatusEnum} 枚举 * 参见 {@link PromotionActivityStatusEnum} 枚举
*/ */
private Integer status; private Integer status;
/** /**
......
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon; package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO; import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.util.Date; import java.util.Date;
...@@ -9,7 +11,9 @@ import java.util.Date; ...@@ -9,7 +11,9 @@ import java.util.Date;
/** /**
* 优惠劵 DO * 优惠劵 DO
*/ */
@TableName("coupon_card")
@Data @Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true) @Accessors(chain = true)
public class CouponCardDO extends BaseDO { public class CouponCardDO extends BaseDO {
......
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon; package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO; import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateStatusEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.util.Date; import java.util.Date;
...@@ -11,7 +14,9 @@ import java.util.Date; ...@@ -11,7 +14,9 @@ import java.util.Date;
* *
* 当用户领取时,会生成 {@link CouponCardDO} 优惠劵(码)。 * 当用户领取时,会生成 {@link CouponCardDO} 优惠劵(码)。
*/ */
@TableName("coupon_template")
@Data @Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true) @Accessors(chain = true)
public class CouponTemplateDO extends BaseDO { public class CouponTemplateDO extends BaseDO {
...@@ -38,7 +43,7 @@ public class CouponTemplateDO extends BaseDO { ...@@ -38,7 +43,7 @@ public class CouponTemplateDO extends BaseDO {
/** /**
* 优惠码状态 * 优惠码状态
* *
* {@link cn.iocoder.mall.promotion.api.enums.CouponTemplateStatusEnum} * {@link CouponTemplateStatusEnum}
* *
* 当优惠劵(码)开启中,可以手动操作,设置禁用中。 * 当优惠劵(码)开启中,可以手动操作,设置禁用中。
*/ */
......
...@@ -2,7 +2,7 @@ package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.recommend; ...@@ -2,7 +2,7 @@ package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.recommend;
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO; import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
import cn.iocoder.mall.promotion.api.enums.ProductRecommendTypeEnum; import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
......
package cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon; package cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardPageReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponCardDO; import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponCardDO;
import org.apache.ibatis.annotations.Param; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.List; import java.util.List;
@Repository @Repository
public interface CouponCardMapper { public interface CouponCardMapper extends BaseMapper<CouponCardDO> {
CouponCardDO selectById(@Param("id") Integer id); default List<CouponCardDO> selectListByUserIdAndStatus(Integer userId, Integer status) {
return selectList(new QueryWrapper<CouponCardDO>().eq("user_id", userId)
List<CouponCardDO> selectListByUserIdAndStatus(@Param("userId") Integer userId, .eq("status", status));
@Param("status") Integer status); }
List<CouponCardDO> selectListByPage(@Param("userId") Integer userId, default int selectCountByUserIdAndTemplateId(Integer userId, Integer templateId) {
@Param("status") Integer status, return selectCount(new QueryWrapper<CouponCardDO>().eq("user_id", userId)
@Param("offset") Integer offset, .eq("template_id", templateId));
@Param("limit") Integer limit); }
Integer selectCountByPage(@Param("userId") Integer userId, default int updateByIdAndStatus(Integer id, Integer status, CouponCardDO updateObj) {
@Param("status") Integer status); return update(updateObj, new QueryWrapper<CouponCardDO>().eq("id", id)
.eq("status", status));
int selectCountByUserIdAndTemplateId(@Param("userId") Integer userId, }
@Param("templateId") Integer templateId);
default IPage<CouponCardDO> selectPage(CouponCardPageReqDTO pageReqDTO) {
void insert(CouponCardDO couponCardDO); return selectPage(new Page<>(pageReqDTO.getPageNo(), pageReqDTO.getPageSize()),
new QueryWrapperX<CouponCardDO>().eqIfPresent("user_id", pageReqDTO.getUserId())
int update(CouponCardDO couponCardDO); .eqIfPresent("status", pageReqDTO.getStatus()));
}
int updateByIdAndStatus(@Param("id") Integer id,
@Param("status") Integer status,
@Param("updateObj") CouponCardDO updateObj);
} }
package cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon; package cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponTemplateDO; import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponTemplateDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List; import java.util.List;
@Repository @Repository
public interface CouponTemplateMapper { public interface CouponTemplateMapper extends BaseMapper<CouponTemplateDO> {
CouponTemplateDO selectById(@Param("id") Integer id);
List<CouponTemplateDO> selectListByIds(@Param("ids") Collection<Integer> ids);
List<CouponTemplateDO> selectListByPage(@Param("type") Integer type, List<CouponTemplateDO> selectListByPage(@Param("type") Integer type,
@Param("title") String title, @Param("title") String title,
...@@ -26,10 +22,14 @@ public interface CouponTemplateMapper { ...@@ -26,10 +22,14 @@ public interface CouponTemplateMapper {
@Param("status") Integer status, @Param("status") Integer status,
@Param("preferentialType") Integer preferentialType); @Param("preferentialType") Integer preferentialType);
void insert(CouponTemplateDO couponTemplate); /**
* 更新优惠劵模板已领取的数量
int update(CouponTemplateDO couponTemplate); *
* 如果超过领取上限,则返回 0
*
* @param id 优惠劵模板编号
* @return 更新数量
*/
int updateStatFetchNumIncr(@Param("id") Integer id); int updateStatFetchNumIncr(@Param("id") Integer id);
} }
package cn.iocoder.mall.promotionservice.manager.coupon.card;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.*;
import cn.iocoder.mall.promotionservice.service.coupon.CouponCardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
@Service
@Validated
public class CouponCardManager {
@Autowired
private CouponCardService couponCardService;
public PageResult<CouponCardRespDTO> pageCouponCard(CouponCardPageReqDTO pageReqDTO) {
return couponCardService.pageCouponCard(pageReqDTO);
}
public Integer createCouponCard(CouponCardCreateReqDTO createReqDTO) {
return couponCardService.createCouponCard(createReqDTO.getUserId(), createReqDTO.getCouponTemplateId());
}
public Boolean useCouponCard(CouponCardUseReqDTO useReqDTO) {
couponCardService.useCouponCard(useReqDTO.getUserId(), useReqDTO.getCouponCardId());
return true;
}
public Boolean cancelUseCouponCard(CouponCardCancelUseReqDTO cancelUseReqDTO) {
couponCardService.cancelUseCouponCard(cancelUseReqDTO.getUserId(), cancelUseReqDTO.getCouponCardId());
return true;
}
public List<CouponCardAvailableRespDTO> listAvailableCouponCards(CouponCardAvailableListReqDTO listReqDTO) {
return couponCardService.listAvailableCouponCards(listReqDTO);
}
}
...@@ -9,6 +9,8 @@ import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO; ...@@ -9,6 +9,8 @@ import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO;
import cn.iocoder.mall.productservice.rpc.spu.ProductSpuRpc; import cn.iocoder.mall.productservice.rpc.spu.ProductSpuRpc;
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO; import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
import cn.iocoder.mall.promotion.api.enums.*; import cn.iocoder.mall.promotion.api.enums.*;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO; import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO; import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplateRespDTO; import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplateRespDTO;
......
package cn.iocoder.mall.promotionservice.rpc.coupon;
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.*;
import cn.iocoder.mall.promotionservice.manager.coupon.card.CouponCardManager;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@DubboService
public class CouponCardRpcImpl implements CouponCardRpc {
@Autowired
private CouponCardManager couponCardManager;
@Override
public CommonResult<PageResult<CouponCardRespDTO>> pageCouponCard(CouponCardPageReqDTO pageReqDTO) {
return success(couponCardManager.pageCouponCard(pageReqDTO));
}
@Override
public CommonResult<Integer> createCouponCard(CouponCardCreateReqDTO createReqDTO) {
return success(couponCardManager.createCouponCard(createReqDTO));
}
@Override
public CommonResult<Boolean> useCouponCard(CouponCardUseReqDTO useReqDTO) {
return success(couponCardManager.useCouponCard(useReqDTO));
}
@Override
public CommonResult<Boolean> cancelUseCouponCard(CouponCardCancelUseReqDTO cancelUseReqDTO) {
return success(couponCardManager.cancelUseCouponCard(cancelUseReqDTO));
}
@Override
public CommonResult<List<CouponCardAvailableRespDTO>> listAvailableCouponCards(CouponCardAvailableListReqDTO listReqDTO) {
return success(couponCardManager.listAvailableCouponCards(listReqDTO));
}
}
package cn.iocoder.mall.promotionservice.service.activity; package cn.iocoder.mall.promotionservice.service.activity;
import cn.iocoder.mall.promotion.api.enums.PromotionActivityTypeEnum; import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum; import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO; 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.activity.dto.PromotionActivityRespDTO;
......
package cn.iocoder.mall.promotionservice.service.activity.bo; package cn.iocoder.mall.promotionservice.service.activity.bo;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
...@@ -22,13 +24,13 @@ public class PromotionActivityBO implements Serializable { ...@@ -22,13 +24,13 @@ public class PromotionActivityBO implements Serializable {
/** /**
* 活动类型 * 活动类型
* *
* 参见 {@link cn.iocoder.mall.promotion.api.enums.PromotionActivityTypeEnum} 枚举 * 参见 {@link PromotionActivityTypeEnum} 枚举
*/ */
private Integer activityType; private Integer activityType;
/** /**
* 活动状态 * 活动状态
* *
* 参见 {@link cn.iocoder.mall.promotion.api.enums.PromotionActivityStatusEnum} 枚举 * 参见 {@link PromotionActivityStatusEnum} 枚举
*/ */
private Integer status; private Integer status;
/** /**
......
package cn.iocoder.mall.promotionservice.service.coupon.bo;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.CouponCardReqDTO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 可用优惠劵 BO
*
* 注意,如果优惠劵不可用,标记 available = false ,并写明 unavailableReason 原因
*/
@Data
@Accessors(chain = true)
public class CouponCardAvailableBO extends CouponCardReqDTO {
/**
* 是否可用
*/
private Boolean available;
/**
* 不可用原因
*/
private String unavailableReason;
}
package cn.iocoder.mall.promotionservice.service.coupon.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 优惠劵明细 BO 。
*
* 主要是,会带上 {@link CouponTemplateBO} 的信息
*/
@Data
@Accessors(chain = true)
public class CouponCardDetailBO implements Serializable {
// ========== 基本信息 BEGIN ==========
/**
* 优惠劵编号
*/
private Integer id;
/**
* 优惠劵(码)分组编号
*/
private Integer templateId;
/**
* 优惠劵名
*/
private String title;
// /**
// * 核销码
// */
// private String verifyCode;
/**
* 优惠码状态
*
* 1-未使用
* 2-已使用
* 3-已失效
*/
private Integer status;
// ========== 基本信息 END ==========
// ========== 领取情况 BEGIN ==========
/**
* 用户编号
*/
private Integer userId;
/**
* 领取类型
*
* 1 - 用户主动领取
* 2 - 后台自动发放
*/
private Integer takeType;
// ========== 领取情况 END ==========
// ========== 使用规则 BEGIN ==========
/**
* 是否设置满多少金额可用,单位:分
*/
private Integer priceAvailable;
/**
* 生效开始时间
*/
private Date validStartTime;
/**
* 生效结束时间
*/
private Date validEndTime;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
/**
* 优惠类型
*
* 1-代金卷
* 2-折扣卷
*/
private Integer preferentialType;
/**
* 折扣
*/
private Integer percentOff;
/**
* 优惠金额,单位:分。
*/
private Integer priceOff;
/**
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
*
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
*/
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 使用情况 BEGIN ==========
/**
* 是否使用
*/
private Boolean used;
/**
* 使用订单号
*/
private Integer usedOrderId;
/**
* 订单中优惠面值,单位:分
*/
private Integer usedPrice;
/**
* 使用时间
*/
private Date usedTime;
// TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
// ========== 使用情况 END ==========
/**
* 创建时间
*/
private Date createTime;
// ========== FROM template 使用规则 BEGIN ==========
/**
* 可用范围的类型
*
* 10-全部(ALL):所有可用
* 20-部分(PART):部分商品可用,或指定商品可用
* 21-部分(PART):部分商品不可用,或指定商品可用
* 30-部分(PART):部分分类可用,或指定商品可用
* 31-部分(PART):部分分类不可用,或指定商品可用
*/
private Integer rangeType;
/**
* 指定商品 / 分类列表,使用逗号分隔商品编号
*/
private List<Integer> rangeValues;
}
package cn.iocoder.mall.promotionservice.service.coupon.bo;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.CouponCardReqDTO;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 优惠劵分页 BO
*/
@Data
@Accessors(chain = true)
public class CouponCardPageBO implements Serializable {
/**
* 优惠劵数组
*/
private List<CouponCardReqDTO> list;
/**
* 总量
*/
private Integer total;
}
package cn.iocoder.mall.promotionservice.service.coupon.bo;
import cn.iocoder.mall.promotion.api.rpc.coupon.CouponRpc;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 优惠劵商品 DTO
*
* 主要用于 {@link CouponRpc#getCouponCardList(Integer, List)}
*/
@Data
@Accessors(chain = true)
public class CouponCardSpuBO implements Serializable {
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 商品 SKU 编号
*/
private Integer skuId;
/**
* 分类编号
*/
private Integer categoryId;
/**
* 价格
*/
private Integer price;
/**
* 数量
*/
private Integer quantity;
}
package cn.iocoder.mall.promotionservice.service.coupon.bo; package cn.iocoder.mall.promotionservice.service.coupon.bo;
import cn.iocoder.common.framework.validator.InEnum; import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.CouponTemplateDateTypeEnum; import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateDateTypeEnum;
import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum; import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum; import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import lombok.Data; import lombok.Data;
......
package cn.iocoder.mall.promotionservice.service.recommend.bo; package cn.iocoder.mall.promotionservice.service.recommend.bo;
import cn.iocoder.common.framework.validator.InEnum; import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.ProductRecommendTypeEnum; import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
......
package cn.iocoder.mall.promotionservice.service.recommend.bo; package cn.iocoder.mall.promotionservice.service.recommend.bo;
import cn.iocoder.mall.promotion.api.enums.ProductRecommendTypeEnum; import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
......
package cn.iocoder.mall.promotionservice.service.recommend.bo; package cn.iocoder.mall.promotionservice.service.recommend.bo;
import cn.iocoder.common.framework.validator.InEnum; import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.ProductRecommendTypeEnum; import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
......
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon.CouponTemplateMapper">
<update id="updateStatFetchNumIncr" parameterType="Integer">
UPDATE coupon_template
SET stat_fetch_Num = stat_fetch_Num + 1
WHERE id = #{id}
AND total > stat_fetch_Num
</update>
</mapper>
package cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity; package cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity;
import cn.iocoder.mall.promotion.api.enums.PromotionActivityStatusEnum; import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO; import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
......
package cn.iocoder.mall.promotion.biz.dao;
import cn.iocoder.mall.promotion.biz.dataobject.CouponCardDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CouponCardMapper {
CouponCardDO selectById(@Param("id") Integer id);
List<CouponCardDO> selectListByUserIdAndStatus(@Param("userId") Integer userId,
@Param("status") Integer status);
List<CouponCardDO> selectListByPage(@Param("userId") Integer userId,
@Param("status") Integer status,
@Param("offset") Integer offset,
@Param("limit") Integer limit);
Integer selectCountByPage(@Param("userId") Integer userId,
@Param("status") Integer status);
int selectCountByUserIdAndTemplateId(@Param("userId") Integer userId,
@Param("templateId") Integer templateId);
void insert(CouponCardDO couponCardDO);
int update(CouponCardDO couponCardDO);
int updateByIdAndStatus(@Param("id") Integer id,
@Param("status") Integer status,
@Param("updateObj") CouponCardDO updateObj);
}
package cn.iocoder.mall.promotion.biz.dao;
import cn.iocoder.mall.promotion.biz.dataobject.CouponTemplateDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
@Repository
public interface CouponTemplateMapper {
CouponTemplateDO selectById(@Param("id") Integer id);
List<CouponTemplateDO> selectListByIds(@Param("ids") Collection<Integer> ids);
List<CouponTemplateDO> selectListByPage(@Param("type") Integer type,
@Param("title") String title,
@Param("status") Integer status,
@Param("preferentialType") Integer preferentialType,
@Param("offset") Integer offset,
@Param("limit") Integer limit);
Integer selectCountByPage(@Param("type") Integer type,
@Param("title") String title,
@Param("status") Integer status,
@Param("preferentialType") Integer preferentialType);
void insert(CouponTemplateDO couponTemplate);
int update(CouponTemplateDO couponTemplate);
int updateStatFetchNumIncr(@Param("id") Integer id);
}
package cn.iocoder.mall.promotion.biz.dataobject;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 优惠劵 DO
*/
@Data
@Accessors(chain = true)
public class CouponCardDO extends BaseDO {
// ========== 基本信息 BEGIN ==========
/**
* 优惠劵编号
*/
private Integer id;
/**
* 优惠劵(码)分组编号,{@link CouponTemplateDO} 的 id
*/
private Integer templateId;
/**
* 优惠劵名
*
* 冗余自 {@link CouponTemplateDO} 的 title
*
* TODO 芋艿,暂时不考虑冗余的更新
*/
private String title;
// /**
// * 核销码
// */
// private String verifyCode;
/**
* 优惠码状态
*
* 1-未使用
* 2-已使用
* 3-已失效
*/
private Integer status;
// ========== 基本信息 END ==========
// ========== 领取情况 BEGIN ==========
/**
* 用户编号
*/
private Integer userId;
/**
* 领取类型
*
* 1 - 用户主动领取
* 2 - 后台自动发放
*/
private Integer takeType;
// ========== 领取情况 END ==========
// ========== 使用规则 BEGIN ==========
/**
* 是否设置满多少金额可用,单位:分
*/
private Integer priceAvailable;
/**
* 生效开始时间
*/
private Date validStartTime;
/**
* 生效结束时间
*/
private Date validEndTime;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
/**
* 优惠类型
*
* 1-代金卷
* 2-折扣卷
*/
private Integer preferentialType;
/**
* 折扣
*/
private Integer percentOff;
/**
* 优惠金额,单位:分。
*/
private Integer priceOff;
/**
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
*
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
*/
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 使用情况 BEGIN ==========
// /**
// * 使用订单号
// */
// private Integer usedOrderId; // TODO 芋艿,暂时不考虑这个字段
// /**
// * 订单中优惠面值,单位:分
// */
// private Integer usedPrice; // TODO 芋艿,暂时不考虑这个字段
/**
* 使用时间
*/
private Date usedTime;
// TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
// ========== 使用情况 END ==========
}
package cn.iocoder.mall.promotion.biz.dataobject;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 优惠码
*/
@Data
@Accessors(chain = true)
public class CouponCodeDO extends BaseDO {
/**
* 编号
*/
private Integer id;
/**
* 模板编号 {@link CouponTemplateDO} 的 id
*/
private Integer templateId;
/**
* 优惠码
*/
private Integer code;
/**
* 领取时间
*/
private Date takeTime;
/**
* 领取用户编号
*/
private Integer userId;
/**
* 领取的优惠劵编号
*/
private Integer couponId;
// TODO 芋艿,后续要考虑状态的追踪
}
package cn.iocoder.mall.promotion.biz.dataobject;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 优惠劵(码)模板 DO
*
* 当用户领取时,会生成 {@link CouponCardDO} 优惠劵(码)。
*/
@Data
@Accessors(chain = true)
public class CouponTemplateDO extends BaseDO {
// ========== 基本信息 BEGIN ==========
/**
* 模板编号,自增唯一。
*/
private Integer id;
/**
* 标题
*/
private String title;
/**
* 使用说明
*/
private String description;
/**
* 类型
*
* 1-优惠劵
* 2-优惠码
*/
private Integer type;
/**
* 优惠码状态
*
* {@link cn.iocoder.mall.promotion.api.enums.CouponTemplateStatusEnum}
*
* 当优惠劵(码)开启中,可以手动操作,设置禁用中。
*/
private Integer status;
// /**
// * 是否可分享领取链接
// */
// private Boolean isShare;
// /**
// * 设置为失效时间
// */
// private Date invalidTime;
// /**
// * 删除时间
// */
// private Date deleteTime;
// ========== 基本信息 END ==========
// ========== 领取规则 BEGIN ==========
// /**
// * 是否限制领用者的等级
// *
// * 0-不限制
// * 大于0-领用者必须是这个等级编号
// *
// * 【优惠劵独有】
// */
// private Integer needUserLevel;
/**
* 每人限领个数
*
* null - 则表示不限制
*/
private Integer quota;
/**
* 发行总量
*/
private Integer total;
// ========== 领取规则 END ==========
// ========== 使用规则 BEGIN ==========
// /**
// * 是否仅原价购买商品时可用
// *
// * true-是
// * false-否
// */
// private Boolean isForbidPreference;
/**
* 是否设置满多少金额可用,单位:分
*
* 0-不限制
* 大于0-多少金额可用
*/
private Integer priceAvailable;
/**
* 可用范围的类型
*
* 10-全部(ALL):所有可用
* 20-部分(PART):部分商品可用,或指定商品可用
* 21-部分(PART):部分商品不可用,或指定商品可用
* 30-部分(PART):部分分类可用,或指定商品可用
* 31-部分(PART):部分分类不可用,或指定商品可用
*/
private Integer rangeType;
/**
* 指定商品 / 分类列表,使用逗号分隔商品编号
*/
private String rangeValues;
/**
* 生效日期类型
*
* 1-固定日期
* 2-领取日期:领到券 {@link #fixedStartTerm} 日开始 N 天内有效
*/
private Integer dateType;
/**
* 固定日期-生效开始时间
*/
private Date validStartTime;
/**
* 固定日期-生效结束时间
*/
private Date validEndTime;
/**
* 领取日期-开始天数
*
* 例如,0-当天;1-次天
*/
private Integer fixedStartTerm;
/**
* 领取日期-结束天数
*/
private Integer fixedEndTerm;
// /**
// * 是否到期前4天发送提醒
// *
// * true-发送
// * false-不发送
// */
// private Boolean expireNotice;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
/**
* 优惠类型
*
* 1-代金卷
* 2-折扣卷
*/
private Integer preferentialType;
/**
* 折扣百分比。
*
* 例如,80% 为 80。
* 当 100% 为 100 ,则代表免费。
*/
private Integer percentOff;
// /**
// * 是否是随机优惠券
// *
// * true-随机
// * false-不随机
// *
// * 【优惠劵独有】
// */
// private Boolean isRandom;
/**
* 优惠金额,单位:分
*/
// * 当 {@link #isRandom} 为 true 时,代表随机优惠金额的下限
private Integer priceOff;
// /**
// * 优惠金额上限
// *
// * 【优惠劵独有】
// */
// private Integer valueRandomTo;
/**
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
*
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
*/
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 统计信息 BEGIN ==========
// /**
// * 领取优惠券的人数
// */
// private Integer statFetchUserNum;
/**
* 领取优惠券的次数
*/
private Integer statFetchNum;
// /**
// * 使用优惠券的次数
// */
// private Integer statUseNum;
// ========== 统计信息 END ==========
// ========== 优惠码 BEGIN ==========
/**
* 码类型
*
* 1-一卡一码(UNIQUE)
* 2-通用码(GENERAL)
*
* 【优惠码独有】 @see CouponCodeDO
*/
private Integer codeType;
/**
* 通用码
*/
private String commonCode;
// ========== 优惠码 BEGIN ==========
}
package cn.iocoder.mall.promotion.biz.mybatis;
import cn.iocoder.mall.mybatis.core.type.JSONTypeHandler;
import cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO;
public class TestHandler extends JSONTypeHandler<PromotionActivityDO.TimeLimitedDiscount> {
public TestHandler(Class<PromotionActivityDO.TimeLimitedDiscount> clazz) {
super(clazz);
}
}
package cn.iocoder.mall.promotion.biz.scheduler;
/**
* 优惠劵过期 Job
*
* TODO 芋艿
*/
public class CouponCardExpireJob {
}
spring:
# datasource
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://s1.iocoder.cn:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 3WLiVUBEwTbvAfsh
# Spring Cloud 配置项
cloud:
nacos:
# Spring Cloud Nacos Discovery 配置项
discovery:
server-addr: s1.iocoder.cn:8848 # Nacos 服务器地址
# mybatis-plus
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: auto
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: cn.iocoder.mall.promotion.biz.dataobject
# Dubbo 配置项
dubbo:
# Dubbo 注册中心
registry:
address: spring-cloud://s1.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
# Spring Cloud Alibaba Dubbo 专属配置
cloud:
subscribed-services: admin-application, product-application # 设置订阅的应用列表,默认为 * 订阅所有应用
# Dubbo 提供者的协议
protocol:
name: dubbo
port: -1
# Dubbo 提供服务的扫描基础包
scan:
base-packages: cn.iocoder.mall.promotion.biz.service
consumer:
ProductSpuService:
version: 1.0.0
# Dubbo 服务提供者的配置
provider:
filter: -exception
BannerService:
version: 1.0.0
CouponService:
version: 1.0.0
ProductRecommendService:
version: 1.0.0
PromotionActivityService:
version: 1.0.0
# Seata 配置项
seata:
tx-service-group: default # Seata 事务组编号,用于 TC 集群名
# 服务配置项,对应 ServiceProperties 类
service:
# 虚拟组和分组的映射
vgroup-mapping:
default: default
# Seata 注册中心配置项
registry:
type: nacos # 注册中心类型
nacos:
serverAddr: ${spring.cloud.nacos.discovery.server-addr} # Nacos 服务地址
namespace: # Nacos 命名空间
cluster: default # 使用的 Seata 分组
# logging
logging:
level:
cn.iocoder.mall.promotion.dao: debug
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.iocoder.mall.promotion.biz.dao.CouponCardMapper">
<sql id="FIELDS">
id, template_id, title, status, user_id, take_type,
price_available, valid_start_time, valid_end_time, preferential_type, percent_off, price_off,
discount_price_limit, used_time,
create_time
</sql>
<!-- <select id="selectListByPidAndStatusOrderBySort" resultType="CouponCardDO">-->
<!-- SELECT-->
<!-- <include refid="FIELDS" />-->
<!-- FROM coupon_card-->
<!-- WHERE pid = #{pid}-->
<!-- AND status = #{status}-->
<!-- AND deleted = 0-->
<!-- ORDER BY sort ASC-->
<!-- </select>-->
<!-- <select id="selectList" resultType="CouponCardDO">-->
<!-- SELECT-->
<!-- <include refid="FIELDS" />-->
<!-- FROM coupon_card-->
<!-- WHERE deleted = 0-->
<!-- </select>-->
<select id="selectById" parameterType="Integer" resultType="CouponCardDO">
SELECT
<include refid="FIELDS" />
FROM coupon_card
WHERE id = #{id}
</select>
<select id="selectListByUserIdAndStatus" resultType="CouponCardDO">
SELECT
<include refid="FIELDS" />
FROM coupon_card
<where>
<if test="userId != null">
AND user_id = #{userId}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>
<select id="selectListByPage" resultType="CouponCardDO">
SELECT
<include refid="FIELDS" />
FROM coupon_card
<where>
<if test="userId != null">
AND user_id = #{userId}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
LIMIT #{offset}, #{limit}
</select>
<select id="selectCountByPage" resultType="Integer">
SELECT
COUNT(1)
FROM coupon_card
<where>
<if test="userId != null">
AND user_id = #{userId}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>
<select id="selectCountByUserIdAndTemplateId" resultType="Integer">
SELECT
COUNT(1)
FROM coupon_card
<where>
<if test="userId != null">
AND user_id = #{userId}
</if>
<if test="templateId != null">
AND template_id = #{templateId}
</if>
</where>
</select>
<insert id="insert" parameterType="CouponCardDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
INSERT INTO coupon_card (
template_id, title, status, user_id, take_type,
price_available, valid_start_time, valid_end_time, preferential_type, percent_off, price_off,
discount_price_limit, used_time,
create_time
) VALUES (
#{templateId}, #{title}, #{status}, #{userId}, #{takeType},
#{priceAvailable}, #{validStartTime}, #{validEndTime}, #{preferentialType}, #{percentOff}, #{priceOff},
#{discountPriceLimit}, #{usedTime},
#{createTime}
)
</insert>
<update id="update" parameterType="CouponCardDO">
UPDATE coupon_card
<set>
<if test="status != null">
status = #{status},
</if>
<if test="usedTime != null">
used_time = #{usedTime},
</if>
</set>
WHERE id = #{id}
</update>
<update id="updateByIdAndStatus">
UPDATE coupon_card
<set>
<if test="updateObj.status != null">
status = #{updateObj.status},
</if>
<if test="updateObj.usedTime != null">
used_time = #{updateObj.usedTime},
</if>
</set>
WHERE id = #{id}
AND status = #{status}
</update>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.iocoder.mall.promotion.biz.dao.CouponTemplateMapper">
<sql id="FIELDS">
id, title, description, type, code_type,
status, quota, total, price_available, range_type,
range_values, date_type, valid_start_time, valid_end_time, fixed_start_term, fixed_end_term,
preferential_type, percent_off, price_off, discount_price_limit, stat_fetch_num,
create_time
</sql>
<!-- <select id="selectListByPidAndStatusOrderBySort" resultType="CouponTemplateDO">-->
<!-- SELECT-->
<!-- <include refid="FIELDS" />-->
<!-- FROM coupon_template-->
<!-- WHERE pid = #{pid}-->
<!-- AND status = #{status}-->
<!-- AND deleted = 0-->
<!-- ORDER BY sort ASC-->
<!-- </select>-->
<!-- <select id="selectList" resultType="CouponTemplateDO">-->
<!-- SELECT-->
<!-- <include refid="FIELDS" />-->
<!-- FROM coupon_template-->
<!-- WHERE deleted = 0-->
<!-- </select>-->
<select id="selectById" parameterType="Integer" resultType="CouponTemplateDO">
SELECT
<include refid="FIELDS" />
FROM coupon_template
WHERE id = #{id}
</select>
<select id="selectListByIds" resultType="CouponTemplateDO">
SELECT
<include refid="FIELDS"/>
FROM coupon_template
WHERE id IN
<foreach item="id" collection="ids" separator="," open="(" close=")" index="">
#{id}
</foreach>
</select>
<select id="selectListByPage" resultType="CouponTemplateDO">
SELECT
<include refid="FIELDS" />
FROM coupon_template
<where>
<if test="type != null">
AND type = #{type}
</if>
<if test="title != null">
AND title LIKE "%"#{title}"%"
</if>
<if test="status != null">
AND status = #{status}
</if>
<if test="preferentialType != null">
AND preferential_type = #{preferentialType}
</if>
</where>
LIMIT #{offset}, #{limit}
</select>
<select id="selectCountByPage" resultType="Integer">
SELECT
COUNT(1)
FROM coupon_template
<where>
<if test="type != null">
AND type = #{type}
</if>
<if test="title != null">
AND title LIKE "%"#{title}"%"
</if>
<if test="status != null">
AND status = #{status}
</if>
<if test="preferentialType != null">
AND preferential_type = #{preferentialType}
</if>
</where>
</select>
<insert id="insert" parameterType="CouponTemplateDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
INSERT INTO coupon_template (
title, description, type, code_type,
status, quota, total, price_available, range_type,
range_values, date_type, valid_start_time, valid_end_time, fixed_start_term, fixed_end_term,
preferential_type, percent_off, price_off, discount_price_limit, stat_fetch_num,
create_time
) VALUES (
#{title}, #{description}, #{type}, #{codeType},
#{status}, #{quota}, #{total}, #{priceAvailable}, #{rangeType},
#{rangeValues}, #{dateType}, #{validStartTime}, #{validEndTime}, #{fixedStartTerm}, #{fixedEndTerm}
#{preferentialType}, #{percentOff}, #{priceOff}, #{discountPriceLimit}, #{statFetchNum},
#{createTime}
)
</insert>
<update id="update" parameterType="CouponTemplateDO">
UPDATE coupon_template
<set>
<if test="title != null">
title = #{title},
</if>
<if test="description != null">
description = #{description},
</if>
<if test="status != null">
status = #{status},
</if>
<if test="quota != null">
quota = #{quota},
</if>
<if test="total != null">
total = #{total},
</if>
<if test="priceAvailable != null">
price_available = #{priceAvailable},
</if>
<if test="rangeType != null">
range_type = #{rangeType},
</if>
<if test="rangeValues != null">
range_values = #{rangeValues},
</if>
<if test="dateType != null">
date_type = #{dateType},
</if>
<if test="validStartTime != null">
valid_start_time = #{validStartTime},
</if>
<if test="validEndTime != null">
valid_end_time = #{validEndTime},
</if>
<if test="fixedStartTerm != null">
fixed_start_term = #{fixedStartTerm},
</if>
<if test="fixedEndTerm != null">
fixed_end_term = #{fixedEndTerm},
</if>
<if test="preferentialType != null">
preferential_type = #{preferentialType},
</if>
<if test="percentOff != null">
percent_off = #{percentOff},
</if>
<if test="priceOff != null">
price_off = #{priceOff},
</if>
<if test="discountPriceLimit != null">
discount_price_limit = #{discountPriceLimit},
</if>
</set>
WHERE id = #{id}
</update>
<update id="updateStatFetchNumIncr" parameterType="Integer">
UPDATE coupon_template
SET stat_fetch_Num = stat_fetch_Num + 1
WHERE id = #{id}
AND total > stat_fetch_Num
</update>
</mapper>
package cn.iocoder.mall.promotion.biz;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.promotion"})
public class Application {
}
package cn.iocoder.mall.promotion.biz.dao;
import cn.iocoder.common.framework.util.DateUtil;
import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum;
import cn.iocoder.mall.promotion.api.enums.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotion.api.enums.PromotionActivityTypeEnum;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO;
import org.junit.Ignore;
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.ArrayList;
import java.util.Calendar;
import java.util.Date;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class PromotionActivityMapperTest {
@Autowired
private PromotionActivityMapper promotionActivityMapper;
/**
* 插入限时折扣活动
*/
@Test
@Ignore
public void testInsert01() {
// 创建 PromotionActivityDO 对象
PromotionActivityDO activityDO = new PromotionActivityDO();
activityDO.setTitle("老板跑路了");
activityDO.setActivityType(PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue());
activityDO.setStatus(PromotionActivityStatusEnum.RUN.getValue());
activityDO.setStartTime(new Date());
activityDO.setEndTime(DateUtil.addDate(new Date(), Calendar.DAY_OF_YEAR, 100));
activityDO.setCreateTime(new Date());
// 创建 TimeLimitedDiscount 对象
PromotionActivityDO.TimeLimitedDiscount discount = new PromotionActivityDO.TimeLimitedDiscount();
discount.setQuota(0);
discount.setItems(new ArrayList<>());
PromotionActivityDO.TimeLimitedDiscount.Item item01 = new PromotionActivityDO.TimeLimitedDiscount.Item();
item01.setSpuId(32);
item01.setPreferentialType(PreferentialTypeEnum.DISCOUNT.getValue());
item01.setPreferentialValue(40);
discount.getItems().add(item01);
activityDO.setTimeLimitedDiscount(discount);
promotionActivityMapper.insert(activityDO);
}
/**
* 插入满减送活动
*/
@Test
public void testInsert02() {
// 创建 PromotionActivityDO 对象
PromotionActivityDO activityDO = new PromotionActivityDO();
activityDO.setTitle("老四赶海");
activityDO.setActivityType(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue());
activityDO.setStatus(PromotionActivityStatusEnum.RUN.getValue());
activityDO.setStartTime(new Date());
activityDO.setEndTime(DateUtil.addDate(new Date(), Calendar.DAY_OF_YEAR, 100));
activityDO.setCreateTime(new Date());
// 创建 TimeLimitedDiscount 对象
PromotionActivityDO.FullPrivilege fullPrivilege = new PromotionActivityDO.FullPrivilege();
fullPrivilege.setRangeType(RangeTypeEnum.ALL.getValue());
fullPrivilege.setCycled(Boolean.FALSE);
fullPrivilege.setPrivileges(new ArrayList<>());
PromotionActivityDO.FullPrivilege.Privilege privilege01 = new PromotionActivityDO.FullPrivilege.Privilege();
privilege01.setMeetType(1); // TODO 芋艿,硬编码
privilege01.setMeetValue(20);
privilege01.setPreferentialType(PreferentialTypeEnum.DISCOUNT.getValue());
privilege01.setPreferentialValue(80);
fullPrivilege.getPrivileges().add(privilege01);
activityDO.setFullPrivilege(fullPrivilege);
promotionActivityMapper.insert(activityDO);
}
/**
* 查询促销活动
*/
@Test
public void testSelectById() {
PromotionActivityDO activity01 = promotionActivityMapper.selectById(1);
System.out.println(activity01);
PromotionActivityDO activity02 = promotionActivityMapper.selectById(2);
System.out.println(activity02);
}
}
package cn.iocoder.mall.promotion.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.promotion"})
public class PromotionApplication {
public static void main(String[] args) {
SpringApplication.run(PromotionApplication.class, args);
}
}
package cn.iocoder.mall.promotion.application.convert;
import cn.iocoder.mall.promotion.api.bo.CouponCardBO;
import cn.iocoder.mall.promotion.api.bo.CouponCardPageBO;
import cn.iocoder.mall.promotion.application.vo.users.UsersCouponCardPageVO;
import cn.iocoder.mall.promotion.application.vo.users.UsersCouponCardVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CouponCardConvert {
CouponCardConvert INSTANCE = Mappers.getMapper(CouponCardConvert.class);
@Mappings({})
UsersCouponCardVO convert(CouponCardBO result);
@Mappings({})
UsersCouponCardPageVO convert2(CouponCardPageBO result);
//
// @Mappings({})
// List<UsersCouponTemplateVO> convertList2(List<CouponTemplateBO> banners);
}
package cn.iocoder.mall.promotion.application.convert;
import cn.iocoder.mall.promotion.api.bo.CouponTemplateBO;
import cn.iocoder.mall.promotion.api.bo.CouponTemplatePageBO;
import cn.iocoder.mall.promotion.application.vo.admins.AdminsCouponTemplatePageVO;
import cn.iocoder.mall.promotion.application.vo.admins.AdminsCouponTemplateVO;
import cn.iocoder.mall.promotion.application.vo.users.UsersCouponTemplateVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface CouponTemplateConvert {
Users USERS = Mappers.getMapper(Users.class);
Admins ADMINS = Mappers.getMapper(Admins.class);
@Mapper
interface Admins {
@Mappings({})
AdminsCouponTemplateVO convert(CouponTemplateBO template);
@Mappings({})
AdminsCouponTemplatePageVO convertPage(CouponTemplatePageBO result);
@Mappings({})
List<AdminsCouponTemplateVO> convertList(List<CouponTemplateBO> templates);
}
@Mapper
interface Users {
@Mappings({})
UsersCouponTemplateVO convert2(CouponTemplateBO template);
}
}
package cn.iocoder.mall.promotion.application.vo.users;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("优惠劵分页 VO")
@Data
@Accessors(chain = true)
public class UsersCouponCardPageVO {
@ApiModelProperty(value = "优惠劵数组")
private List<UsersCouponCardVO> list;
@ApiModelProperty(value = "优惠劵总数")
private Integer total;
}
...@@ -3,7 +3,7 @@ package cn.iocoder.mall.shopweb.controller.order; ...@@ -3,7 +3,7 @@ package cn.iocoder.mall.shopweb.controller.order;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder; import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.order.vo.cart.CartDetailVO; 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 cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
......
### /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}}
###
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; ...@@ -8,7 +8,7 @@ import lombok.experimental.Accessors;
import java.util.List; import java.util.List;
@ApiModel(value = "用户的购物车明细 VO") @ApiModel(value = "用户的购物车明细 Response VO") // TODO 芋艿:swagger 文档完善
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class CartDetailVO { public class CartDetailVO {
......
package cn.iocoder.mall.order.rest.response; package cn.iocoder.mall.shopweb.controller.order.vo.order;
import java.util.List; 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.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.util.List;
@ApiModel(value = "订单确认创建信息 Response VO") // TODO 芋艿:swagger 文档完善
@Data @Data
@Accessors(chain = true) @Accessors(chain = true) // TODO 芋艿:和 CartDetailVO、ProductSkuCalcPriceRespVO 有点重复,后续要优化下;
public class UsersOrderConfirmCreateResponse { public class OrderConfirmCreateInfoRespVO {
/** /**
* 商品分组数组 * 商品分组数组
...@@ -16,14 +23,12 @@ public class UsersOrderConfirmCreateResponse { ...@@ -16,14 +23,12 @@ public class UsersOrderConfirmCreateResponse {
* 费用 * 费用
*/ */
private Fee fee; private Fee fee;
/** /**
* 优惠劵列表 TODO 芋艿,后续改改 * 优惠劵列表 TODO 芋艿,后续改改
*/ */
// private List<CouponCardAvailableBO> couponCards; private List<CouponCardAvailableRespDTO> couponCards;
/**
* 优惠劵优惠金额
*/
private Integer couponCardDiscountTotal;
/** /**
* 商品分组 * 商品分组
* *
...@@ -36,13 +41,18 @@ public class UsersOrderConfirmCreateResponse { ...@@ -36,13 +41,18 @@ public class UsersOrderConfirmCreateResponse {
/** /**
* 优惠活动 * 优惠活动
*/ */
// TODO 芋艿,目前只会有【满减送】的情况,未来有新的促销方式,可能需要改成数组 private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
// TODO 芋艿,后面改成 VO /**
// private PromotionActivityBO activity; * 促销减少的金额
*
* 1. 若未参与促销活动,或不满足促销条件,返回 null
* 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
*/
private Integer activityDiscountTotal;
/** /**
* 商品数组 * 商品数组
*/ */
private List<Sku> items; private List<CartDetailVO.Sku> items;
} }
...@@ -66,7 +76,7 @@ public class UsersOrderConfirmCreateResponse { ...@@ -66,7 +76,7 @@ public class UsersOrderConfirmCreateResponse {
/** /**
* 规格值数组 * 规格值数组
*/ */
// private List<ProductAttrAndValuePairBO> attrs; // TODO 后面改下 private List<ProductAttrKeyValueRespVO> attrs; // TODO 后面改下
/** /**
* 价格,单位:分 * 价格,单位:分
*/ */
...@@ -82,6 +92,10 @@ public class UsersOrderConfirmCreateResponse { ...@@ -82,6 +92,10 @@ public class UsersOrderConfirmCreateResponse {
* 购买数量 * 购买数量
*/ */
private Integer buyQuantity; private Integer buyQuantity;
/**
* 优惠活动
*/
private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
/** /**
* 原始单价,单位:分。 * 原始单价,单位:分。
*/ */
...@@ -182,10 +196,11 @@ public class UsersOrderConfirmCreateResponse { ...@@ -182,10 +196,11 @@ public class UsersOrderConfirmCreateResponse {
this.postageTotal = postageTotal; this.postageTotal = postageTotal;
this.presentTotal = presentTotal; this.presentTotal = presentTotal;
} }
} }
/** /**
* 邮费信息 * 邮费信息 TODO 芋艿,未完成
*/ */
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
......
### /product-spu/page 计算商品 SKU 价格
GET {{shop-api-base-url}}/product-sku/cal-price?id=33
Content-Type: application/x-www-form-urlencoded
###
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));
}
}
package cn.iocoder.mall.order.api.bo; package cn.iocoder.mall.shopweb.controller.product.vo.sku;
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO; import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import io.swagger.annotations.ApiModel;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable; @ApiModel("计算商品 SKU 价格结果 VO")
/**
* 计算商品 SKU 价格结果 BO
*/
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class CalcSkuPriceBO implements Serializable { // TODO 芋艿:swagger 注解
public class ProductSkuCalcPriceRespVO {
/** /**
* 满减送促销活动 * 原价格,单位:分。
*/ */
private PromotionActivityBO fullPrivilege; private Integer originalPrice;
/** /**
* 限时折扣促销活动 * 最终价格,单位:分。
*/ */
private PromotionActivityBO timeLimitedDiscount; private Integer buyPrice;
/** /**
* 原价格,单位:分。 * 满减送促销活动
*
* TODO 芋艿,后续改成 VO
*/ */
private Integer originalPrice; private PromotionActivityRespDTO fullPrivilege;
/** /**
* 购买价格,单位:分。 * 限时折扣促销活动
*
* TODO 芋艿,后续改成 VO
*/ */
private Integer buyPrice; private PromotionActivityRespDTO timeLimitedDiscount;
} }
### /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
###
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));
}
}
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;
}
package cn.iocoder.mall.promotion.application.vo.users; package cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.util.Date; import java.util.Date;
/** @ApiModel("优惠劵 Response VO")
* 优惠劵 VO
*/
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class UsersCouponCardVO { public class CouponCardRespVO {
// ========== 基本信息 BEGIN ========== // ========== 基本信息 BEGIN ==========
@ApiModelProperty(value = "优惠劵编号", required = true, example = "1") @ApiModelProperty(value = "优惠劵编号", required = true, example = "1")
...@@ -47,8 +46,6 @@ public class UsersCouponCardVO { ...@@ -47,8 +46,6 @@ public class UsersCouponCardVO {
// ========== 使用情况 BEGIN ========== // ========== 使用情况 BEGIN ==========
// TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
// ========== 使用情况 END ========== // ========== 使用情况 END ==========
} }
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;
}
}
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);
}
}
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);
}
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 库存不足!");
}
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.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
...@@ -94,7 +94,7 @@ public class CartManager { ...@@ -94,7 +94,7 @@ public class CartManager {
* @return 商品列表 * @return 商品列表
*/ */
public CartDetailVO getCartDetail(Integer userId) { public CartDetailVO getCartDetail(Integer userId) {
// 获得购物车中选中的 // 获得购物车的商品
CommonResult<List<CartItemRespDTO>> listCartItemsResult = cartRpc.listCartItems(new CartItemListReqDTO().setUserId(userId)); CommonResult<List<CartItemRespDTO>> listCartItemsResult = cartRpc.listCartItems(new CartItemListReqDTO().setUserId(userId));
listCartItemsResult.checkError(); listCartItemsResult.checkError();
// 购物车为空时,构造空的 UsersOrderConfirmCreateVO 返回 // 购物车为空时,构造空的 UsersOrderConfirmCreateVO 返回
...@@ -104,7 +104,7 @@ public class CartManager { ...@@ -104,7 +104,7 @@ public class CartManager {
result.setFee(new CartDetailVO.Fee(0, 0, 0, 0)); result.setFee(new CartDetailVO.Fee(0, 0, 0, 0));
return result; return result;
} }
// 计算选中的商品价格 // 计算商品价格
CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceRpc.calcProductPrice(new PriceProductCalcReqDTO().setUserId(userId) CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceRpc.calcProductPrice(new PriceProductCalcReqDTO().setUserId(userId)
.setItems(listCartItemsResult.getData().stream() .setItems(listCartItemsResult.getData().stream()
.map(cartItem -> new PriceProductCalcReqDTO.Item(cartItem.getSkuId(), cartItem.getQuantity(), cartItem.getSelected())) .map(cartItem -> new PriceProductCalcReqDTO.Item(cartItem.getSkuId(), cartItem.getQuantity(), cartItem.getSelected()))
......
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);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论