将 onemall 老代码,统一到归档目录,后续不断迁移移除

This commit is contained in:
YunaiV
2022-06-16 09:06:44 +08:00
parent 64c478a45b
commit 71930d492e
1095 changed files with 0 additions and 16 deletions

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onemall</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>promotion-service-project</artifactId>
<packaging>pom</packaging>
<modules>
<module>promotion-service-api</module>
<module>promotion-service-app</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- onemall 基础 bom 文件 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 自身项目 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>promotion-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>promotion-service-project</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>promotion-service-api</artifactId>
<dependencies>
<!-- Mall 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 提供给 mapstruct 使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.promotion.api.enums;
/**
* 匹配类型枚举
*/
public enum MeetTypeEnum {
PRICE(1, "金额"),
QUANTITY(2, "数量"),;
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
MeetTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,46 @@
package cn.iocoder.mall.promotion.api.enums;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 优惠类型枚举
*/
public enum PreferentialTypeEnum implements IntArrayValuable {
PRICE(1, "减价"),
DISCOUNT(2, "打折"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PreferentialTypeEnum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
PreferentialTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,40 @@
package cn.iocoder.mall.promotion.api.enums;
import cn.iocoder.common.framework.exception.ErrorCode;
/**
* 错误码枚举类
*
* 营销系统,使用 1-006-000-000 段
*/
public interface PromotionErrorCodeConstants {
// ========== Banner 模块 ==========
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1006000000, "账号不存在");
// ========== PRODUCT RECOMMEND 模块 ==========
ErrorCode PRODUCT_RECOMMEND_NOT_EXISTS = new ErrorCode(1006001000, "商品推荐不存在");
ErrorCode PRODUCT_RECOMMEND_PRODUCT_NOT_EXISTS = new ErrorCode(1006001001, "商品不存在");
ErrorCode PRODUCT_RECOMMEND_EXISTS = new ErrorCode(1006001002, "该商品推荐已经存在");
// ========== COUPON TEMPLATE 模块 ==========
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1006002000, "优惠劵模板(码)不存在");
ErrorCode COUPON_TEMPLATE_NOT_CARD = new ErrorCode(1006002001, "不是优惠劵模板");
ErrorCode COUPON_TEMPLATE_NOT_CODE = new ErrorCode(1006002002, "不是优惠码模板");
ErrorCode COUPON_TEMPLATE_TOTAL_CAN_NOT_REDUCE = new ErrorCode(1006002003, "优惠劵(码)模板的发放数量不能减小");
ErrorCode COUPON_TEMPLATE_STATUS_NOT_ENABLE = new ErrorCode(1006002004, "优惠劵模板(码)未开启");
ErrorCode COUPON_TEMPLATE_TOTAL_NOT_ENOUGH = new ErrorCode(1006002005, "优惠劵(码)模板的发放量不足");
ErrorCode COUPON_TEMPLATE_CARD_ADD_EXCEED_QUOTA = new ErrorCode(1006002006, "优惠劵领取到达上限");
// ========== COUPON CARD 模块 ==========
ErrorCode COUPON_CARD_NOT_EXISTS = new ErrorCode(1006003000, "优惠劵不存在");
ErrorCode COUPON_CARD_ERROR_USER = new ErrorCode(1006003001, "优惠劵不属于当前用户");
ErrorCode COUPON_CARD_NOT_MATCH = new ErrorCode(1006003002, "优惠劵不匹配,无法使用");
ErrorCode COUPON_CARD_STATUS_NOT_UNUSED = new ErrorCode(1006003003, "优惠劵不处于待使用状态");
ErrorCode COUPON_CARD_STATUS_NOT_USED = new ErrorCode( 1006003004, "优惠劵不处于已使用状态");
// ========== PRICE 模块 ==========
ErrorCode PRICE_PRODUCT_SKU_NOT_EXISTS = new ErrorCode(1006004000, "有不存在的商品!");
}

View File

@@ -0,0 +1,49 @@
package cn.iocoder.mall.promotion.api.enums;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 可用范围的类型枚举
*/
public enum RangeTypeEnum implements IntArrayValuable {
ALL(10, "所有可用"),
PRODUCT_INCLUDE_PART(20, "部分商品可用,或指定商品可用"),
PRODUCT_EXCLUDE_PART(21, "部分商品不可用,或指定商品不可用"),
CATEGORY_INCLUDE_PART(30, "部分分类可用,或指定分类可用"),
CATEGORY_EXCLUDE_PART(31, "部分分类不可用,或指定分类不可用"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(RangeTypeEnum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
RangeTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,41 @@
package cn.iocoder.mall.promotion.api.enums.activity;
/**
* 促销活动状态枚举
*/
public enum PromotionActivityStatusEnum {
WAIT(10, "未开始"),
RUN(20, "进行中"),
END(30, "已结束"),
/**
* 1. WAIT、RUN、END 可以转换成 INVALID 状态。
* 2. INVALID 只可以转换成 DELETED 状态。
*/
INVALID(40, "已撤销"),
DELETED(50, "已删除"),
;
/**
* 状态值
*/
private final Integer value;
/**
* 状态名
*/
private final String name;
PromotionActivityStatusEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.mall.promotion.api.enums.activity;
/**
* 促销活动类型枚举
*/
public enum PromotionActivityTypeEnum {
TIME_LIMITED_DISCOUNT(1, "限时折扣"),
FULL_PRIVILEGE(2, "满减送"),
;
/**
* 类型值
*/
private final Integer value;
/**
* 类型名
*/
private final String name;
PromotionActivityTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.mall.promotion.api.enums.coupon.card;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 优惠劵状态枚举
*/
public enum CouponCardStatusEnum implements IntArrayValuable {
UNUSED(1, "未使用"),
USED(2, "已使用"),
EXPIRE(3, "已过期"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponCardStatusEnum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
CouponCardStatusEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,44 @@
package cn.iocoder.mall.promotion.api.enums.coupon.card;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 优惠劵领取方式
*/
public enum CouponCardTakeTypeEnum implements IntArrayValuable {
BY_USER(1, "用户主动领取"),
BY_ADMIN(2, "已使用"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponCardTakeTypeEnum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
CouponCardTakeTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,46 @@
package cn.iocoder.mall.promotion.api.enums.coupon.template;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 优惠劵(码)模板的时间类型的枚举
*/
public enum CouponTemplateDateTypeEnum implements IntArrayValuable {
FIXED_DATE(1, "固定日期"),
FIXED_TERM(2, "领取日期"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponTemplateDateTypeEnum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
CouponTemplateDateTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.mall.promotion.api.enums.coupon.template;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 优惠劵(码)模板的状态的枚举
*/
public enum CouponTemplateStatusEnum implements IntArrayValuable {
ENABLE(1, "生效中"),
DISABLE(2, "已失效"),
// EXPIRE(3, "已过期"), TODO 芋艿,暂时不考虑过期的
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponTemplateStatusEnum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
CouponTemplateStatusEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.mall.promotion.api.enums.coupon.template;
import java.util.Arrays;
/**
* 优惠劵(码)模板的类型的枚举
*/
public enum CouponTemplateTypeEnum {
CARD(1, "优惠劵"),
CODE(2, "折扣卷"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponTemplateTypeEnum::getValue).toArray();
/**
* 值
*/
private final Integer value;
/**
* 名字
*/
private final String name;
CouponTemplateTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,53 @@
package cn.iocoder.mall.promotion.api.enums.recommend;
import cn.iocoder.common.framework.core.IntArrayValuable;
import java.util.Arrays;
/**
* 商品推荐类型
*/
public enum ProductRecommendTypeEnum implements IntArrayValuable {
HOT(1, "热卖推荐"),
NEW(2, "新品推荐"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductRecommendTypeEnum::getValue).toArray();
/**
* 状态值
*/
private final Integer value;
/**
* 状态名
*/
private final String name;
ProductRecommendTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
public static boolean isValid(Integer status) {
if (status == null) {
return false;
}
return HOT.value.equals(status)
|| NEW.value.equals(status);
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.mall.promotion.api.rpc.activity;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient("promotion-service")
public interface PromotionActivityFeign {
@PostMapping("/promotion/activity/pagePromotionActivity")
CommonResult<PageResult<PromotionActivityRespDTO>> pagePromotionActivity(@RequestBody PromotionActivityPageReqDTO pageReqDTO) ;
@PostMapping("/promotion/activity/listPromotionActivities")
CommonResult<List<PromotionActivityRespDTO>> listPromotionActivities(@RequestBody PromotionActivityListReqDTO listReqDTO) ;
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.promotion.api.rpc.activity.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Collection;
/**
* 促销活动列表查询的 Request DTO
*/
@Data
@Accessors(chain = true)
public class PromotionActivityListReqDTO implements Serializable {
/**
* 活动编号数组
*/
private Collection<Integer> activeIds;
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.promotion.api.rpc.activity.dto;
import cn.iocoder.common.framework.vo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Collection;
/**
* 促销活动分页 Request DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class PromotionActivityPageReqDTO extends PageParam {
/**
* 标题
*
* 模糊匹配
*/
private String title;
/**
* 活动类型
*/
private Integer activityType;
/**
* 状态
*/
private Collection<Integer> statuses;
}

View File

@@ -0,0 +1,160 @@
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.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 促销活动 Response DTO
*/
@Data
@Accessors(chain = true)
public class PromotionActivityRespDTO implements Serializable {
/**
* 活动编号
*/
private Integer id;
/**
* 活动标题
*/
private String title;
/**
* 活动类型
*
* 参见 {@link PromotionActivityTypeEnum} 枚举
*/
private Integer activityType;
/**
* 活动状态
*
* 参见 {@link PromotionActivityStatusEnum} 枚举
*/
private Integer status;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 限制折扣
*/
private TimeLimitedDiscount timeLimitedDiscount;
/**
* 满减送
*/
private FullPrivilege fullPrivilege;
/**
* 创建时间
*/
private Date createTime;
/**
* 限时折扣
*/
@Data
@Accessors(chain = true)
public static class TimeLimitedDiscount implements Serializable {
/**
* 商品折扣
*/
@Data
@Accessors(chain = true)
public static class Item implements Serializable {
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 优惠类型
*/
private Integer preferentialType;
/**
* 优惠值
*/
private Integer preferentialValue;
}
/**
* 每人每种限购多少
*
* 当 quota = 0 时,表示不限购
*/
private Integer quota;
/**
* 商品折扣数组
*/
private List<Item> items;
}
/**
* 满减送
*/
@Data
@Accessors(chain = true)
public static class FullPrivilege implements Serializable {
/**
* 优惠
*/
@Data
@Accessors(chain = true)
public static class Privilege implements Serializable {
/**
* 满足类型
*
* 1 - 金额
* 2 - 件数
*/
private Integer meetType;
/**
* 满足值
*/
private Integer meetValue;
/**
* 优惠类型
*/
private Integer preferentialType;
/**
* 优惠值
*/
private Integer preferentialValue;
}
/**
* 可用范围的类型
*
* 参见 {@link cn.iocoder.mall.promotion.api.enums.RangeTypeEnum} 枚举
* 暂时只用 “所有可用” + “PRODUCT_INCLUDE_PRT”
*/
private Integer rangeType;
/**
* 指定可用商品列表
*/
private List<Integer> rangeValues;
/**
* 是否循环
*/
private Boolean cycled;
/**
* 优惠数组
*/
private List<Privilege> privileges;
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.mall.promotion.api.rpc.banner;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient("promotion-service")
public interface BannerFeign {
@PostMapping("/promotion/banner/createBanner")
public CommonResult<Integer> createBanner(@RequestBody BannerCreateReqDTO createDTO) ;
@PostMapping("/promotion/banner/updateBanner")
public CommonResult<Boolean> updateBanner(@RequestBody BannerUpdateReqDTO updateDTO) ;
@PostMapping("/promotion/banner/deleteBanner")
public CommonResult<Boolean> deleteBanner(@RequestBody Integer bannerId) ;
@PostMapping("/promotion/banner/listBanners")
public CommonResult<List<BannerRespDTO>> listBanners(@RequestBody BannerListReqDTO listDTO) ;
@PostMapping("/promotion/banner/pageBanner")
public CommonResult<PageResult<BannerRespDTO>> pageBanner(@RequestBody BannerPageReqDTO pageDTO);
}

View File

@@ -0,0 +1,58 @@
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Banner 创建 Request DTO
*/
@Data
@Accessors(chain = true)
public class BannerCreateReqDTO implements Serializable {
/**
* 标题
*/
@NotEmpty(message = "标题不能为空")
@Length(min = 2, max = 32, message = "标题长度为 2-32 位")
private String title;
/**
* 跳转链接
*/
@NotEmpty(message = "跳转链接不能为空")
@URL(message = "跳转链接格式不正确")
@Length(max = 255, message = "跳转链接最大长度为 255 位")
private String url;
/**
* 图片链接
*/
@NotEmpty(message = "图片链接不能为空")
@URL(message = "图片链接格式不正确")
@Length(max = 255, message = "图片链接最大长度为 255 位")
private String picUrl;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
private Integer sort;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
/**
* 备注
*/
@Length(max = 255, message = "备注最大长度为 255 位")
private String memo;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* Banner 列表 Request DTO
*/
@Data
@Accessors(chain = true)
public class BannerListReqDTO implements Serializable {
/**
* 状态
*/
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.vo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* Banner 分页 Request DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class BannerPageReqDTO extends PageParam {
/**
* 标题,模糊匹配
*/
private String title;
}

View File

@@ -0,0 +1,49 @@
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* Banner Response DTO
*/
@Data
@Accessors(chain = true)
public class BannerRespDTO implements Serializable {
/**
* 编号
*/
private Integer id;
/**
* 标题
*/
private String title;
/**
* 跳转链接
*/
private String url;
/**
* 图片链接
*/
private String picUrl;
/**
* 排序
*/
private Integer sort;
/**
* 状态
*/
private Integer status;
/**
* 备注
*/
private String memo;
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,63 @@
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Banner 更新 Request DTO
*/
@Data
@Accessors(chain = true)
public class BannerUpdateReqDTO implements Serializable {
/**
* 编号
*/
@NotNull(message = "编号不能为空")
private Integer id;
/**
* 标题
*/
@NotEmpty(message = "标题不能为空")
@Length(min = 2, max = 32, message = "标题长度为 2-32 位")
private String title;
/**
* 跳转链接
*/
@NotEmpty(message = "跳转链接不能为空")
@URL(message = "跳转链接格式不正确")
@Length(max = 255, message = "跳转链接最大长度为 255 位")
private String url;
/**
* 图片链接
*/
@NotEmpty(message = "图片链接不能为空")
@URL(message = "图片链接格式不正确")
@Length(max = 255, message = "图片链接最大长度为 255 位")
private String picUrl;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
private Integer sort;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
/**
* 备注
*/
@Length(max = 255, message = "备注最大长度为 255 位")
private String memo;
}

View File

@@ -0,0 +1,35 @@
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 org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient("promotion-service")
public interface CouponCardFeign {
@PostMapping("/coupon/card/pageCouponCard")
public CommonResult<PageResult<CouponCardRespDTO>> pageCouponCard(@RequestBody CouponCardPageReqDTO pageReqDTO) ;
@PostMapping("/coupon/card/createCouponCard")
public CommonResult<Integer> createCouponCard(@RequestBody CouponCardCreateReqDTO createReqDTO);
@PostMapping("/coupon/card/useCouponCard")
public CommonResult<Boolean> useCouponCard(@RequestBody CouponCardUseReqDTO useReqDTO) ;
@PostMapping("/coupon/card/cancelUseCouponCard")
public CommonResult<Boolean> cancelUseCouponCard(@RequestBody CouponCardCancelUseReqDTO cancelUseReqDTO) ;
@PostMapping("/coupon/card/listAvailableCouponCards")
public CommonResult<List<CouponCardAvailableRespDTO>> listAvailableCouponCards(@RequestBody CouponCardAvailableListReqDTO listReqDTO);
}

View File

@@ -0,0 +1,39 @@
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.template.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient("promotion-service")
public interface CouponTemplateFeign {
// ========== 通用逻辑 =========
@GetMapping("/coupon/template/getCouponTemplate")
public CommonResult<CouponTemplateRespDTO> getCouponTemplate(@RequestParam("couponTemplateId") Integer couponTemplateId);
@PostMapping("/coupon/template/pageCouponTemplate")
public CommonResult<PageResult<CouponTemplateRespDTO>> pageCouponTemplate(@RequestBody CouponTemplatePageReqDTO pageDTO);
@PostMapping("/coupon/template/updateCouponTemplateStatus")
public CommonResult<Boolean> updateCouponTemplateStatus(@RequestBody CouponCardTemplateUpdateStatusReqDTO updateStatusReqDTO) ;
// ========== 优惠劵模板 ==========
@PostMapping("/coupon/template/createCouponCardTemplate")
public CommonResult<Integer> createCouponCardTemplate(@RequestBody CouponCardTemplateCreateReqDTO createDTO) ;
@PostMapping("/coupon/template/updateCouponCardTemplate")
public CommonResult<Boolean> updateCouponCardTemplate(@RequestBody CouponCardTemplateUpdateReqDTO updateDTO) ;
}

View File

@@ -0,0 +1,68 @@
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;
}
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 可用优惠劵 BO
*
* 注意,如果优惠劵不可用,标记 available = false ,并写明 unavailableReason 原因
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class CouponCardAvailableRespDTO extends CouponCardRespDTO {
/**
* 是否可用
*/
private Boolean available;
/**
* 不可用原因
*/
private String unavailableReason;
}

View File

@@ -0,0 +1,27 @@
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 CouponCardCancelUseReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 优惠劵编号
*/
@NotNull(message = "优惠劵编号不能为空")
private Integer couponCardId;
}

View File

@@ -0,0 +1,27 @@
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;
}

View File

@@ -0,0 +1,25 @@
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;
}

View File

@@ -0,0 +1,110 @@
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.card;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 优惠劵 Response DTO
*/
@Data
@Accessors(chain = true)
public class CouponCardRespDTO 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;
// ========== 使用情况 END ==========
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,27 @@
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;
}

View File

@@ -0,0 +1,143 @@
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.template;
import cn.iocoder.common.framework.validator.InEnum;
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.RangeTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
* 优惠劵模板创建 Request DTO
*/
@Data
@Accessors(chain = true)
public class CouponCardTemplateCreateReqDTO implements Serializable {
// ========== 基本信息 BEGIN ==========
/**
* 标题
*/
@NotEmpty(message = "标题不能为空")
@Length(min = 2, max = 16, message = "标题长度为 {min}-{max} 位")
private String title;
/**
* 使用说明
*/
@Length(max = 255, message = "使用说明最大长度为 {max} 位")
private String description;
// ========== 基本信息 END ==========
// ========== 领取规则 BEGIN ==========
/**
* 每人限领个数
*/
@NotNull(message = "每人限领个数不能为空")
@Min(value = 1, message = "每人限领个数最小为 {value}")
private Integer quota;
/**
* 发放总量
*/
@NotNull(message = "发放总量不能为空")
@Min(value = 1, message = "每人限领个数最小为 {value}")
private Integer total;
// ========== 领取规则 END ==========
// ========== 使用规则 BEGIN ==========
/**
* 是否设置满多少金额可用,单位:分
*
* 0-不限制
* 大于0-多少金额可用
*/
@NotNull(message = "使用金额门槛不能为空")
@Min(value = 0L, message = "使用金额门槛最低为 {value}")
private Integer priceAvailable;
/**
* 可用范围的类型
*
* 10-全部ALL所有可用
* 20-部分PART部分商品可用或指定商品可用
* 21-部分PART部分商品不可用或指定商品可用
* 30-部分PART部分分类可用或指定分类可用
* 31-部分PART部分分类不可用或指定分类可用
*/
@NotNull(message = "可用范围的类型不能为空")
@InEnum(value = RangeTypeEnum.class, message = "可用范围的类型必须在 {value}")
private Integer rangeType;
/**
* 指定商品 / 分类列表,使用逗号分隔商品编号
*/
private String rangeValues;
/**
* 生效日期类型
*
* 1-固定日期
* 2-领取日期:领到券 {@link #fixedEndTerm} 日开始 N 天内有效
*/
@NotNull(message = "生效日期类型不能为空")
@InEnum(value = CouponTemplateDateTypeEnum.class, message = "生效日期类型必须在 {value}")
private Integer dateType;
/**
* 固定日期-生效开始时间
*/
private Date validStartTime;
/**
* 固定日期-生效结束时间
*/
private Date validEndTime;
/**
* 领取日期-开始天数
*
* 例如0-当天1-次天
*/
@Min(value = 0L, message = "领取日期开始时间最小为 {value}")
private Integer fixedStartTerm;
/**
* 领取日期-结束天数
*/
@Min(value = 1L, message = "领取日期结束时间最小为 {value}")
private Integer fixedEndTerm;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
/**
* 优惠类型
*
* 1-代金卷
* 2-折扣卷
*/
@NotNull(message = "优惠类型不能为空")
@InEnum(value = PreferentialTypeEnum.class, message = "优惠类型必须在 {value}")
private Integer preferentialType;
/**
* 优惠金额,单位:分
*/
@Min(value = 1, message = "优惠金额最小值为 {value}")
private Integer priceOff;
/**
* 折扣百分比。
*
* 例如80% 为 80。
* 当 100% 为 100 ,则代表免费。
*/
@Max(value = 100, message = "折扣比最大值为 {value}")
private Integer percentOff;
/**
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
*
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
*/
@Min(value = 1, message = "折扣上限最小值为 {value}")
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
}

View File

@@ -0,0 +1,142 @@
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.template;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 优惠劵模板更新 Request DTO
*/
@Data
@Accessors(chain = true)
public class CouponCardTemplateUpdateReqDTO implements Serializable {
@NotNull(message = "编号不能为空")
private Integer id;
// ========== 基本信息 BEGIN ==========
/**
* 标题
*/
@NotEmpty(message = "标题不能为空")
@Length(min = 2, max = 16, message = "标题长度为 {min}-{max} 位")
private String title;
/**
* 使用说明
*/
@Length(max = 255, message = "使用说明最大长度为 {max} 位")
private String description;
// ========== 基本信息 END ==========
// ========== 领取规则 BEGIN ==========
/**
* 每人限领个数
*/
@NotNull(message = "每人限领个数不能为空")
@Min(value = 1, message = "每人限领个数最小为 {value}")
private Integer quota;
/**
* 发放总量
*/
@NotNull(message = "发放总量不能为空")
@Min(value = 1, message = "每人限领个数最小为 {value}")
private Integer total;
// ========== 领取规则 END ==========
// ========== 使用规则 BEGIN ==========
// /**
// * 是否设置满多少金额可用,单位:分
// *
// * 0-不限制
// * 大于0-多少金额可用
// */
// @NotNull(message = "使用金额门槛不能为空")
// @Min(value = 0L, message = "使用金额门槛最低为 {value}")
// private Integer priceAvailable;
/**
* 可用范围的类型
*
* 10-全部ALL所有可用
* 20-部分PART部分商品可用或指定商品可用
* 21-部分PART部分商品不可用或指定商品可用
* 30-部分PART部分分类可用或指定分类可用
* 31-部分PART部分分类不可用或指定分类可用
*/
@NotNull(message = "可用范围的类型不能为空")
@InEnum(value = RangeTypeEnum.class, message = "可用范围的类型必须在 {value}")
private Integer rangeType;
/**
* 指定商品 / 分类列表,使用逗号分隔商品编号
*/
private String rangeValues;
// /**
// * 生效日期类型
// *
// * 1-固定日期
// * 2-领取日期:领到券 {@link #fixedEndTerm} 日开始 N 天内有效
// */
// @NotNull(message = "生效日期类型不能为空")
// @InEnum(value = CouponTemplateDateTypeEnum.class, message = "生效日期类型必须在 {value}")
// private Integer dateType;
// /**
// * 固定日期-生效开始时间
// */
// private Date validStartTime;
// /**
// * 固定日期-生效结束时间
// */
// private Date validEndTime;
// /**
// * 领取日期-开始天数
// *
// * 例如0-当天1-次天
// */
// @Min(value = 0L, message = "领取日期开始时间最小为 {value}")
// private Integer fixedStartTerm;
// /**
// * 领取日期-结束天数
// */
// @Min(value = 1L, message = "领取日期结束时间最小为 {value}")
// private Integer fixedEndTerm;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
// /**
// * 优惠类型
// *
// * 1-代金卷
// * 2-折扣卷
// */
// @NotNull(message = "优惠类型不能为空")
// @InEnum(value = CouponTemplatePreferentialTypeEnum.class, message = "优惠类型必须在 {value}")
// private Integer preferentialType;
// /**
// * 优惠金额,单位:分
// */
// @Min(value = 1, message = "优惠金额最小值为 {value}")
// private Integer priceOff;
// /**
// * 折扣百分比。
// *
// * 例如80% 为 80。
// * 当 100% 为 100 ,则代表免费。
// */
// @Max(value = 100, message = "折扣比最大值为 {value}")
// private Integer percentOff;
// /**
// * 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
// *
// * 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
// */
// @Min(value = 1, message = "折扣上限最小值为 {value}")
// private Integer discountPriceLimit;
// ========== 使用效果 END ==========
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.template;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateStatusEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 优惠劵(码)模板更新状态 Request DTO
*/
@Data
@Accessors(chain = true)
public class CouponCardTemplateUpdateStatusReqDTO implements Serializable {
/**
* 编号
*/
@NotNull(message = "编号不能为空")
private Integer id;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
@InEnum(value = CouponTemplateStatusEnum.class, message = "状态必须是 {value}")
private Integer status;
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.template;
import cn.iocoder.common.framework.vo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 优惠劵模板分页 Request DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class CouponTemplatePageReqDTO extends PageParam {
/**
* 类型
*/
private Integer type;
/**
* 标题
*/
private String title;
/**
* 状态
*/
private Integer status;
/**
* 优惠类型
*/
private Integer preferentialType;
}

View File

@@ -0,0 +1,156 @@
package cn.iocoder.mall.promotion.api.rpc.coupon.dto.template;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 优惠劵(码)模板 BO
*/
@Data
@Accessors(chain = true)
public class CouponTemplateRespDTO implements Serializable {
// ========== 基本信息 BEGIN ==========
/**
* 模板编号,自增唯一。
*/
private Integer id;
/**
* 标题
*/
private String title;
/**
* 使用说明
*/
private String description;
/**
* 类型
*
* 1-优惠劵
* 2-优惠码
*/
private Integer type;
/**
* 码类型
*
* 1-一卡一码UNIQUE
* 2-通用码GENERAL
*
* 【优惠码独有】 @see CouponCodeDO
*/
private Integer codeType;
/**
* 优惠码状态
*
* 1-开启中
* 2-禁用中
* 3-已过期
*
* 当优惠劵(码)开启中,可以手动操作,设置禁用中。
*/
private Integer status;
/**
* 每人限领个数
*
* null - 则表示不限制
*/
private Integer quota;
/**
* 发放总量
*/
private Integer total;
// ========== 领取规则 END ==========
// ========== 使用规则 BEGIN ==========
/**
* 是否设置满多少金额可用,单位:分
*
* 0-不限制
* 大于0-多少金额可用
*/
private Integer priceAvailable;
/**
* 可用范围的类型
*
* 10-全部ALL所有可用
* 20-部分PART部分商品可用或指定商品可用
* 21-部分PART部分商品不可用或指定商品可用
* 30-部分PART部分分类可用或指定商品可用
* 31-部分PART部分分类不可用或指定商品可用
*/
private Integer rangeType;
/**
* 指定商品 / 分类列表,使用逗号分隔商品编号
*/
private List<Integer> rangeValues;
/**
* 生效日期类型
*
* 1-固定日期
* 2-领取日期:领到券 {@link #fixedStartTerm} 日开始 N 天内有效
*/
private Integer dateType;
/**
* 固定日期-生效开始时间
*/
private Date validStartTime;
/**
* 固定日期-生效结束时间
*/
private Date validEndTime;
/**
* 领取日期-开始天数
*
* 例如0-当天1-次天
*/
private Integer fixedStartTerm;
/**
* 领取日期-结束天数
*/
private Integer fixedEndTerm;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
/**
* 优惠类型
*
* 1-代金卷
* 2-折扣卷
*/
private Integer preferentialType;
/**
* 折扣百分比。
*
* 例如80% 为 80。
* 当 100% 为 100 ,则代表免费。
*/
private Integer percentOff;
/**
* 优惠金额,单位:分
*/
private Integer priceOff;
/**
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
*
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
*/
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 统计信息 BEGIN ==========
/**
* 领取优惠券的次数
*/
private Integer statFetchNum;
// ========== 统计信息 END ==========
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.promotion.api.rpc.price;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient("promotion-service")
public interface PriceFeign {
@PostMapping("/promotion/price/calcProductPrice")
public CommonResult<PriceProductCalcRespDTO> calcProductPrice(@RequestBody PriceProductCalcReqDTO calcReqDTO) ;
}

View File

@@ -0,0 +1,67 @@
package cn.iocoder.mall.promotion.api.rpc.price.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
/**
* 商品价格计算 Request DTO
*/
@Data
@Accessors(chain = true)
public class PriceProductCalcReqDTO implements Serializable {
/**
* 用户编号
*/
private Integer userId;
/**
* 优惠劵编号
*/
private Integer couponCardId;
/**
* 商品 SKU 数组
*/
@NotNull(message = "商品数组不能为空")
private List<Item> items;
/**
* 商品 SKU
*/
@Data
@Accessors(chain = true)
public static class Item implements Serializable {
/**
* SKU 编号
*/
@NotNull(message = "商品 SKU 编号不能为空")
private Integer skuId;
/**
* 数量
*/
@NotNull(message = "商品 SKU 数量不能为空")
private Integer quantity;
/**
* 是否选中
*/
@NotNull(message = "是否选中不能为空")
private Boolean selected;
public Item() {
}
public Item(Integer skuId, Integer quantity, Boolean selected) {
this.skuId = skuId;
this.quantity = quantity;
this.selected = selected;
}
}
}

View File

@@ -0,0 +1,200 @@
package cn.iocoder.mall.promotion.api.rpc.price.dto;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 商品价格计算 Request DTO
*/
@Data
@Accessors(chain = true)
public class PriceProductCalcRespDTO implements Serializable {
/**
* 商品分组数组
*/
private List<ItemGroup> itemGroups;
/**
* 优惠劵编号
*/
private Integer couponCardId;
/**
* 优惠劵减少的金额
*
* 1. 若未使用优惠劵,返回 null
* 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
*/
private Integer couponCardDiscountTotal;
/**
* 邮费信息
*
* TODO 芋艿,暂时未弄
*/
private Postage postage;
/**
* 费用
*/
private Fee fee;
/**
* 商品分组
*
* 多个商品,参加同一个活动,从而形成分组。
*/
@Data
@Accessors(chain = true)
public static class ItemGroup implements Serializable {
/**
* 优惠活动
*
* 目前会有满减送 {@link PromotionActivityTypeEnum#FULL_PRIVILEGE} 类型的活动
*
* // TODO 芋艿,目前只会有【满减送】的情况,未来有新的促销方式,可能需要改成数组
*/
private Integer activityId;
/**
* 促销减少的金额
*
* 1. 若未参与促销活动,或不满足促销条件,返回 null
* 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
*/
private Integer activityDiscountTotal;
/**
* 商品数组
*/
private List<Item> items;
// /**
// * 费用
// *
// * TODO 芋艿这里先偷懒postageTotal 字段用不到。
// */
// private Fee fee; // 注释原因,不用这里了
}
@Data
@Accessors(chain = true)
public static class Item implements Serializable {
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 商品 SKU 编号
*/
private Integer skuId;
/**
* 商品 Category 编号
*/
private Integer cid;
// 非 SKU 自带信息
/**
* 是否选中
*/
private Boolean selected;
/**
* 购买数量
*/
private Integer buyQuantity;
/**
* 优惠活动
*
* 目前会有限时折扣 {@link PromotionActivityTypeEnum#TIME_LIMITED_DISCOUNT} 类型的活动
*/
private Integer activityId;
/**
* 原始单价,单位:分。
*/
private Integer originPrice;
/**
* 购买单价,单位:分
*/
private Integer buyPrice;
/**
* 最终价格,单位:分。
*/
private Integer presentPrice;
/**
* 购买总金额,单位:分
*
* 用途类似 {@link #presentTotal}
*/
private Integer buyTotal;
/**
* 优惠总金额,单位:分。
*/
private Integer discountTotal;
/**
* 最终总金额,单位:分。
*
* 注意presentPrice * quantity 不一定等于 presentTotal 。
* 因为,存在无法整除的情况。
* 举个例子presentPrice = 8.33 quantity = 3 的情况presentTotal 有可能是 24.99 ,也可能是 25 。
* 所以,需要存储一个该字段。
*/
private Integer presentTotal;
}
/**
* 费用(合计)
*/
@Data
@Accessors(chain = true)
public static class Fee implements Serializable {
/**
* 购买总价
*/
private Integer buyTotal;
/**
* 优惠总价
*
* 注意,满多少元包邮,不算在优惠中。
*/
private Integer discountTotal;
/**
* 邮费 TODO 芋艿,将 postage 改成 logistics
*/
private Integer postageTotal;
/**
* 最终价格
*
* 计算公式 = 总价 - 优惠总价 + 邮费
*/
private Integer presentTotal;
public Fee() {
}
public Fee(Integer buyTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) {
this.buyTotal = buyTotal;
this.discountTotal = discountTotal;
this.postageTotal = postageTotal;
this.presentTotal = presentTotal;
}
}
/**
* 邮费信息
*/
@Data
@Accessors(chain = true)
public static class Postage implements Serializable {
/**
* 需要满足多少钱,可以包邮。单位:分
*/
private Integer threshold;
}
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.mall.promotion.api.rpc.recommend;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient("promotion-service")
public interface ProductRecommendFeign {
@PostMapping("/promotion/prod/recommend/createProductRecommend")
public CommonResult<Integer> createProductRecommend(@RequestBody ProductRecommendCreateReqDTO createDTO) ;
@PostMapping("/promotion/prod/recommend/updateProductRecommend")
public CommonResult<Boolean> updateProductRecommend(@RequestBody ProductRecommendUpdateReqDTO updateDTO);
@GetMapping("/promotion/prod/recommend/deleteProductRecommend")
public CommonResult<Boolean> deleteProductRecommend(@RequestParam("productRecommendId") Integer productRecommendId) ;
@PostMapping("/promotion/prod/recommend/listProductRecommends")
public CommonResult<List<ProductRecommendRespDTO>> listProductRecommends(@RequestBody ProductRecommendListReqDTO listReqDTO) ;
@PostMapping("/promotion/prod/recommend/pageProductRecommend")
public CommonResult<PageResult<ProductRecommendRespDTO>> pageProductRecommend(@RequestBody ProductRecommendPageReqDTO pageDTO) ;
}

View File

@@ -0,0 +1,48 @@
package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 商品推荐创建 Request DTO
*/
@Data
@Accessors(chain = true)
public class ProductRecommendCreateReqDTO implements Serializable {
/**
* 推荐类型
*/
@InEnum(value = ProductRecommendTypeEnum.class, message = "修改推荐类型必须是 {value}")
@NotNull(message = "推荐类型不能为空")
private Integer type;
/**
* 商品编号
*/
@NotNull(message = "商品编号不能为空")
private Integer productSpuId;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
private Integer sort;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
/**
* 备注
*/
@Length(max = 255, message = "备注最大长度为 255 位")
private String memo;
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 商品推荐列表 Req DTO
*/
@Data
@Accessors(chain = true)
public class ProductRecommendListReqDTO implements Serializable {
/**
* 类型
*/
@InEnum(value = ProductRecommendTypeEnum.class, message = "推荐类型必须是 {value}")
private Integer type;
/**
* 状态
*/
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.common.framework.vo.PageParam;
import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 商品推荐分页 Request DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class ProductRecommendPageReqDTO extends PageParam {
/**
* 推荐类型
*/
@InEnum(value = ProductRecommendTypeEnum.class, message = "推荐类型必须是 {value}")
private Integer type;
}

View File

@@ -0,0 +1,50 @@
package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 商品推荐 BO
*/
@Data
@Accessors(chain = true)
public class ProductRecommendRespDTO implements Serializable {
/**
* 编号
*/
private Integer id;
/**
* 类型
*
* {@link ProductRecommendTypeEnum}
*/
private Integer type;
/**
* 商品 Spu 编号
*/
private Integer productSpuId;
/**
* 排序
*/
private Integer sort;
/**
* 状态
*
* {@link cn.iocoder.common.framework.enums.CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String memo;
/**
* 创建时间
*/
private Date createTime;
}

View File

@@ -0,0 +1,53 @@
package cn.iocoder.mall.promotion.api.rpc.recommend.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 商品推荐更新 Request DTO
*/
@Data
@Accessors(chain = true)
public class ProductRecommendUpdateReqDTO implements Serializable {
/**
* 编号
*/
@NotNull(message = "编号不能为空")
private Integer id;
/**
* 推荐类型
*/
@InEnum(value = ProductRecommendTypeEnum.class, message = "修改推荐类型必须是 {value}")
@NotNull(message = "推荐类型不能为空")
private Integer type;
/**
* 商品编号
*/
@NotNull(message = "商品编号不能为空")
private Integer productSpuId;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
private Integer sort;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
/**
* 备注
*/
@Length(max = 255, message = "备注最大长度为 255 位")
private String memo;
}

View File

@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>promotion-service-project</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>promotion-service-app</artifactId>
<dependencies>
<!-- RPC 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-dubbo</artifactId>
</dependency>
<dependency>
<!-- 系统服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<!-- 商品服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>product-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 营销服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>promotion-service-api</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <!-- 需要开启 Web 容器,因为 Actuator 需要使用到 -->
</dependency>
<!-- Registry 和 Config 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Test 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<!-- 使用 spring-boot-maven-plugin 插件打包 -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,18 @@
package cn.iocoder.mall.promotionservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"cn.iocoder.mall.productservice.rpc"})
public class PromotionServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PromotionServiceApplication.class, args);
}
}

View File

@@ -0,0 +1,9 @@
package cn.iocoder.mall.promotionservice.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync(proxyTargetClass = true) // 开启 Spring Async 异步的功能
public class AsyncConfiguration {
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.mall.promotionservice.config;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("cn.iocoder.mall.promotionservice.dal.mysql.mapper") // 扫描对应的 Mapper 接口
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理。
public class DatabaseConfiguration {
// 数据库连接池 Druid
@Bean
public ISqlInjector sqlInjector() {
return new DefaultSqlInjector(); // MyBatis Plus 逻辑删除
}
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor(); // MyBatis Plus 分页插件
}
}

View File

@@ -0,0 +1,57 @@
package cn.iocoder.mall.promotionservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.*;
import cn.iocoder.mall.promotionservice.manager.banner.BannerManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@RestController
@RequestMapping("/promotion/banner")
public class BannerController {
@Autowired
private BannerManager bannerManager;
@PostMapping("createBanner")
public CommonResult<Integer> createBanner(@RequestBody BannerCreateReqDTO createDTO) {
return success(bannerManager.createBanner(createDTO));
}
@PostMapping("updateBanner")
public CommonResult<Boolean> updateBanner(@RequestBody BannerUpdateReqDTO updateDTO) {
bannerManager.updateBanner(updateDTO);
return success(true);
}
@PostMapping("deleteBanner")
public CommonResult<Boolean> deleteBanner(@RequestBody Integer bannerId) {
bannerManager.deleteBanner(bannerId);
return success(true);
}
@PostMapping("listBanners")
public CommonResult<List<BannerRespDTO>> listBanners(@RequestBody BannerListReqDTO listDTO) {
return success(bannerManager.listBanners(listDTO));
}
@PostMapping("pageBanner")
public CommonResult<PageResult<BannerRespDTO>> pageBanner(@RequestBody BannerPageReqDTO pageDTO) {
return success(bannerManager.pageBanner(pageDTO));
}
}

View File

@@ -0,0 +1,55 @@
package cn.iocoder.mall.promotionservice.controller;
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 cn.iocoder.mall.promotionservice.manager.coupon.CouponCardManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@RestController
@RequestMapping("/coupon/card")
public class CouponCardController {
@Autowired
private CouponCardManager couponCardManager;
@PostMapping("pageCouponCard")
public CommonResult<PageResult<CouponCardRespDTO>> pageCouponCard(@RequestBody CouponCardPageReqDTO pageReqDTO) {
return success(couponCardManager.pageCouponCard(pageReqDTO));
}
@PostMapping("createCouponCard")
public CommonResult<Integer> createCouponCard(@RequestBody CouponCardCreateReqDTO createReqDTO) {
return success(couponCardManager.createCouponCard(createReqDTO));
}
@PostMapping("useCouponCard")
public CommonResult<Boolean> useCouponCard(@RequestBody CouponCardUseReqDTO useReqDTO) {
return success(couponCardManager.useCouponCard(useReqDTO));
}
@PostMapping("cancelUseCouponCard")
public CommonResult<Boolean> cancelUseCouponCard(@RequestBody CouponCardCancelUseReqDTO cancelUseReqDTO) {
return success(couponCardManager.cancelUseCouponCard(cancelUseReqDTO));
}
@PostMapping("listAvailableCouponCards")
public CommonResult<List<CouponCardAvailableRespDTO>> listAvailableCouponCards(@RequestBody CouponCardAvailableListReqDTO listReqDTO) {
return success(couponCardManager.listAvailableCouponCards(listReqDTO));
}
}

View File

@@ -0,0 +1,56 @@
package cn.iocoder.mall.promotionservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.*;
import cn.iocoder.mall.promotionservice.manager.coupon.CouponTemplateManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@RestController
@RequestMapping("/coupon/template")
public class CouponTemplateController {
@Autowired
private CouponTemplateManager couponTemplateManager;
// ========== 通用逻辑 =========
@GetMapping("getCouponTemplate")
public CommonResult<CouponTemplateRespDTO> getCouponTemplate(@RequestParam("couponTemplateId") Integer couponTemplateId) {
return success(couponTemplateManager.getCouponTemplate(couponTemplateId));
}
@PostMapping("pageCouponTemplate")
public CommonResult<PageResult<CouponTemplateRespDTO>> pageCouponTemplate(@RequestBody CouponTemplatePageReqDTO pageDTO) {
return success(couponTemplateManager.pageCouponTemplate(pageDTO));
}
@PostMapping("updateCouponTemplateStatus")
public CommonResult<Boolean> updateCouponTemplateStatus(@RequestBody CouponCardTemplateUpdateStatusReqDTO updateStatusReqDTO) {
couponTemplateManager.updateCouponTemplateStatus(updateStatusReqDTO);
return success(true);
}
// ========== 优惠劵模板 ==========
@PostMapping("createCouponCardTemplate")
public CommonResult<Integer> createCouponCardTemplate(@RequestBody CouponCardTemplateCreateReqDTO createDTO) {
return success(couponTemplateManager.createCouponCardTemplate(createDTO));
}
@PostMapping("updateCouponCardTemplate")
public CommonResult<Boolean> updateCouponCardTemplate(@RequestBody CouponCardTemplateUpdateReqDTO updateDTO) {
couponTemplateManager.updateCouponCardTemplate(updateDTO);
return success(true);
}
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.promotionservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.promotionservice.manager.price.PriceManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@RestController
@RequestMapping("/promotion/price")
public class PriceController {
@Autowired
private PriceManager priceManager;
@PostMapping("calcProductPrice")
public CommonResult<PriceProductCalcRespDTO> calcProductPrice(@RequestBody PriceProductCalcReqDTO calcReqDTO) {
return success(priceManager.calcProductPrice(calcReqDTO));
}
}

View File

@@ -0,0 +1,55 @@
package cn.iocoder.mall.promotionservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.*;
import cn.iocoder.mall.promotionservice.manager.recommend.ProductRecommendManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@RestController
@RequestMapping("/promotion/prod/recommend")
public class ProductRecommendController {
@Autowired
private ProductRecommendManager productRecommendManager;
@PostMapping("createProductRecommend")
public CommonResult<Integer> createProductRecommend(@RequestBody ProductRecommendCreateReqDTO createDTO) {
return success(productRecommendManager.createProductRecommend(createDTO));
}
@PostMapping("updateProductRecommend")
public CommonResult<Boolean> updateProductRecommend(@RequestBody ProductRecommendUpdateReqDTO updateDTO) {
productRecommendManager.updateProductRecommend(updateDTO);
return success(true);
}
@GetMapping("deleteProductRecommend")
public CommonResult<Boolean> deleteProductRecommend(@RequestParam("productRecommendId") Integer productRecommendId) {
productRecommendManager.deleteProductRecommend(productRecommendId);
return success(true);
}
@PostMapping("listProductRecommends")
public CommonResult<List<ProductRecommendRespDTO>> listProductRecommends(@RequestBody ProductRecommendListReqDTO listReqDTO) {
return success(productRecommendManager.listProductRecommends(listReqDTO));
}
@PostMapping("pageProductRecommend")
public CommonResult<PageResult<ProductRecommendRespDTO>> pageProductRecommend(@RequestBody ProductRecommendPageReqDTO pageDTO) {
return success(productRecommendManager.pageProductRecommend(pageDTO));
}
}

View File

@@ -0,0 +1,40 @@
package cn.iocoder.mall.promotionservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotionservice.manager.activity.PromotionActivityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@RestController
@RequestMapping("/promotion/activity")
public class PromotionActivityController {
@Autowired
private PromotionActivityManager promotionActivityManager;
@PostMapping("pagePromotionActivity")
public CommonResult<PageResult<PromotionActivityRespDTO>> pagePromotionActivity(@RequestBody PromotionActivityPageReqDTO pageReqDTO) {
return success(promotionActivityManager.pagePromotionActivity(pageReqDTO));
}
@PostMapping("listPromotionActivities")
public CommonResult<List<PromotionActivityRespDTO>> listPromotionActivities(@RequestBody PromotionActivityListReqDTO listReqDTO) {
return success(promotionActivityManager.listPromotionActivities(listReqDTO));
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.promotionservice.convert.activity;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface PromotionActivityConvert {
PromotionActivityConvert INSTANCE = Mappers.getMapper(PromotionActivityConvert.class);
List<PromotionActivityRespDTO> convertList(List<PromotionActivityDO> list);
@Mapping(source = "records", target = "list")
PageResult<PromotionActivityRespDTO> convertPage(IPage<PromotionActivityDO> page);
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.mall.promotionservice.convert.banner;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerRespDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerUpdateReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.banner.BannerDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface BannerConvert {
BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class);
BannerDO convert(BannerCreateReqDTO bean);
BannerDO convert(BannerUpdateReqDTO bean);
@Mapping(source = "records", target = "list")
PageResult<BannerRespDTO> convertPage(IPage<BannerDO> page);
List<BannerRespDTO> convertList(List<BannerDO> list);
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.promotionservice.convert.coupon;
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.promotionservice.dal.mysql.dataobject.coupon.CouponCardDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CouponCardConvert {
CouponCardConvert INSTANCE = Mappers.getMapper(CouponCardConvert.class);
CouponCardRespDTO convert(CouponCardDO bean);
@Mapping(source = "records", target = "list")
PageResult<CouponCardRespDTO> convertPage(IPage<CouponCardDO> page);
@Named("convertCouponCardDOToCouponCardAvailableRespDTO") // 避免生成的方法名的冲突
CouponCardAvailableRespDTO convert01(CouponCardDO bean);
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.mall.promotionservice.convert.coupon;
import cn.iocoder.common.framework.util.StringUtils;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponCardTemplateCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponCardTemplateUpdateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplateRespDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponTemplateDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface CouponTemplateConvert {
CouponTemplateConvert INSTANCE = Mappers.getMapper(CouponTemplateConvert.class);
@Mapping(source = "rangeValues", target = "rangeValues", qualifiedByName = "translateStringToIntList")
CouponTemplateRespDTO convert(CouponTemplateDO bean);
CouponTemplateDO convert(CouponCardTemplateCreateReqDTO bean);
CouponTemplateDO convert(CouponCardTemplateUpdateReqDTO bean);
@Mapping(source = "records", target = "list")
PageResult<CouponTemplateRespDTO> convertPage(IPage<CouponTemplateDO> couponTemplatePage);
@Named("translateStringToIntList")
default List<Integer> translateStringToIntList(String str) {
return StringUtils.splitToInt(str, ",");
}
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.mall.promotionservice.convert.recommend;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.ProductRecommendCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.ProductRecommendRespDTO;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.ProductRecommendUpdateReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.recommend.ProductRecommendDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ProductRecommendConvert {
ProductRecommendConvert INSTANCE = Mappers.getMapper(ProductRecommendConvert.class);
List<ProductRecommendRespDTO> convertList(List<ProductRecommendDO> list);
@Mapping(source = "records", target = "list")
PageResult<ProductRecommendRespDTO> convertPage(IPage<ProductRecommendDO> page);
ProductRecommendDO convert(ProductRecommendCreateReqDTO bean);
ProductRecommendDO convert(ProductRecommendUpdateReqDTO bean);
}

View File

@@ -0,0 +1,197 @@
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity;
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.TableName;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
/**
* 促销活动 DO
*/
@TableName(value = "promotion_activity", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class PromotionActivityDO extends BaseDO {
/**
* 活动编号
*/
private Integer id;
/**
* 活动标题
*/
private String title;
/**
* 活动类型
*
* 参见 {@link PromotionActivityTypeEnum} 枚举
*/
private Integer activityType;
// /**
// * 促销类型
// * // TODO 芋艿 https://jos.jd.com/api/complexTemplate.htm?webPamer=promotion_v_o&groupName=%E4%BF%83%E9%94%80API&id=54&restName=jingdong.seller.promotion.list&isMulti=false 促销类型可选值单品促销1赠品促销4套装促销6总价促销10
// */
// private Integer promotionType;
/**
* 活动状态
*
* 参见 {@link PromotionActivityStatusEnum} 枚举
*/
private Integer status;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 失效时间
*/
private Date invalidTime;
/**
* 删除时间
*/
private Date deleteTime;
/**
* 限制折扣字符串,使用 JSON 序列化成字符串存储
*/
@TableField(typeHandler = FastjsonTypeHandler.class)
private TimeLimitedDiscount timeLimitedDiscount;
/**
* 满减送字符串,使用 JSON 序列化成字符串存储
*/
@TableField(typeHandler = FastjsonTypeHandler.class)
private FullPrivilege fullPrivilege;
/**
* 限制折扣
*/
@Data
@Accessors(chain = true)
public static class TimeLimitedDiscount {
/**
* 商品折扣
*/
@Data
@Accessors(chain = true)
public static class Item {
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 优惠类型
*/
private Integer preferentialType;
/**
* 优惠值
*/
private Integer preferentialValue;
}
/**
* 每人每种限购多少
*
* 当 quota = 0 时,表示不限购
*/
private Integer quota;
/**
* 商品折扣数组
*/
private List<Item> items;
}
/**
* 满减送
*/
@Data
@Accessors(chain = true)
public static class FullPrivilege {
/**
* 优惠
*/
@Data
@Accessors(chain = true)
public static class Privilege {
/**
* 满足类型
*
* 1 - 金额
* 2 - 件数
*/
private Integer meetType;
/**
* 满足值
*/
private Integer meetValue;
/**
* 优惠类型
*/
private Integer preferentialType;
/**
* 优惠值
*/
private Integer preferentialValue;
// /**
// * 是否包邮
// */
// private Boolean isPostage;
// /**
// * 积分
// */
// private Integer score;
// /**
// * 优惠劵(码)分组编号
// */
// private Integer couponTemplateId;
// /**
// * 优惠劵(码)数量
// */
// private Integer couponNum;
// /**
// * 赠品编号
// */
// private Integer presentId;
}
/**
* 可用范围的类型
*
* 参见 {@link cn.iocoder.mall.promotion.api.enums.RangeTypeEnum} 枚举
* 暂时只用 “所有可用” + “PRODUCT_INCLUDE_PRT”
*/
private Integer rangeType;
/**
* 指定可用商品列表
*/
private List<Integer> rangeValues;
/**
* 是否循环
*/
private Boolean cycled;
/**
* 优惠数组
*/
private List<Privilege> privileges;
}
}

View File

@@ -0,0 +1,51 @@
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.banner;
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* Banner 广告页
*/
@TableName("banner")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class BannerDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 标题
*/
private String title;
/**
* 跳转链接
*/
private String url;
/**
* 图片链接
*/
private String picUrl;
/**
* 排序
*/
private Integer sort;
/**
* 状态
*
* {@link cn.iocoder.common.framework.enums.CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String memo;
// TODO 芋艿 点击次数。&& 其他数据相关
}

View File

@@ -0,0 +1,123 @@
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 优惠劵 DO
*/
@TableName("coupon_card")
@Data
@EqualsAndHashCode(callSuper = true)
@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 ==========
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon;
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 芋艿,后续要考虑状态的追踪
}

View File

@@ -0,0 +1,237 @@
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateDateTypeEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateStatusEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateTypeEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 优惠劵(码)模板 DO
*
* 当用户领取时,会生成 {@link CouponCardDO} 优惠劵(码)。
*/
@TableName("coupon_template")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class CouponTemplateDO extends BaseDO {
// ========== 基本信息 BEGIN ==========
/**
* 模板编号,自增唯一。
*/
private Integer id;
/**
* 标题
*/
private String title;
/**
* 使用说明
*/
private String description;
/**
* 类型
*
* 枚举 {@link CouponTemplateTypeEnum}
*
* 1-优惠劵
* 2-优惠码
*/
private Integer type;
/**
* 优惠码状态
*
* 枚举 {@link 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;
/**
* 可用范围的类型
*
* 枚举 {@link RangeTypeEnum}
*
* 10-全部ALL所有可用
* 20-部分PART部分商品可用或指定商品可用
* 21-部分PART部分商品不可用或指定商品可用
* 30-部分PART部分分类可用或指定商品可用
* 31-部分PART部分分类不可用或指定商品可用
*/
private Integer rangeType;
/**
* 指定商品 / 分类列表,使用逗号分隔商品编号
*/
private String rangeValues;
/**
* 生效日期类型
*
* 枚举 {@link CouponTemplateDateTypeEnum}
*
* 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 ==========
/**
* 优惠类型
*
* 枚举 {@link PreferentialTypeEnum}
*
* 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 ==========
}

View File

@@ -0,0 +1,48 @@
package cn.iocoder.mall.promotionservice.dal.mysql.dataobject.recommend;
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
import cn.iocoder.mall.promotion.api.enums.recommend.ProductRecommendTypeEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 商品推荐 DO
*/
@TableName("product_recommend")
@Data
@Accessors(chain = true)
public class ProductRecommendDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 类型
*
* {@link ProductRecommendTypeEnum}
*/
private Integer type;
/**
* 商品 Spu 编号
*/
private Integer productSpuId;
// TODO 芋艿,商品 spu 名
/**
* 排序
*/
private Integer sort;
/**
* 状态
*
* {@link cn.iocoder.common.framework.enums.CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String memo;
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityPageReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
@Repository
public interface PromotionActivityMapper extends BaseMapper<PromotionActivityDO> {
default List<PromotionActivityDO> selectListByStatus(Collection<Integer> statuses) {
return selectList(new QueryWrapper<PromotionActivityDO>().in("status", statuses));
}
default IPage<PromotionActivityDO> selectPage(PromotionActivityPageReqDTO pageReqDTO) {
return selectPage(new Page<>(pageReqDTO.getPageNo(), pageReqDTO.getPageSize()),
new QueryWrapperX<PromotionActivityDO>().likeIfPresent("title", pageReqDTO.getTitle())
.eqIfPresent("activity_type", pageReqDTO.getActivityType())
.inIfPresent("status", pageReqDTO.getStatuses()));
}
default List<PromotionActivityDO> selectList(PromotionActivityListReqDTO listReqDTO) {
return selectList(new QueryWrapperX<PromotionActivityDO>().inIfPresent("id", listReqDTO.getActiveIds()));
}
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.promotionservice.dal.mysql.mapper.banner;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerPageReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.banner.BannerDO;
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 java.util.List;
@Repository
public interface BannerMapper extends BaseMapper<BannerDO> {
default List<BannerDO> selectList(BannerListReqDTO listReqDTO) {
return selectList(new QueryWrapperX<BannerDO>().eqIfPresent("status", listReqDTO.getStatus()));
}
default IPage<BannerDO> selectPage(BannerPageReqDTO pageReqDTO) {
return selectPage(new Page<>(pageReqDTO.getPageNo(), pageReqDTO.getPageSize()),
new QueryWrapperX<BannerDO>().likeIfPresent("title", pageReqDTO.getTitle()));
}
}

View File

@@ -0,0 +1,38 @@
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 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 java.util.List;
@Repository
public interface CouponCardMapper extends BaseMapper<CouponCardDO> {
default List<CouponCardDO> selectListByUserIdAndStatus(Integer userId, Integer status) {
return selectList(new QueryWrapper<CouponCardDO>().eq("user_id", userId)
.eq("status", status));
}
default int selectCountByUserIdAndTemplateId(Integer userId, Integer templateId) {
return selectCount(new QueryWrapper<CouponCardDO>().eq("user_id", userId)
.eq("template_id", templateId));
}
default int updateByIdAndStatus(Integer id, Integer status, CouponCardDO updateObj) {
return update(updateObj, new QueryWrapper<CouponCardDO>().eq("id", id)
.eq("status", status));
}
default IPage<CouponCardDO> selectPage(CouponCardPageReqDTO pageReqDTO) {
return selectPage(new Page<>(pageReqDTO.getPageNo(), pageReqDTO.getPageSize()),
new QueryWrapperX<CouponCardDO>().eqIfPresent("user_id", pageReqDTO.getUserId())
.eqIfPresent("status", pageReqDTO.getStatus()));
}
}

View File

@@ -0,0 +1,33 @@
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.template.CouponTemplatePageReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponTemplateDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface CouponTemplateMapper extends BaseMapper<CouponTemplateDO> {
default IPage<CouponTemplateDO> selectPage(CouponTemplatePageReqDTO pageReqDTO) {
return selectPage(new Page<>(pageReqDTO.getPageNo(), pageReqDTO.getPageSize()),
new QueryWrapperX<CouponTemplateDO>().eqIfPresent("type", pageReqDTO.getType())
.likeIfPresent("title", pageReqDTO.getTitle())
.eqIfPresent("status", pageReqDTO.getStatus())
.eqIfPresent("preferential_type", pageReqDTO.getPreferentialType()));
}
/**
* 更新优惠劵模板已领取的数量
*
* 如果超过领取上限,则返回 0
*
* @param id 优惠劵模板编号
* @return 更新数量
*/
int updateStatFetchNumIncr(@Param("id") Integer id);
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.promotionservice.dal.mysql.mapper.recommend;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.ProductRecommendListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.ProductRecommendPageReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.recommend.ProductRecommendDO;
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 java.util.List;
@Repository
public interface ProductRecommendMapper extends BaseMapper<ProductRecommendDO> {
default ProductRecommendDO selectByProductSpuIdAndType(Integer productSpuId, Integer type) {
return selectOne(new QueryWrapper<ProductRecommendDO>().eq("product_spu_id", productSpuId)
.eq("type", type));
}
default List<ProductRecommendDO> selectList(ProductRecommendListReqDTO listReqDTO) {
return selectList(new QueryWrapperX<ProductRecommendDO>().eqIfPresent("type", listReqDTO.getType())
.eqIfPresent("status", listReqDTO.getStatus()));
}
default IPage<ProductRecommendDO> selectPage(ProductRecommendPageReqDTO pageReqDTO) {
return selectPage(new Page<>(pageReqDTO.getPageNo(), pageReqDTO.getPageSize()),
new QueryWrapperX<ProductRecommendDO>().eqIfPresent("type", pageReqDTO.getType()));
}
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.mall.promotionservice.manager.activity;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotionservice.service.activity.PromotionActivityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
/**
* 促销活动 Manager
*/
@Service
@Validated
public class PromotionActivityManager {
@Autowired
private PromotionActivityService promotionActivityService;
public List<PromotionActivityRespDTO> listPromotionActivities(PromotionActivityListReqDTO listReqDTO) {
return promotionActivityService.listPromotionActivities(listReqDTO);
}
public PageResult<PromotionActivityRespDTO> pagePromotionActivity(PromotionActivityPageReqDTO pageReqDTO) {
return promotionActivityService.pagePromotionActivity(pageReqDTO);
}
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.mall.promotionservice.manager.banner;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.*;
import cn.iocoder.mall.promotionservice.service.banner.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.Valid;
import java.util.List;
/**
* Banner Manager
*/
@Service
@Valid
public class BannerManager {
@Autowired
private BannerService bannerService;
public Integer createBanner(BannerCreateReqDTO createDTO) {
return bannerService.createBanner(createDTO);
}
public void updateBanner(BannerUpdateReqDTO updateDTO) {
bannerService.updateBanner(updateDTO);
}
public void deleteBanner(Integer bannerId) {
bannerService.deleteBanner(bannerId);
}
public List<BannerRespDTO> listBanners(BannerListReqDTO listDTO) {
return bannerService.listBanners(listDTO);
}
public PageResult<BannerRespDTO> pageBanner(BannerPageReqDTO pageDTO) {
return bannerService.pageBanner(pageDTO);
}
}

View File

@@ -0,0 +1,41 @@
package cn.iocoder.mall.promotionservice.manager.coupon;
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);
}
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.mall.promotionservice.manager.coupon;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.*;
import cn.iocoder.mall.promotionservice.service.coupon.CouponTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 优惠劵(码)模板 Manager
*/
@Service
public class CouponTemplateManager {
@Autowired
private CouponTemplateService couponTemplateService;
// ========== 通用逻辑 =========
public CouponTemplateRespDTO getCouponTemplate(Integer couponTemplateId) {
return couponTemplateService.getCouponTemplate(couponTemplateId);
}
public PageResult<CouponTemplateRespDTO> pageCouponTemplate(CouponTemplatePageReqDTO pageDTO) {
return couponTemplateService.pageCouponTemplate(pageDTO);
}
public void updateCouponTemplateStatus(CouponCardTemplateUpdateStatusReqDTO updateStatusReqDTO) {
couponTemplateService.updateCouponTemplateStatus(updateStatusReqDTO.getId(), updateStatusReqDTO.getStatus());
}
// ========== 优惠劵模板 ==========
public Integer createCouponCardTemplate(CouponCardTemplateCreateReqDTO createDTO) {
return couponTemplateService.createCouponCardTemplate(createDTO);
}
public void updateCouponCardTemplate(CouponCardTemplateUpdateReqDTO updateDTO) {
couponTemplateService.updateCouponCardTemplate(updateDTO);
}
}

View File

@@ -0,0 +1,379 @@
package cn.iocoder.mall.promotionservice.manager.price;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.productservice.rpc.sku.ProductSkuFeign;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuListQueryReqDTO;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO;
import cn.iocoder.mall.productservice.rpc.spu.ProductSpuFeign;
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
import cn.iocoder.mall.promotion.api.enums.MeetTypeEnum;
import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
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.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplateRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.promotionservice.service.activity.PromotionActivityService;
import cn.iocoder.mall.promotionservice.service.coupon.CouponCardService;
import cn.iocoder.mall.promotionservice.service.coupon.CouponTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants.*;
@Service
@Validated
public class PriceManager {
@Autowired
private ProductSkuFeign productSkuFeign;
@Autowired
private ProductSpuFeign productSpuFeign;
@Autowired
private PromotionActivityService promotionActivityService;
@Autowired
private CouponCardService couponCardService;
@Autowired
private CouponTemplateService couponTemplateService;
public PriceProductCalcRespDTO calcProductPrice(PriceProductCalcReqDTO calcReqDTO) {
// TODO 芋艿,补充一些表单校验。例如说,需要传入用户编号。
// 校验商品都存在
Map<Integer, PriceProductCalcReqDTO.Item> calcProductItemDTOMap = CollectionUtils.convertMap(
calcReqDTO.getItems(), PriceProductCalcReqDTO.Item::getSkuId);
CommonResult<List<ProductSkuRespDTO>> listProductSkusResult = productSkuFeign.listProductSkus(
new ProductSkuListQueryReqDTO().setProductSkuIds(calcProductItemDTOMap.keySet()));
listProductSkusResult.checkError();
if (calcReqDTO.getItems().size() != listProductSkusResult.getData().size()) {
throw ServiceExceptionUtil.exception(PRICE_PRODUCT_SKU_NOT_EXISTS);
}
// TODO 库存相关
// 查询促销活动
List<PromotionActivityRespDTO> activityRespDTOs = promotionActivityService.listPromotionActivitiesBySpuIds(
CollectionUtils.convertSet(listProductSkusResult.getData(), ProductSkuRespDTO::getSpuId),
Collections.singleton(PromotionActivityStatusEnum.RUN.getValue()));
// 拼装结果(主要是计算价格)
PriceProductCalcRespDTO calcRespDTO = new PriceProductCalcRespDTO();
// 1. 创建初始的每一项的数组
List<PriceProductCalcRespDTO.Item> calcItemRespDTOs = this.initCalcOrderPriceItems(
listProductSkusResult.getData(), calcProductItemDTOMap);
// 2. 计算【限时折扣】促销
this.modifyPriceByTimeLimitDiscount(calcItemRespDTOs, activityRespDTOs);
// 3. 计算【满减送】促销
List<PriceProductCalcRespDTO.ItemGroup> itemGroups = this.groupByFullPrivilege(calcItemRespDTOs, activityRespDTOs);
calcRespDTO.setItemGroups(itemGroups);
// 4. 计算优惠劵 TODO 芋艿:未详细测试;
if (calcReqDTO.getCouponCardId() != null) {
Integer result = this.modifyPriceByCouponCard(calcReqDTO.getUserId(), calcReqDTO.getCouponCardId(), itemGroups);
calcRespDTO.setCouponCardDiscountTotal(result);
}
// 5. 计算最终的价格
int buyTotal = 0;
int discountTotal = 0;
int presentTotal = 0;
for (PriceProductCalcRespDTO.ItemGroup itemGroup : calcRespDTO.getItemGroups()) {
buyTotal += itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getBuyTotal() : 0).sum();
discountTotal += itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getDiscountTotal() : 0).sum();
presentTotal += itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getPresentTotal() : 0).sum();
}
Assert.isTrue(buyTotal - discountTotal == presentTotal,
String.format("价格合计( %d - %d == %d )不正确", buyTotal, discountTotal, presentTotal));
calcRespDTO.setFee(new PriceProductCalcRespDTO.Fee(buyTotal, discountTotal, 0, presentTotal));
return calcRespDTO;
}
private List<PriceProductCalcRespDTO.Item> initCalcOrderPriceItems(List<ProductSkuRespDTO> skus,
Map<Integer, PriceProductCalcReqDTO.Item> calcProductItemDTOMap) {
// 获得商品分类 Map
CommonResult<List<ProductSpuRespDTO>> listProductSpusResult = productSpuFeign.listProductSpus(CollectionUtils.convertSet(skus, ProductSkuRespDTO::getSpuId));
listProductSpusResult.checkError();
Map<Integer, Integer> spuIdCategoryIdMap = CollectionUtils.convertMap(listProductSpusResult.getData(), // SPU 编号与 Category 编号的映射
ProductSpuRespDTO::getId, ProductSpuRespDTO::getCid);
// 生成商品列表
List<PriceProductCalcRespDTO.Item> items = new ArrayList<>();
for (ProductSkuRespDTO sku : skus) {
PriceProductCalcRespDTO.Item item = new PriceProductCalcRespDTO.Item();
items.add(item);
// 将基本信息,复制到 item 中
PriceProductCalcReqDTO.Item calcOrderItem = calcProductItemDTOMap.get(sku.getId());
item.setSpuId(sku.getSpuId()).setSkuId(sku.getId());
item.setCid(spuIdCategoryIdMap.get(sku.getSpuId()));
item.setSelected(calcOrderItem.getSelected());
item.setBuyQuantity(calcOrderItem.getQuantity());
// 计算初始价格
item.setOriginPrice(sku.getPrice());
item.setBuyPrice(sku.getPrice());
item.setPresentPrice(sku.getPrice());
item.setBuyTotal(sku.getPrice() * calcOrderItem.getQuantity());
item.setDiscountTotal(0);
item.setPresentTotal(item.getBuyTotal());
}
return items;
}
private void modifyPriceByTimeLimitDiscount(List<PriceProductCalcRespDTO.Item> items, List<PromotionActivityRespDTO> activityList) {
for (PriceProductCalcRespDTO.Item item : items) {
// 获得符合条件的限时折扣
PromotionActivityRespDTO timeLimitedDiscount = activityList.stream()
.filter(activity -> PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType())
&& activity.getTimeLimitedDiscount().getItems().stream().anyMatch(item0 -> item0.getSpuId().equals(item.getSpuId())))
.findFirst().orElse(null);
if (timeLimitedDiscount == null) {
continue;
}
// 计算价格
Integer newBuyPrice = calcSkuPriceByTimeLimitDiscount(item, timeLimitedDiscount);
if (newBuyPrice.equals(item.getBuyPrice())) { // 未优惠
continue;
}
// 设置优惠
item.setActivityId(timeLimitedDiscount.getId());
// 设置价格
item.setBuyPrice(newBuyPrice);
item.setBuyTotal(newBuyPrice * item.getBuyQuantity());
item.setPresentTotal(item.getBuyTotal() - item.getDiscountTotal());
item.setPresentPrice(item.getPresentTotal() / item.getBuyQuantity());
}
}
/**
* 计算指定 SKU 在限时折扣下的价格
*
* @param sku SKU
* @param timeLimitedDiscount 限时折扣促销。
* 传入的该活动,要保证该 SKU 在该促销下一定有优惠。
* @return 计算后的价格
*/
private Integer calcSkuPriceByTimeLimitDiscount(PriceProductCalcRespDTO.Item sku, PromotionActivityRespDTO timeLimitedDiscount) {
if (timeLimitedDiscount == null) {
return sku.getBuyPrice();
}
// 获得对应的优惠项
PromotionActivityRespDTO.TimeLimitedDiscount.Item item = timeLimitedDiscount.getTimeLimitedDiscount().getItems().stream()
.filter(item0 -> item0.getSpuId().equals(sku.getSpuId()))
.findFirst().orElse(null);
if (item == null) {
throw new IllegalArgumentException(String.format("折扣活动(%s) 不存在商品(%s) 的优惠配置",
timeLimitedDiscount.toString(), sku.toString()));
}
// 计算价格
if (PreferentialTypeEnum.PRICE.getValue().equals(item.getPreferentialType())) { // 减价
int presentPrice = sku.getBuyPrice() - item.getPreferentialValue();
return presentPrice >= 0 ? presentPrice : sku.getBuyPrice(); // 如果计算优惠价格小于 0 ,则说明无法使用优惠。
}
if (PreferentialTypeEnum.DISCOUNT.getValue().equals(item.getPreferentialType())) { // 打折
return sku.getBuyPrice() * item.getPreferentialValue() / 100;
}
throw new IllegalArgumentException(String.format("折扣活动(%s) 的优惠类型不正确", timeLimitedDiscount.toString()));
}
private List<PriceProductCalcRespDTO.ItemGroup> groupByFullPrivilege(List<PriceProductCalcRespDTO.Item> items, List<PromotionActivityRespDTO> activityList) {
List<PriceProductCalcRespDTO.ItemGroup> itemGroups = new ArrayList<>();
// 获得所有满减送促销
List<PromotionActivityRespDTO> fullPrivileges = activityList.stream()
.filter(activity -> PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()))
.collect(Collectors.toList());
// 基于满减送促销,进行分组
if (!fullPrivileges.isEmpty()) {
items = new ArrayList<>(items); // 因为下面会修改数组,进行浅拷贝,避免影响传入的 items 。
for (PromotionActivityRespDTO fullPrivilege : fullPrivileges) {
// 创建 fullPrivilege 对应的分组
PriceProductCalcRespDTO.ItemGroup itemGroup = new PriceProductCalcRespDTO.ItemGroup()
.setActivityId(fullPrivilege.getId())
.setItems(new ArrayList<>());
// 筛选商品到分组中
for (Iterator<PriceProductCalcRespDTO.Item> iterator = items.iterator(); iterator.hasNext(); ) {
PriceProductCalcRespDTO.Item item = iterator.next();
if (!isSpuMatchFullPrivilege(item.getSpuId(), fullPrivilege)) {
continue;
}
itemGroup.getItems().add(item);
iterator.remove();
}
// 如果匹配到,则添加到 itemGroups 中
if (!itemGroup.getItems().isEmpty()) {
itemGroups.add(itemGroup);
}
}
}
// 处理未参加活动的商品,形成一个分组
if (!items.isEmpty()) {
itemGroups.add(new PriceProductCalcRespDTO.ItemGroup().setItems(items));
}
// 计算每个分组的价格
Map<Integer, PromotionActivityRespDTO> activityMap = CollectionUtils.convertMap(activityList, PromotionActivityRespDTO::getId);
for (PriceProductCalcRespDTO.ItemGroup itemGroup : itemGroups) {
itemGroup.setActivityDiscountTotal(calcSkuPriceByFullPrivilege(itemGroup, activityMap.get(itemGroup.getActivityId())));
}
// 返回结果
return itemGroups;
}
private boolean isSpuMatchFullPrivilege(Integer spuId, PromotionActivityRespDTO activity) {
Assert.isTrue(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()),
"传入的必须的促销活动必须是满减送");
PromotionActivityRespDTO.FullPrivilege fullPrivilege = activity.getFullPrivilege();
if (RangeTypeEnum.ALL.getValue().equals(fullPrivilege.getRangeType())) {
return true;
} else if (RangeTypeEnum.PRODUCT_INCLUDE_PART.getValue().equals(fullPrivilege.getRangeType())) {
return fullPrivilege.getRangeValues().contains(spuId);
}
throw new IllegalArgumentException(String.format("促销活动(%s) 可用范围的类型是不正确", activity.toString()));
}
private Integer calcSkuPriceByFullPrivilege(PriceProductCalcRespDTO.ItemGroup itemGroup, PromotionActivityRespDTO activity) {
if (itemGroup.getActivityId() == null) {
return null;
}
Assert.isTrue(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()),
"传入的必须的满减送活动必须是满减送");
// 获得优惠信息
List<PriceProductCalcRespDTO.Item> items = itemGroup.getItems().stream().filter(PriceProductCalcRespDTO.Item::getSelected)
.collect(Collectors.toList());
Integer itemCnt = items.stream().mapToInt(PriceProductCalcRespDTO.Item::getBuyQuantity).sum();
Integer originalTotal = items.stream().mapToInt(PriceProductCalcRespDTO.Item::getPresentTotal).sum();
List<PromotionActivityRespDTO.FullPrivilege.Privilege> privileges = activity.getFullPrivilege().getPrivileges().stream()
.filter(privilege -> {
if (MeetTypeEnum.PRICE.getValue().equals(privilege.getMeetType())) {
return originalTotal >= privilege.getMeetValue();
}
if (MeetTypeEnum.QUANTITY.getValue().equals(privilege.getMeetType())) {
return itemCnt >= privilege.getMeetValue();
}
throw new IllegalArgumentException(String.format("满减送活动(%s) 的匹配(%s)不正确",
activity.toString(), privilege.toString()));
}).collect(Collectors.toList());
// 获得不到优惠信息,返回原始价格
if (privileges.isEmpty()) {
return null;
}
// 获得到优惠信息,进行价格计算
PromotionActivityRespDTO.FullPrivilege.Privilege privilege = privileges.get(privileges.size() - 1);
Integer presentTotal;
if (PreferentialTypeEnum.PRICE.getValue().equals(privilege.getPreferentialType())) { // 减价
// 计算循环次数。这样,后续优惠的金额就是相乘了
Integer cycleCount = 1;
if (activity.getFullPrivilege().getCycled()) {
if (MeetTypeEnum.PRICE.getValue().equals(privilege.getMeetType())) {
cycleCount = originalTotal / privilege.getMeetValue();
} else if (MeetTypeEnum.QUANTITY.getValue().equals(privilege.getMeetType())) {
cycleCount = itemCnt / privilege.getMeetValue();
}
}
presentTotal = originalTotal - cycleCount * privilege.getMeetValue();
if (presentTotal < 0) { // 如果计算优惠价格小于 0 ,则说明无法使用优惠。
presentTotal = originalTotal;
}
} else if (PreferentialTypeEnum.DISCOUNT.getValue().equals(privilege.getPreferentialType())) { // 打折
presentTotal = originalTotal * privilege.getPreferentialValue() / 100;
} else {
throw new IllegalArgumentException(String.format("满减送促销(%s) 的优惠类型不正确", activity.toString()));
}
int discountTotal = originalTotal - presentTotal;
if (discountTotal == 0) {
return null;
}
// 按比例,拆分 presentTotal
splitDiscountPriceToItems(items, discountTotal, presentTotal);
// 返回优惠金额
return originalTotal - presentTotal;
}
private Integer modifyPriceByCouponCard(Integer userId, Integer couponCardId, List<PriceProductCalcRespDTO.ItemGroup> itemGroups) {
Assert.isTrue(couponCardId != null, "优惠劵编号不能为空");
// 查询优惠劵
CouponCardRespDTO couponCard = couponCardService.getCouponCard(userId, couponCardId);
if (couponCard == null) {
throw ServiceExceptionUtil.exception(COUPON_CARD_NOT_EXISTS);
}
CouponTemplateRespDTO couponTemplate = couponTemplateService.getCouponTemplate(couponCardId);
if (couponTemplate == null) {
throw ServiceExceptionUtil.exception(COUPON_TEMPLATE_NOT_EXISTS);
}
// 获得匹配的商品
List<PriceProductCalcRespDTO.Item> items = new ArrayList<>();
if (RangeTypeEnum.ALL.getValue().equals(couponTemplate.getRangeType())) {
itemGroups.forEach(itemGroup -> items.addAll(itemGroup.getItems()));
} else if (RangeTypeEnum.PRODUCT_INCLUDE_PART.getValue().equals(couponTemplate.getRangeType())) {
itemGroups.forEach(itemGroup -> items.forEach(item -> {
if (couponTemplate.getRangeValues().contains(item.getSpuId())) {
items.add(item);
}
}));
} else if (RangeTypeEnum.PRODUCT_EXCLUDE_PART.getValue().equals(couponTemplate.getRangeType())) {
itemGroups.forEach(itemGroup -> items.forEach(item -> {
if (!couponTemplate.getRangeValues().contains(item.getSpuId())) {
items.add(item);
}
}));
} else if (RangeTypeEnum.CATEGORY_INCLUDE_PART.getValue().equals(couponTemplate.getRangeType())) {
itemGroups.forEach(itemGroup -> items.forEach(item -> {
if (couponTemplate.getRangeValues().contains(item.getCid())) {
items.add(item);
}
}));
} else if (RangeTypeEnum.CATEGORY_EXCLUDE_PART.getValue().equals(couponTemplate.getRangeType())) {
itemGroups.forEach(itemGroup -> items.forEach(item -> {
if (!couponTemplate.getRangeValues().contains(item.getCid())) {
items.add(item);
}
}));
}
// 判断是否符合条件
int originalTotal = items.stream().mapToInt(PriceProductCalcRespDTO.Item::getPresentTotal).sum(); // 此处,指的是以优惠劵视角的原价
if (originalTotal == 0 || originalTotal < couponCard.getPriceAvailable()) {
throw ServiceExceptionUtil.exception(COUPON_CARD_NOT_MATCH); // TODO 芋艿,这种情况,会出现错误码的提示,无法格式化出来。另外,这块的最佳实践,找人讨论下。
}
// 计算价格
// 获得到优惠信息,进行价格计算
int presentTotal;
if (PreferentialTypeEnum.PRICE.getValue().equals(couponCard.getPreferentialType())) { // 减价
// 计算循环次数。这样,后续优惠的金额就是相乘了
presentTotal = originalTotal - couponCard.getPriceOff();
Assert.isTrue(presentTotal > 0, "计算后,价格为负数:" + presentTotal);
} else if (PreferentialTypeEnum.DISCOUNT.getValue().equals(couponCard.getPreferentialType())) { // 打折
presentTotal = originalTotal * couponCard.getPercentOff() / 100;
if (couponCard.getDiscountPriceLimit() != null // 空,代表不限制优惠上限
&& originalTotal - presentTotal > couponCard.getDiscountPriceLimit()) {
presentTotal = originalTotal - couponCard.getDiscountPriceLimit();
}
} else {
throw new IllegalArgumentException(String.format("优惠劵(%s) 的优惠类型不正确", couponCard.toString()));
}
int discountTotal = originalTotal - presentTotal;
Assert.isTrue(discountTotal > 0, "计算后,不产生优惠:" + discountTotal);
// 按比例,拆分 presentTotal
splitDiscountPriceToItems(items, discountTotal, presentTotal);
// 返回优惠金额
return originalTotal - presentTotal;
}
private void splitDiscountPriceToItems(List<PriceProductCalcRespDTO.Item> items, Integer discountTotal, Integer presentTotal) {
for (int i = 0; i < items.size(); i++) {
PriceProductCalcRespDTO.Item item = items.get(i);
Integer discountPart;
if (i < items.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减
discountPart = (int) (discountTotal * (1.0D * item.getPresentTotal() / presentTotal));
discountTotal -= discountPart;
} else {
discountPart = discountTotal;
}
Assert.isTrue(discountPart > 0, "优惠金额必须大于 0");
item.setDiscountTotal(item.getDiscountTotal() + discountPart);
item.setPresentTotal(item.getBuyTotal() - item.getDiscountTotal());
item.setPresentPrice(item.getPresentTotal() / item.getBuyQuantity());
}
}
}

View File

@@ -0,0 +1,65 @@
package cn.iocoder.mall.promotionservice.manager.recommend;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.productservice.rpc.spu.ProductSpuFeign;
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.*;
import cn.iocoder.mall.promotionservice.service.recommend.ProductRecommendService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants.PRODUCT_RECOMMEND_PRODUCT_NOT_EXISTS;
/**
* 商品推荐 Manager
*/
@Service
@Validated
public class ProductRecommendManager {
@Autowired
private ProductSpuFeign productSpuFeign;
@Autowired
private ProductRecommendService productRecommendService;
public List<ProductRecommendRespDTO> listProductRecommends(ProductRecommendListReqDTO listReqDTO) {
return productRecommendService.listProductRecommends(listReqDTO);
}
public PageResult<ProductRecommendRespDTO> pageProductRecommend(ProductRecommendPageReqDTO pageReqDTO) {
return productRecommendService.pageProductRecommend(pageReqDTO);
}
public Integer createProductRecommend(ProductRecommendCreateReqDTO createReqDTO) {
// 校验商品不存在
checkProductSpu(createReqDTO.getProductSpuId());
// 创建商品推荐
return productRecommendService.createProductRecommend(createReqDTO);
}
public void updateProductRecommend(ProductRecommendUpdateReqDTO updateReqDTO) {
// 校验商品不存在
checkProductSpu(updateReqDTO.getProductSpuId());
// 更新商品推荐
productRecommendService.updateProductRecommend(updateReqDTO);
}
public void deleteProductRecommend(Integer productRecommendId) {
productRecommendService.deleteProductRecommend(productRecommendId);
}
private void checkProductSpu(Integer productSpuId) {
CommonResult<ProductSpuRespDTO> getProductSpuResult = productSpuFeign.getProductSpu(productSpuId);
getProductSpuResult.checkError();
if (getProductSpuResult.getData() == null) {
throw ServiceExceptionUtil.exception(PRODUCT_RECOMMEND_PRODUCT_NOT_EXISTS);
}
}
}

View File

@@ -0,0 +1,98 @@
package cn.iocoder.mall.promotionservice.service.activity;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityTypeEnum;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotionservice.convert.activity.PromotionActivityConvert;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO;
import cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity.PromotionActivityMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@Service
@Validated
public class PromotionActivityService {
@Autowired
private PromotionActivityMapper promotionActivityMapper;
public List<PromotionActivityRespDTO> listPromotionActivities(PromotionActivityListReqDTO listReqDTO) {
List<PromotionActivityDO> activityList = promotionActivityMapper.selectList(listReqDTO);
return PromotionActivityConvert.INSTANCE.convertList(activityList);
}
public List<PromotionActivityRespDTO> listPromotionActivitiesBySpuIds(Collection<Integer> spuIds, Collection<Integer> activityStatuses) {
if (spuIds.isEmpty() || activityStatuses.isEmpty()) {
return Collections.emptyList();
}
// 查询指定状态的促销活动
List<PromotionActivityDO> activityList = promotionActivityMapper.selectListByStatus(activityStatuses);
if (activityList.isEmpty()) {
return Collections.emptyList();
}
// 匹配商品
for (Iterator<PromotionActivityDO> iterator = activityList.iterator(); iterator.hasNext();) {
PromotionActivityDO activity = iterator.next();
boolean matched = false;
for (Integer spuId : spuIds) {
if (PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType())) {
matched = isSpuMatchTimeLimitDiscount(spuId, activity);
} else if (PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType())) {
matched = isSpuMatchFullPrivilege(spuId, activity);
}
if (matched) {
break;
}
}
// 不匹配,则进行移除
if (!matched) {
iterator.remove();
} else { // 匹配,则做一些后续的处理
// 如果是限时折扣,移除不在 spuId 数组中的折扣规则
if (PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType())) {
activity.getTimeLimitedDiscount().getItems().removeIf(item -> !spuIds.contains(item.getSpuId()));
}
}
}
// 返回最终结果
return PromotionActivityConvert.INSTANCE.convertList(activityList);
}
public PageResult<PromotionActivityRespDTO> pagePromotionActivity(PromotionActivityPageReqDTO pageReqDTO) {
IPage<PromotionActivityDO> promotionActivityPage = promotionActivityMapper.selectPage(pageReqDTO);
return PromotionActivityConvert.INSTANCE.convertPage(promotionActivityPage);
}
private boolean isSpuMatchTimeLimitDiscount(Integer spuId, PromotionActivityDO activity) {
Assert.isTrue(PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType()),
"传入的必须的促销活动必须是限时折扣");
return activity.getTimeLimitedDiscount().getItems().stream()
.anyMatch(item -> spuId.equals(item.getSpuId()));
}
private boolean isSpuMatchFullPrivilege(Integer spuId, PromotionActivityDO activity) {
Assert.isTrue(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()),
"传入的必须的促销活动必须是满减送");
PromotionActivityDO.FullPrivilege fullPrivilege = activity.getFullPrivilege();
if (RangeTypeEnum.ALL.getValue().equals(fullPrivilege.getRangeType())) {
return true;
} else if (RangeTypeEnum.PRODUCT_INCLUDE_PART.getValue().equals(fullPrivilege.getRangeType())) {
return fullPrivilege.getRangeValues().contains(spuId);
} else {
throw new IllegalArgumentException(String.format("促销活动(%s) 可用范围的类型是不正确", activity.toString()));
}
}
}

View File

@@ -0,0 +1,92 @@
package cn.iocoder.mall.promotionservice.service.banner;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.*;
import cn.iocoder.mall.promotionservice.convert.banner.BannerConvert;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.banner.BannerDO;
import cn.iocoder.mall.promotionservice.dal.mysql.mapper.banner.BannerMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants.BANNER_NOT_EXISTS;
@Service
@Validated
public class BannerService {
@Autowired
private BannerMapper bannerMapper;
/**
* 获得 Banner 列表
*
* @param listReqDTO Banner 列表查询
* @return Banner 列表
*/
public List<BannerRespDTO> listBanners(BannerListReqDTO listReqDTO) {
List<BannerDO> banners = bannerMapper.selectList(listReqDTO);
return BannerConvert.INSTANCE.convertList(banners);
}
/**
* 获得 Banner 分页
*
* @param bannerPageDTO Banner 分页查询
* @return Banner 分页结果
*/
public PageResult<BannerRespDTO> pageBanner(BannerPageReqDTO bannerPageDTO) {
IPage<BannerDO> bannerPage = bannerMapper.selectPage(bannerPageDTO);
return BannerConvert.INSTANCE.convertPage(bannerPage);
}
/**
* 创建 Banner
*
* @param createReqDTO 创建 Banner 信息
* @return banner
*/
public Integer createBanner(@Valid BannerCreateReqDTO createReqDTO) {
// 插入到数据库
BannerDO bannerDO = BannerConvert.INSTANCE.convert(createReqDTO);
bannerMapper.insert(bannerDO);
// 返回
return bannerDO.getId();
}
/**
* 更新 Banner
*
* @param updateReqDTO 更新 Banner 信息
*/
public void updateBanner(@Valid BannerUpdateReqDTO updateReqDTO) {
// 校验更新的 Banner 是否存在
if (bannerMapper.selectById(updateReqDTO.getId()) == null) {
throw ServiceExceptionUtil.exception(BANNER_NOT_EXISTS);
}
// 更新到数据库
BannerDO updateObject = BannerConvert.INSTANCE.convert(updateReqDTO);
bannerMapper.updateById(updateObject);
}
/**
* 删除 Banner
*
* @param bannerId Banner 编号
*/
public void deleteBanner(Integer bannerId) {
// 校验 Banner 存在
if (bannerMapper.selectById(bannerId) == null) {
throw ServiceExceptionUtil.exception(BANNER_NOT_EXISTS);
}
// 更新到数据库
bannerMapper.deleteById(bannerId);
}
}

View File

@@ -0,0 +1,258 @@
package cn.iocoder.mall.promotionservice.service.coupon;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.util.DateUtil;
import cn.iocoder.common.framework.util.StringUtils;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants;
import cn.iocoder.mall.promotion.api.enums.RangeTypeEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.card.CouponCardStatusEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.card.CouponCardTakeTypeEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateDateTypeEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateStatusEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateTypeEnum;
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.coupon.dto.card.CouponCardPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.promotionservice.convert.coupon.CouponCardConvert;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponCardDO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon.CouponCardMapper;
import cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon.CouponTemplateMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import java.util.stream.Collectors;
/**
* 优惠劵 Service
*/
@Service
@Validated
public class CouponCardService {
@Autowired
private CouponCardMapper couponCardMapper;
@Autowired
private CouponTemplateMapper couponTemplateMapper;
/**
* 获得用户的优惠劵
*
* @param userId 用户编号
* @param couponCardId 优惠劵编号
* @return 优惠劵
*/
public CouponCardRespDTO getCouponCard(Integer userId, Integer couponCardId) {
CouponCardDO card = couponCardMapper.selectById(couponCardId);
if (card == null) {
return null;
}
if (!Objects.equals(userId, card.getUserId())) {
return null;
}
return CouponCardConvert.INSTANCE.convert(card);
}
/**
* 获得优惠劵分页
*
* @param pageReqDTO 优惠劵分页查询
* @return 优惠劵分页结果
*/
public PageResult<CouponCardRespDTO> pageCouponCard(CouponCardPageReqDTO pageReqDTO) {
IPage<CouponCardDO> couponCardDOPage = couponCardMapper.selectPage(pageReqDTO);
return CouponCardConvert.INSTANCE.convertPage(couponCardDOPage);
}
/**
* 给用户添加优惠劵
*
* @param userId 用户编号
* @param couponTemplateId 优惠劵模板编号
* @return 优惠劵编号
*/
@Transactional
public Integer createCouponCard(Integer userId, Integer couponTemplateId) {
// 校验 CouponCardTemplate 存在
CouponTemplateDO template = couponTemplateMapper.selectById(couponTemplateId);
if (template == null) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_NOT_EXISTS.getCode());
}
// 校验 CouponCardTemplate 是 CARD
if (!CouponTemplateTypeEnum.CARD.getValue().equals(template.getType())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_NOT_CARD.getCode());
}
// 校验 CouponCardTemplate 状态是否开启
if (!CouponTemplateStatusEnum.ENABLE.getValue().equals(template.getStatus())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_STATUS_NOT_ENABLE.getCode());
}
// 校验 CouponCardTemplate 是否到达可领取的上限
if (template.getStatFetchNum() > template.getTotal()) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_TOTAL_NOT_ENOUGH.getCode());
}
// 校验单人可领取优惠劵是否到达上限
if (couponCardMapper.selectCountByUserIdAndTemplateId(userId, couponTemplateId) > template.getQuota()) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_CARD_ADD_EXCEED_QUOTA.getCode());
}
// 增加优惠劵已领取量
int updateTemplateCount = couponTemplateMapper.updateStatFetchNumIncr(couponTemplateId);
if (updateTemplateCount == 0) { // 超过 CouponCardTemplate 发放量
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_CARD_ADD_EXCEED_QUOTA.getCode());
}
// 创建优惠劵
// 1. 基本信息 + 领取情况
CouponCardDO card = new CouponCardDO()
.setTemplateId(couponTemplateId)
.setTitle(template.getTitle())
.setStatus(CouponCardStatusEnum.UNUSED.getValue())
.setUserId(userId)
.setTakeType(CouponCardTakeTypeEnum.BY_USER.getValue()); // TODO 需要改
// 2. 使用规则
card.setPriceAvailable(template.getPriceAvailable());
setCouponCardValidTime(card, template);
// 3. 使用效果
card.setPreferentialType(template.getPreferentialType())
.setPriceOff(template.getPriceOff())
.setPercentOff(template.getPercentOff()).setDiscountPriceLimit(template.getDiscountPriceLimit());
// 保存优惠劵模板到数据库
couponCardMapper.insert(card);
// 返回成功
return card.getId();
}
/**
* 用户使用优惠劵
*
* @param userId 用户编号
* @param couponCardId 优惠劵编号
*/
public void useCouponCard(Integer userId, Integer couponCardId) {
// 查询优惠劵
CouponCardDO card = couponCardMapper.selectById(couponCardId);
if (card == null) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_NOT_EXISTS.getCode());
}
if (!userId.equals(card.getUserId())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_ERROR_USER.getCode());
}
if (!CouponCardStatusEnum.UNUSED.getValue().equals(card.getStatus())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_STATUS_NOT_UNUSED.getCode());
}
if (DateUtil.isBetween(card.getValidStartTime(), card.getValidEndTime())) { // 为避免定时器没跑,实际优惠劵已经过期
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_STATUS_NOT_UNUSED.getCode());
}
// 更新优惠劵已使用
int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.UNUSED.getValue(),
new CouponCardDO().setStatus(CouponCardStatusEnum.USED.getValue()).setUsedTime(new Date()));
if (updateCount == 0) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_STATUS_NOT_UNUSED.getCode());
}
}
/**
* 用户取消使用优惠劵
*
* @param userId 用户编号
* @param couponCardId 优惠劵编号
*/
public void cancelUseCouponCard(Integer userId, Integer couponCardId) {
// 查询优惠劵
CouponCardDO card = couponCardMapper.selectById(couponCardId);
if (card == null) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_NOT_EXISTS.getCode());
}
if (!userId.equals(card.getUserId())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_ERROR_USER.getCode());
}
if (!CouponCardStatusEnum.USED.getValue().equals(card.getStatus())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_STATUS_NOT_USED.getCode());
}
// 更新优惠劵已使用
int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.USED.getValue(),
new CouponCardDO().setStatus(CouponCardStatusEnum.UNUSED.getValue())); // TODO 芋艿usedTime 未设置空,后面处理。
if (updateCount == 0) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_CARD_STATUS_NOT_USED.getCode());
}
// 有一点要注意,更新会未使用时,优惠劵可能已经过期了,直接让定时器跑过期,这里不做处理。
}
/**
* 获得用户优惠劵的可用信息列表
*
* @param listReqDTO 查询信息
* @return 优惠劵的可用信息列表
*/
public List<CouponCardAvailableRespDTO> listAvailableCouponCards(CouponCardAvailableListReqDTO listReqDTO) {
// 查询用户未使用的优惠劵列表
List<CouponCardDO> cards = couponCardMapper.selectListByUserIdAndStatus(listReqDTO.getUserId(), CouponCardStatusEnum.UNUSED.getValue());
if (cards.isEmpty()) {
return Collections.emptyList();
}
// 查询优惠劵模板集合
Map<Integer, CouponTemplateDO> templates = CollectionUtils.convertMap(
couponTemplateMapper.selectBatchIds(CollectionUtils.convertSet(cards, CouponCardDO::getTemplateId)),
CouponTemplateDO::getId);
// 逐个判断是否可用
return cards.stream().map(card -> {
CouponCardAvailableRespDTO availableCard = CouponCardConvert.INSTANCE.convert01(card);
availableCard.setUnavailableReason(isMatch(card, templates.get(card.getTemplateId()), listReqDTO.getItems()));
availableCard.setAvailable(availableCard.getUnavailableReason() == null);
return availableCard;
}).collect(Collectors.toList());
}
/**
* 匹配商品是否可以使用指定优惠劵
*
* @param card 优惠劵
* @param template 优惠劵模板
* @param items 商品 SKU 数组
* @return 如果不匹配,返回原因
*/
private String isMatch(CouponCardDO card, CouponTemplateDO template, List<CouponCardAvailableListReqDTO.Item> items) {
int totalPrice = 0;
if (RangeTypeEnum.ALL.getValue().equals(template.getRangeType())) {
totalPrice = items.stream().mapToInt(spu -> spu.getPrice() * spu.getQuantity()).sum();
} else if (RangeTypeEnum.PRODUCT_INCLUDE_PART.getValue().equals(template.getRangeType())) {
List<Integer> spuIds = StringUtils.splitToInt(template.getRangeValues(), ",");
totalPrice = items.stream().mapToInt(spu -> spuIds.contains(spu.getSpuId()) ? spu.getPrice() * spu.getQuantity() : 0).sum();
} else if (RangeTypeEnum.PRODUCT_EXCLUDE_PART.getValue().equals(template.getRangeType())) {
List<Integer> spuIds = StringUtils.splitToInt(template.getRangeValues(), ",");
totalPrice = items.stream().mapToInt(spu -> !spuIds.contains(spu.getSpuId()) ? spu.getPrice() * spu.getQuantity() : 0).sum();
} else if (RangeTypeEnum.CATEGORY_INCLUDE_PART.getValue().equals(template.getRangeType())) {
List<Integer> spuIds = StringUtils.splitToInt(template.getRangeValues(), ",");
totalPrice = items.stream().mapToInt(spu -> spuIds.contains(spu.getCid()) ? spu.getPrice() * spu.getQuantity() : 0).sum();
} else if (RangeTypeEnum.CATEGORY_EXCLUDE_PART.getValue().equals(template.getRangeType())) {
List<Integer> spuIds = StringUtils.splitToInt(template.getRangeValues(), ",");
totalPrice = items.stream().mapToInt(spu -> !spuIds.contains(spu.getCid()) ? spu.getPrice() * spu.getQuantity() : 0).sum();
}
// 总价为 0 时,说明优惠劵丫根不匹配
if (totalPrice == 0) {
return "优惠劵不匹配";
}
// 如果不满足金额
if (totalPrice < card.getPriceAvailable()) {
return String.format("差 %1$,.2f 元可用优惠劵", (card.getPriceAvailable() - totalPrice) / 100D);
}
return null;
}
private void setCouponCardValidTime(CouponCardDO card, CouponTemplateDO template) {
if (CouponTemplateDateTypeEnum.FIXED_DATE.getValue().equals(template.getDateType())) {
card.setValidStartTime(template.getValidStartTime()).setValidEndTime(template.getValidEndTime());
} else if (CouponTemplateDateTypeEnum.FIXED_TERM.getValue().equals(template.getDateType())) {
Date validStartTime = DateUtil.getDayBegin(new Date());
card.setValidStartTime(DateUtil.addDate(validStartTime, Calendar.DAY_OF_YEAR, template.getFixedStartTerm()));
Date validEndTime = DateUtil.getDayEnd(card.getValidStartTime());
card.setValidEndTime(DateUtil.addDate(validEndTime, Calendar.DAY_OF_YEAR, template.getFixedEndTerm() - 1));
}
}
}

View File

@@ -0,0 +1,173 @@
package cn.iocoder.mall.promotionservice.service.coupon;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.enums.PreferentialTypeEnum;
import cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateDateTypeEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateStatusEnum;
import cn.iocoder.mall.promotion.api.enums.coupon.template.CouponTemplateTypeEnum;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponCardTemplateCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponCardTemplateUpdateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplatePageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplateRespDTO;
import cn.iocoder.mall.promotionservice.convert.coupon.CouponTemplateConvert;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.mall.promotionservice.dal.mysql.mapper.coupon.CouponTemplateMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Date;
import static cn.iocoder.common.framework.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
/**
* 优惠劵模板 Service
*/
@Service
@Validated
public class CouponTemplateService {
@Autowired
private CouponTemplateMapper couponTemplateMapper;
// ========== 通用逻辑 =========
/**
* 获得优惠劵(码)模板
*
* @param couponCardId 优惠劵模板编号
* @return 优惠劵(码)模板
*/
public CouponTemplateRespDTO getCouponTemplate(Integer couponCardId) {
return CouponTemplateConvert.INSTANCE.convert(couponTemplateMapper.selectById(couponCardId));
}
/**
* 获得优惠劵(码)分页
*
* @param pageDTO 分页条件
* @return 优惠劵(码)分页
*/
public PageResult<CouponTemplateRespDTO> pageCouponTemplate(CouponTemplatePageReqDTO pageDTO) {
IPage<CouponTemplateDO> couponTemplatePage = couponTemplateMapper.selectPage(pageDTO);
return CouponTemplateConvert.INSTANCE.convertPage(couponTemplatePage);
}
/**
* 更新优惠劵(码)模板的状态
*
* @param couponTemplateId 优惠劵模板编号
* @param status 状态
*/
public void updateCouponTemplateStatus(Integer couponTemplateId, Integer status) {
// 校验 CouponCardTemplate 存在
CouponTemplateDO template = couponTemplateMapper.selectById(couponTemplateId);
if (template == null) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_NOT_EXISTS.getCode());
}
// 更新到数据库
CouponTemplateDO updateTemplateDO = new CouponTemplateDO().setId(couponTemplateId).setStatus(status);
couponTemplateMapper.updateById(updateTemplateDO);
}
private Boolean checkCouponTemplateDateType(Integer dateType, Date validStartTime, Date validEndTime, Integer fixedStartTerm, Integer fixedEndTerm) {
// TODO 芋艿:后续这种类型的校验,看看怎么优化到对象里
if (CouponTemplateDateTypeEnum.FIXED_DATE.getValue().equals(dateType)) { // 固定日期
if (validStartTime == null) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "生效开始时间不能为空");
}
if (validEndTime == null) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "生效结束时间不能为空");
}
if (validStartTime.after(validEndTime)) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "生效开始时间不能大于生效结束时间");
}
} else if (CouponTemplateDateTypeEnum.FIXED_TERM.getValue().equals(dateType)) { // 领取日期
if (fixedStartTerm == null) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "领取日期开始时间不能为空");
}
if (fixedEndTerm == null) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "领取日期结束时间不能为空");
}
} else {
throw new IllegalArgumentException("未知的生效日期类型:" + dateType);
}
return true;
}
private Boolean checkCouponTemplatePreferentialType(Integer preferentialType, Integer percentOff,
Integer priceOff, Integer priceAvailable) {
if (PreferentialTypeEnum.PRICE.getValue().equals(preferentialType)) {
if (priceOff == null) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "优惠金额不能为空");
}
if (priceOff >= priceAvailable) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "优惠金额不能d大于等于使用金额门槛");
}
} else if (PreferentialTypeEnum.DISCOUNT.getValue().equals(preferentialType)) {
if (percentOff == null) {
throw ServiceExceptionUtil.exception(BAD_REQUEST, "折扣百分比不能为空");
}
} else {
throw new IllegalArgumentException("未知的优惠类型:" + preferentialType);
}
return true;
}
// ========== 优惠劵模板 ==========
/**
* 添加优惠劵模板
*
* @param createReqDTO 优惠劵模板添加信息
* @return 优惠劵模板编号
*/
public Integer createCouponCardTemplate(CouponCardTemplateCreateReqDTO createReqDTO) {
// 校验生效日期相关
checkCouponTemplateDateType(createReqDTO.getDateType(),
createReqDTO.getValidStartTime(), createReqDTO.getValidEndTime(),
createReqDTO.getFixedStartTerm(), createReqDTO.getFixedEndTerm());
// 校验优惠类型
checkCouponTemplatePreferentialType(createReqDTO.getPreferentialType(), createReqDTO.getPercentOff(),
createReqDTO.getPriceOff(), createReqDTO.getPriceAvailable());
// 保存优惠劵模板到数据库
CouponTemplateDO template = CouponTemplateConvert.INSTANCE.convert(createReqDTO)
.setType(CouponTemplateTypeEnum.CARD.getValue())
.setStatus(CouponTemplateStatusEnum.ENABLE.getValue())
.setStatFetchNum(0);
couponTemplateMapper.insert(template);
// 返回成功
return template.getId();
}
/**
* 更新优惠劵模板
*
* @param updateReqDTO 优惠劵模板修改信息
*/
public void updateCouponCardTemplate(CouponCardTemplateUpdateReqDTO updateReqDTO) {
// 校验 CouponCardTemplate 存在
CouponTemplateDO template = couponTemplateMapper.selectById(updateReqDTO.getId());
if (template == null) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_NOT_EXISTS.getCode());
}
// 校验 CouponCardTemplate 是 CARD
if (!CouponTemplateTypeEnum.CARD.getValue().equals(template.getType())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_NOT_CARD.getCode());
}
// 校验发放数量不能减少
if (updateReqDTO.getTotal() < template.getTotal()) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeConstants.COUPON_TEMPLATE_TOTAL_CAN_NOT_REDUCE.getCode());
}
// 更新优惠劵模板到数据库
CouponTemplateDO updateTemplateDO = CouponTemplateConvert.INSTANCE.convert(updateReqDTO);
couponTemplateMapper.updateById(updateTemplateDO);
}
// ========== 优惠码模板 TODO 芋艿:以后开发 ==========
}

View File

@@ -0,0 +1,93 @@
package cn.iocoder.mall.promotionservice.service.recommend;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.*;
import cn.iocoder.mall.promotionservice.convert.recommend.ProductRecommendConvert;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.recommend.ProductRecommendDO;
import cn.iocoder.mall.promotionservice.dal.mysql.mapper.recommend.ProductRecommendMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants.PRODUCT_RECOMMEND_EXISTS;
import static cn.iocoder.mall.promotion.api.enums.PromotionErrorCodeConstants.PRODUCT_RECOMMEND_NOT_EXISTS;
/**
* 商品推荐 Service
*/
@Service
@Validated
public class ProductRecommendService {
@Autowired
private ProductRecommendMapper productRecommendMapper;
/**
* 获得商品推荐列表
*
* @param listReqDTO 列表查询 DTO
* @return 商品推荐列表
*/
public List<ProductRecommendRespDTO> listProductRecommends(ProductRecommendListReqDTO listReqDTO) {
List<ProductRecommendDO> productRecommends = productRecommendMapper.selectList(listReqDTO);
return ProductRecommendConvert.INSTANCE.convertList(productRecommends);
}
/**
* 获得商品推荐分页
*
* @param pageReqDTO 分页查询 DTO
* @return 商品推荐分页
*/
public PageResult<ProductRecommendRespDTO> pageProductRecommend(ProductRecommendPageReqDTO pageReqDTO) {
IPage<ProductRecommendDO> productRecommendPage = productRecommendMapper.selectPage(pageReqDTO);
return ProductRecommendConvert.INSTANCE.convertPage(productRecommendPage);
}
/**
* 创建商品推荐
*
* @param createReqDTO 商品推荐信息
* @return 商品推荐编号
*/
public Integer createProductRecommend(ProductRecommendCreateReqDTO createReqDTO) {
// 校验商品是否已经推荐
if (productRecommendMapper.selectByProductSpuIdAndType(createReqDTO.getProductSpuId(), createReqDTO.getType()) != null) {
throw ServiceExceptionUtil.exception(PRODUCT_RECOMMEND_EXISTS);
}
// 保存到数据库
ProductRecommendDO productRecommend = ProductRecommendConvert.INSTANCE.convert(createReqDTO);
productRecommendMapper.insert(productRecommend);
// 返回成功
return productRecommend.getId();
}
public void updateProductRecommend(ProductRecommendUpdateReqDTO updateReqDTO) {
// 校验更新的商品推荐存在
if (productRecommendMapper.selectById(updateReqDTO.getId()) == null) {
throw ServiceExceptionUtil.exception(PRODUCT_RECOMMEND_NOT_EXISTS);
}
// 校验商品是否已经推荐
ProductRecommendDO existProductRecommend = productRecommendMapper.selectByProductSpuIdAndType(updateReqDTO.getProductSpuId(), updateReqDTO.getType());
if (existProductRecommend != null && !existProductRecommend.getId().equals(updateReqDTO.getId())) {
throw ServiceExceptionUtil.exception(PRODUCT_RECOMMEND_EXISTS.getCode());
}
// 更新到数据库
ProductRecommendDO updateProductRecommend = ProductRecommendConvert.INSTANCE.convert(updateReqDTO);
productRecommendMapper.updateById(updateProductRecommend);
}
public void deleteProductRecommend(Integer productRecommendId) {
// 校验更新的商品推荐存在
if (productRecommendMapper.selectById(productRecommendId) == null) {
throw ServiceExceptionUtil.exception(PRODUCT_RECOMMEND_NOT_EXISTS);
}
// 更新到数据库
productRecommendMapper.deleteById(productRecommendId);
}
}

View File

@@ -0,0 +1,21 @@
spring:
# 数据源配置项
datasource:
url: jdbc:mysql://localhost:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
driver-class-name: com.mysql.jdbc.Driver
username: root
password: zhuyang
# Spring Cloud 配置项
cloud:
nacos:
# Spring Cloud Nacos Discovery 配置项
discovery:
server-addr: localhost:8848 # Nacos 服务器地址
namespace: dev # Nacos 命名空间
# Dubbo 配置项
dubbo:
# Dubbo 注册中心
registry:
# address: spring-cloud://localhost:8848 # 指定 Dubbo 服务注册中心的地址
address: nacos://localhost:8848?namespace=dev # 指定 Dubbo 服务注册中心的地址

View File

@@ -0,0 +1,24 @@
spring:
# 数据源配置项
datasource:
url: jdbc:mysql://localhost:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
driver-class-name: com.mysql.jdbc.Driver
username: root
password: zhuyang
# Spring Cloud 配置项
cloud:
nacos:
# Spring Cloud Nacos Discovery 配置项
discovery:
server-addr: localhost:8848 # Nacos 服务器地址
namespace: dev # Nacos 命名空间
# Dubbo 配置项
dubbo:
# Dubbo 注册中心
registry:
# address: spring-cloud://localhost:8848 # 指定 Dubbo 服务注册中心的地址
address: nacos://localhost:8848?namespace=dev # 指定 Dubbo 服务注册中心的地址
# Dubbo 服务提供者的配置
provider:
tag: ${DUBBO_TAG} # Dubbo 路由分组

View File

@@ -0,0 +1,64 @@
spring:
# Application 的配置项
application:
name: promotion-service
# Profile 的配置项
profiles:
active: local
# MyBatis Plus 配置项
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: auto
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: cn.iocoder.mall.promotionservice.dal.mysql.dataobject
# Dubbo 配置项
dubbo:
# Spring Cloud Alibaba Dubbo 专属配置
cloud:
subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用
# Dubbo 提供者的协议
protocol:
name: dubbo
port: -1
# Dubbo 提供服务的扫描基础包
scan:
base-packages: cn.iocoder.mall.promotionservice.rpc
# Dubbo 服务提供者的配置
provider:
filter: -exception
validation: true # 开启 Provider 参数校验
version: 1.0.0 # 服务的版本号
# Dubbo 服务消费者的配置
consumer:
ErrorCodeRpc:
version: 1.0.0
ProductSkuRpc:
version: 1.0.0
ProductSpuRpc:
version: 1.0.0
# RocketMQ 配置项
rocketmq:
name-server: localhost:9876
producer:
group: ${spring.application.name}-producer-group
# Actuator 监控配置项
management:
server.port: 38085 # 独立端口,避免被暴露出去
endpoints.web.exposure.include: '*' # 暴露所有监控端点
server.port: ${management.server.port} # 设置使用 Actuator 的服务器端口,因为 RPC 服务不需要 Web 端口
# Mall 配置项
mall:
# 错误码配置项对应 ErrorCodeProperties 配置类
error-code:
group: ${spring.application.name}
constants-class: cn.iocoder.mall.promotionservice.enums.PromotionErrorCodeConstants

View File

@@ -0,0 +1,12 @@
<?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>

View File

@@ -0,0 +1,28 @@
package cn.iocoder.mall.promotionservice.dal.mysql.mapper.activity;
import cn.iocoder.mall.promotion.api.enums.activity.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.activity.PromotionActivityDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Collections;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class PromotionActivityMapperTest {
@Autowired
private PromotionActivityMapper promotionActivityMapper;
@Test
public void testSelectListByStatus() {
List<PromotionActivityDO> result = promotionActivityMapper.selectListByStatus(
Collections.singleton(PromotionActivityStatusEnum.RUN.getValue()));
System.out.println(result.size());
}
}

View File

@@ -0,0 +1 @@
package cn.iocoder.mall.promotionservice.manager;

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.promotionservice.manager.price;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
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.Arrays;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class PriceManagerTest {
@Autowired
private PriceManager priceManager;
@Test
public void testCalcProductPrice() {
PriceProductCalcReqDTO calcReqDTO = new PriceProductCalcReqDTO();
PriceProductCalcReqDTO.Item item01 = new PriceProductCalcReqDTO.Item(33, 2, true); // 满足满减送的商品
PriceProductCalcReqDTO.Item item02 = new PriceProductCalcReqDTO.Item(34, 2, true); // 满足限时折扣的商品
calcReqDTO.setItems(Arrays.asList(item01, item02));
PriceProductCalcRespDTO calcRespDTO = priceManager.calcProductPrice(calcReqDTO);
System.out.println(calcRespDTO);
}
}