Explorar o código

促销活动公共方法开发

qizai hai 1 ano
pai
achega
2a25a6de54
Modificáronse 23 ficheiros con 1327 adicións e 19 borrados
  1. 7 1
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/FavoriteType.java
  2. 35 0
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/GoodsType.java
  3. 79 0
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/PromotionType.java
  4. 2 2
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/UserCouponStatus.java
  5. 42 0
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/dto/CouponActivityItemDTO.java
  6. 4 0
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/dto/GoodsDTO.java
  7. 10 0
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/service/CouponActivityItemService.java
  8. 32 1
      vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/util/PromotionUtils.java
  9. 7 0
      vehicle-server/pom.xml
  10. 15 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/dao/CouponActivityItemMapper.java
  11. 12 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/CouponActivity.java
  12. 139 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/PromotionGoods.java
  13. 119 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/User.java
  14. 110 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/UserCoupon.java
  15. 107 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/AbstractPromotionService.java
  16. 30 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/CouponActivityItemService.java
  17. 8 2
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/CouponActivityService.java
  18. 241 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/AbstractPromotionServiceImpl.java
  19. 35 0
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/CouponActivityItemServiceImpl.java
  20. 278 2
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/CouponActivityServiceImpl.java
  21. 8 9
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/GoodsServiceImpl.java
  22. 2 2
      vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/UserCouponServiceImpl.java
  23. 5 0
      vehicle-server/src/main/resources/mapper/CouponActivityItemMapper.xml

+ 7 - 1
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/FavoriteType.java

@@ -14,7 +14,13 @@ public enum FavoriteType implements Enumeration {
     /**
      * 商家
      */
-    MERCHANT("商家");
+    MERCHANT("商家"),
+
+    /**
+     * 商品
+     */
+    GOODS("商品");
+
     /**
      * 类型描述
      */

+ 35 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/GoodsType.java

@@ -0,0 +1,35 @@
+package com.chelvc.cloud.vehicle.api.constant;
+
+import lombok.Getter;
+
+/**
+ * 商品类型枚举
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+@Getter
+public enum GoodsType {
+
+    /**
+     * "实物商品"
+     */
+    PHYSICAL_GOODS("实物商品"),
+    /**
+     * "虚拟商品"
+     */
+    VIRTUAL_GOODS("虚拟商品"),
+    /**
+     * "电子卡券"
+     */
+    E_COUPON("电子卡券");
+
+    /**
+     * 状态描述
+     */
+    private final String description;
+
+    GoodsType(String description) {
+        this.description = description;
+    }
+}

+ 79 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/PromotionType.java

@@ -0,0 +1,79 @@
+package com.chelvc.cloud.vehicle.api.constant;
+
+
+import lombok.Getter;
+
+/**
+ * 促销分类枚举
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+@Getter
+public enum PromotionType {
+    /**
+     * 拼团
+     */
+    PINTUAN("拼团"),
+
+    /**
+     * 秒杀
+     */
+    SECKILL("秒杀"),
+
+    /**
+     * 优惠券
+     */
+    COUPON("优惠券"),
+
+    /**
+     * 平台优惠券
+     */
+    PLATFORM_COUPON("平台优惠券"),
+
+    /**
+     * 满减
+     */
+    FULL_DISCOUNT("满减"),
+
+    /**
+     * 积分商品
+     */
+    POINTS_GOODS("积分商品"),
+
+    /**
+     * 砍价
+     */
+    KANJIA("砍价"),
+
+    /**
+     * 优惠券活动
+     */
+    COUPON_ACTIVITY("优惠券活动");
+
+    /**
+     * 有促销库存的活动类型
+     */
+    static final PromotionType[] haveStockPromotion = new PromotionType[]{PINTUAN, SECKILL, KANJIA, POINTS_GOODS};
+
+    /**
+     * 状态描述
+     */
+    private final String description;
+
+    PromotionType(String description) {
+        this.description = description;
+    }
+
+    /**
+     * 是否拥有库存
+     */
+    public static boolean haveStock(String promotionType) {
+        for (PromotionType type : haveStockPromotion) {
+            if (type.name().equals(promotionType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 2 - 2
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/UserCouponStatus.java

@@ -12,9 +12,9 @@ import lombok.Getter;
 @Getter
 public enum UserCouponStatus implements Enumeration {
     /**
-     * 未使用
+     * 领取
      */
-    UNUSED("未使用"),
+    CLAIMED("领取"),
 
     /**
      * 已使用

+ 42 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/dto/CouponActivityItemDTO.java

@@ -0,0 +1,42 @@
+package com.chelvc.cloud.vehicle.api.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+
+
+/**
+ * 优惠券活动项目信息
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CouponActivityItemDTO implements Serializable {
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 优惠券活动ID
+     */
+    private Long activityId;
+
+    /**
+     * 优惠券ID
+     */
+    private Long couponId;
+
+    /**
+     * 优惠券数量
+     */
+    private Integer num;
+}

+ 4 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/dto/GoodsDTO.java

@@ -93,4 +93,8 @@ public class GoodsDTO implements Serializable {
      */
     private GoodsStatus status;
 
+    /**
+     * 是否收藏
+     */
+    private Boolean favorited;
 }

+ 10 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/service/CouponActivityItemService.java

@@ -0,0 +1,10 @@
+package com.chelvc.cloud.vehicle.api.service;
+
+/**
+ * 优惠券活动项目业务接口
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+public interface CouponActivityItemService {
+}

+ 32 - 1
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/util/PromotionUtils.java

@@ -1,7 +1,10 @@
 package com.chelvc.cloud.vehicle.api.util;
 
+import com.chelvc.framework.common.util.DateUtils;
 import lombok.extern.slf4j.Slf4j;
 
+import java.util.Date;
+
 /**
  * 优惠活动通用验证类
  *
@@ -13,7 +16,35 @@ public class PromotionUtils {
 
     public static final String START_TIME_COLUMN = "start_time";
     public static final String END_TIME_COLUMN = "end_time";
-    public static final String PLATFORM_ID = "0";
+    public static final Long PLATFORM_ID = 0L;
     public static final String PLATFORM_NAME = "platform";
 
+    /**
+     * 参数验证
+     * 1、活动起始时间必须大于当前时间
+     * 2、验证活动开始时间是否大于活动结束时间
+     *
+     * @param startTime 活动开始时间
+     * @param endTime   活动结束时间
+     */
+    public static void checkPromotionTime(Date startTime, Date endTime) {
+        if (startTime == null) {
+            throw new RuntimeException("活动起始时间不能为空");
+        }
+
+        Date now = DateUtils.today();
+
+        //如果活动起始时间小于现在时间
+        if (now.after(startTime)) {
+            throw new RuntimeException("活动起始时间不能小于当前时间");
+        }
+        //如果活动结束时间小于现在时间
+        if (endTime != null && now.after(endTime)) {
+            throw new RuntimeException("活动结束时间不能小于当前时间");
+        }
+        //开始时间不能大于结束时间
+        if (endTime != null && startTime.after(endTime)) {
+            throw new RuntimeException("活动起始时间必须大于结束时间");
+        }
+    }
 }

+ 7 - 0
vehicle-server/pom.xml

@@ -20,6 +20,7 @@
         <vehicle-api.version>1.0.0-SNAPSHOT</vehicle-api.version>
         <framework-redis.version>1.0.0-RELEASE</framework-redis.version>
         <framework-database.version>1.0.0-RELEASE</framework-database.version>
+        <Hutool-version>5.8.14</Hutool-version>
     </properties>
 
     <dependencies>
@@ -47,5 +48,11 @@
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
         </dependency>
+        <!-- Hutool工具包 -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${Hutool-version}</version>
+        </dependency>
     </dependencies>
 </project>

+ 15 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/dao/CouponActivityItemMapper.java

@@ -0,0 +1,15 @@
+package com.chelvc.cloud.vehicle.server.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chelvc.cloud.vehicle.server.entity.CouponActivityItem;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 优惠券活动项目数据操作接口
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+@Mapper
+public interface CouponActivityItemMapper extends BaseMapper<CouponActivityItem> {
+}

+ 12 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/CouponActivity.java

@@ -7,6 +7,8 @@ import com.chelvc.cloud.vehicle.api.constant.CouponFrequency;
 import lombok.*;
 import lombok.experimental.SuperBuilder;
 
+import java.util.List;
+
 /**
  * 优惠券活动数据模型
  *
@@ -42,4 +44,14 @@ public class CouponActivity extends BasePromotion {
      */
     private String activityScopeInfo;
 
+    /**
+     * 优惠券列表
+     */
+    private List<CouponActivityItem> couponActivityItems;
+
+    /**
+     * 用户列表
+     */
+    private List<User> users;
+
 }

+ 139 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/PromotionGoods.java

@@ -0,0 +1,139 @@
+package com.chelvc.cloud.vehicle.server.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.chelvc.cloud.vehicle.api.constant.GoodsType;
+import com.chelvc.cloud.vehicle.api.constant.PromotionScopeType;
+import com.chelvc.cloud.vehicle.api.constant.PromotionType;
+import com.chelvc.framework.database.entity.ModifyEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import lombok.experimental.SuperBuilder;
+
+import java.util.Date;
+
+/**
+ * 促销活动商品数据模型
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = true)
+@TableName(autoResultMap = true)
+public class PromotionGoods extends ModifyEntity<Long> {
+
+    /**
+     * 主键
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 商家ID
+     */
+    private String merchantId;
+
+    /**
+     * 商家名称
+     */
+    private String merchantName;
+
+    /**
+     * 商品ID
+     */
+    private String goodsId;
+
+    /**
+     * 商品名称
+     */
+    private String goodsName;
+
+    /**
+     * 缩略图
+     */
+    private String thumbnail;
+
+    /**
+     * 活动开始时间
+     */
+    private Date startTime;
+
+    /**
+     * 活动结束时间
+     */
+    private Date endTime;
+
+    /**
+     * 活动ID
+     */
+    private String promotionId;
+
+    /**
+     * 促销类型
+     */
+    private PromotionType promotionType;
+
+    /**
+     * 商品类型
+     */
+    private GoodsType goodsType;
+
+    /**
+     * 活动标题
+     */
+    private String title;
+
+    /**
+     * 卖出的商品数量
+     */
+    private Integer num;
+
+    /**
+     * 原价
+     */
+    private Double originalPrice;
+
+    /**
+     * 促销价格
+     */
+    private Double price;
+
+    /**
+     * 兑换积分
+     */
+    private Long points;
+
+    /**
+     * 限购数量
+     */
+    private Integer limitNum;
+
+    /**
+     * 促销库存
+     */
+    private Integer quantity;
+
+    /**
+     * 分类path
+     */
+    private String categoryPath;
+
+    /**
+     * 关联范围类型
+     */
+    private String scopeType = PromotionScopeType.PORTION_GOODS.name();
+
+    /**
+     * 范围关联的ID
+     */
+    private String scopeId;
+}

+ 119 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/User.java

@@ -0,0 +1,119 @@
+package com.chelvc.cloud.vehicle.server.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.chelvc.cloud.uc.api.constant.Gender;
+import com.chelvc.cloud.uc.api.constant.Scope;
+import com.chelvc.framework.common.model.Platform;
+import com.chelvc.framework.common.model.Terminal;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户数据模型
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+@TableName(autoResultMap = true)
+public class User implements Serializable {
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 应用范围
+     */
+    private Scope scope;
+
+    /**
+     * 手机号码
+     */
+    private String mobile;
+
+    /**
+     * 用户头像
+     */
+    private String avatar;
+
+    /**
+     * 用户昵称
+     */
+    private String nickname;
+
+    /**
+     * 用户性别
+     */
+    private Gender gender;
+
+    /**
+     * 用户生日
+     */
+    private Date birthday;
+
+    /**
+     * 注册IP
+     */
+    private String host;
+
+    /**
+     * 设备标识
+     */
+    private String device;
+
+    /**
+     * openid
+     */
+    private String openid;
+
+    /**
+     * 微信unionid
+     */
+    private String unionid;
+
+    /**
+     * 渠道来源
+     */
+    private String channel;
+
+    /**
+     * 注册平台
+     */
+    private Platform platform;
+
+    /**
+     * 注册终端
+     */
+    private Terminal terminal;
+
+    /**
+     * 客户端版本
+     */
+    private String version;
+
+    /**
+     * 是否启用
+     */
+    private Boolean enabled;
+
+    /**
+     * 活跃时间
+     */
+    private Date activeTime;
+
+    /**
+     * 激活时间
+     */
+    private Date activateTime;
+}

+ 110 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/UserCoupon.java

@@ -1,10 +1,17 @@
 package com.chelvc.cloud.vehicle.server.entity;
 
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
+import com.chelvc.cloud.vehicle.api.constant.CouponClaimType;
 import com.chelvc.cloud.vehicle.api.constant.CouponType;
+import com.chelvc.cloud.vehicle.api.constant.PromotionScopeType;
+import com.chelvc.cloud.vehicle.api.constant.UserCouponStatus;
 import com.chelvc.framework.database.entity.ModifyEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -12,6 +19,8 @@ import lombok.NoArgsConstructor;
 import lombok.ToString;
 import lombok.experimental.SuperBuilder;
 
+import java.util.Date;
+
 /**
  * 优惠券领取数据模型
  *
@@ -56,4 +65,105 @@ public class UserCoupon extends ModifyEntity<Long> {
      */
     @TableField(exist = false)
     private Coupon coupon;
+
+    /**
+     * 商家ID
+     */
+    private String merchantId;
+
+    /**
+     * 商家名称
+     */
+    private String merchantName;
+
+    /**
+     * 面额
+     */
+    private Double price;
+
+    /**
+     * 折扣
+     */
+    private Double discount;
+
+    /**
+     * 消费门槛
+     */
+    private Double consumeThreshold;
+
+    /**
+     * 用户名称
+     */
+    private String userName;
+
+    /**
+     * 关联范围类型
+     */
+    private PromotionScopeType scopeType;
+
+    /**
+     * 优惠券类型
+     */
+    private CouponType couponType;
+
+    /**
+     * 范围关联的ID
+     */
+    private String scopeId;
+
+    /**
+     * 使用起始时间
+     */
+    private Date startTime;
+
+    /**
+     * 使用截止时间
+     */
+    private Date endTime;
+
+    /**
+     * 优惠券类型,分为免费领取和活动赠送
+     */
+    private CouponClaimType claimType;
+
+    /**
+     * 是否是平台优惠券
+     */
+    private Boolean platformFlag;
+
+    /**
+     * 店铺承担比例
+     */
+    private Double storeCommission;
+
+    /**
+     * 核销时间
+     */
+    private Date consumptionTime;
+
+    /**
+     * 用户优惠券状态
+     */
+    private UserCouponStatus userCouponStatus;
+
+    public UserCoupon(Coupon coupon) {
+        setCouponId(coupon.getId());
+        setMemberId(coupon.getMerchantId());
+        setMemberName(coupon.getM());
+        setPrice(coupon.getPrice());
+        setDiscount(coupon.getCouponDiscount());
+        setConsumeThreshold(coupon.getConsumeThreshold());
+        setScopeType(coupon.getScopeType());
+        setScopeId(coupon.getScopeId());
+        setCouponType(coupon.getCouponType());
+        setStartTime(coupon.getStartTime() == null ? new Date() : coupon.getStartTime());
+
+        setGetType(coupon.getGetType());
+        setStoreCommission(coupon.getStoreCommission());
+        if (coupon.getRangeDayType().equals(CouponRangeDayEnum.FIXEDTIME.name())) {
+            setEndTime(coupon.getEndTime());
+        } else {
+            setEndTime(DateUtil.endOfDay(DateUtil.offset(new DateTime(), DateField.DAY_OF_YEAR, (coupon.getEffectiveDays() - 1))));
+        }
+    }
 }

+ 107 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/AbstractPromotionService.java

@@ -0,0 +1,107 @@
+package com.chelvc.cloud.vehicle.server.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chelvc.cloud.vehicle.api.constant.PromotionType;
+import com.chelvc.cloud.vehicle.server.entity.BasePromotion;
+
+import java.util.List;
+
+/**
+ * 抽象通用促销服务
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+public interface AbstractPromotionService<T extends BasePromotion> extends IService<T> {
+
+    /**
+     * 通用促销保存
+     * 调用顺序:
+     * 1. initPromotion 初始化促销信息
+     * 2. checkPromotion 检查促销参数
+     * 3. save 保存促销信息
+     * 4. updatePromotionGoods 更新促销商品信息
+     * 5。 updateEsGoodsIndex 更新商品索引促销信息
+     *
+     * @param promotion 促销信息
+     * @return 是否保存成功
+     */
+    boolean savePromotion(T promotion);
+
+    /**
+     * 通用促销更新
+     * 调用顺序:
+     * 1. checkStatus 检查促销状态
+     * 2. checkPromotion 检查促销参数
+     * 3. saveOrUpdate 保存促销信息
+     * 4. updatePromotionGoods 更新促销商品信息
+     * 5. updateEsGoodsIndex 更新商品索引促销信息
+     *
+     * @param promotions 促销信息
+     * @return 是否更新成功
+     */
+    boolean updatePromotion(T promotions);
+
+    /**
+     * 更新促销状态
+     * 如果要更新促销状态为关闭,startTime和endTime置为空即可
+     *
+     * @param ids       促销id集合
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return 是否更新成功
+     */
+    boolean updateStatus(List<String> ids, Long startTime, Long endTime);
+
+    /**
+     * 移除促销活动
+     *
+     * @param ids 促销活动id集合
+     * @return 是否移除成功
+     */
+    boolean removePromotion(List<String> ids);
+
+    /**
+     * 初始化促销字段
+     *
+     * @param promotions 促销实体
+     */
+    void initPromotion(T promotions);
+
+    /**
+     * 检查促销参数
+     *
+     * @param promotions 促销实体
+     */
+    void checkPromotion(T promotions);
+
+    /**
+     * 检查促销状态
+     *
+     * @param promotions 促销实体
+     */
+    void checkStatus(T promotions);
+
+    /**
+     * 更新促销商品信息
+     *
+     * @param promotions 促销实体
+     * @return
+     */
+    boolean updatePromotionsGoods(T promotions);
+
+    /**
+     * 更新促销信息到商品索引
+     *
+     * @param promotions 促销实体
+     */
+    void updateEsGoodsIndex(T promotions);
+
+    /**
+     * 当前促销类型
+     *
+     * @return 当前促销类型
+     */
+    PromotionType getPromotionType();
+
+}

+ 30 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/CouponActivityItemService.java

@@ -0,0 +1,30 @@
+package com.chelvc.cloud.vehicle.server.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chelvc.cloud.vehicle.server.entity.CouponActivityItem;
+
+import java.util.List;
+
+/**
+ * 优惠券活动项目业务操作接口
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+public interface CouponActivityItemService extends IService<CouponActivityItem> {
+
+    /**
+     * 获取优惠券活动关联优惠券列表
+     *
+     * @param activityId 优惠券活动ID
+     * @return 优惠券关联优惠券列表
+     */
+    List<CouponActivityItem> listCouponActivityItems(Long activityId);
+
+    /**
+     * 根据优惠券id删除优惠活动关联信息项
+     *
+     * @param couponIds 优惠券id集合
+     */
+    void removeByCouponId(List<Long> couponIds);
+}

+ 8 - 2
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/CouponActivityService.java

@@ -1,6 +1,5 @@
 package com.chelvc.cloud.vehicle.server.service;
 
-import com.baomidou.mybatisplus.extension.service.IService;
 import com.chelvc.cloud.vehicle.server.entity.CouponActivity;
 
 /**
@@ -9,5 +8,12 @@ import com.chelvc.cloud.vehicle.server.entity.CouponActivity;
  * @author qizai
  * @date 2023/11/10
  */
-public interface CouponActivityService extends IService<CouponActivity> {
+public interface CouponActivityService extends AbstractPromotionService<CouponActivity> {
+
+    /**
+     * 精准发券
+     *
+     * @param couponActivity 精准发券
+     */
+    void specify(CouponActivity couponActivity);
 }

+ 241 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/AbstractPromotionServiceImpl.java

@@ -0,0 +1,241 @@
+package com.chelvc.cloud.vehicle.server.service.impl;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chelvc.cloud.vehicle.api.constant.PromotionScopeType;
+import com.chelvc.cloud.vehicle.api.util.PromotionUtils;
+import com.chelvc.cloud.vehicle.server.entity.BasePromotion;
+import com.chelvc.cloud.vehicle.server.entity.PromotionGoods;
+import com.chelvc.cloud.vehicle.server.service.AbstractPromotionService;
+import com.chelvc.framework.base.exception.ResourceUnavailableException;
+import com.chelvc.framework.common.util.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 抽象通用促销服务业务操作实现
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+public abstract class AbstractPromotionServiceImpl<M extends BaseMapper<T>, T extends BasePromotion> extends ServiceImpl<M, T> implements AbstractPromotionService<T> {
+
+    /**
+     * 促销商品
+     */
+    @Autowired
+    private PromotionGoodsService promotionGoodsService;
+
+    /**
+     * rocketMq配置
+     */
+    @Autowired
+    private RocketmqCustomProperties rocketmqCustomProperties;
+
+    @Autowired
+    private ApplicationEventPublisher applicationEventPublisher;
+
+    /**
+     * 通用促销保存
+     * 调用顺序:
+     * 1. initPromotion 初始化促销信息
+     * 2. checkPromotion 检查促销参数
+     * 3. save 保存促销信息
+     * 4. updatePromotionGoods 更新促销商品信息
+     * 5。 updateEsGoodsIndex 更新商品索引促销信息
+     *
+     * @param promotion 促销信息
+     * @return 是否保存成功
+     */
+    @Override
+    @Transactional(rollbackFor = {Exception.class})
+    public boolean savePromotion(T promotion) {
+        this.initPromotion(promotion);
+        this.checkPromotion(promotion);
+        boolean save = this.save(promotion);
+        if (this.updatePromotionsGoods(promotion)) {
+            this.updateEsGoodsIndex(promotion);
+        }
+        return save;
+    }
+
+    /**
+     * 通用促销更新
+     * 调用顺序:
+     * 1. checkStatus 检查促销状态
+     * 2. checkPromotions 检查促销参数
+     * 3. saveOrUpdate 保存促销信息
+     * 4. updatePromotionGoods 更新促销商品信息
+     * 5. updateEsGoodsIndex 更新商品索引促销信息
+     *
+     * @param promotions 促销信息
+     * @return 是否更新成功
+     */
+    @Override
+    @Transactional(rollbackFor = {Exception.class})
+    public boolean updatePromotion(T promotions) {
+        this.checkStatus(promotions);
+        this.checkPromotion(promotions);
+        boolean save = this.saveOrUpdate(promotions);
+        if (this.updatePromotionsGoods(promotions)) {
+            this.updateEsGoodsIndex(promotions);
+        }
+        return save;
+    }
+
+    /**
+     * 更新促销状态
+     * 如果要更新促销状态为关闭,startTime和endTime置为空即可
+     *
+     * @param ids       促销id集合
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return 是否更新成功
+     */
+    @Override
+    @Transactional(rollbackFor = {Exception.class})
+    public boolean updateStatus(List<String> ids, Long startTime, Long endTime) {
+        List<T> promotionsList = this.list(new QueryWrapper<T>().in("id", ids));
+        for (T t : promotionsList) {
+            if (startTime != null && endTime != null) {
+                t.setStartTime(new Date(startTime));
+                t.setEndTime(new Date(endTime));
+            } else {
+                t.setStartTime(null);
+                t.setEndTime(null);
+            }
+            this.checkStatus(t);
+            this.updatePromotionsGoods(t);
+            this.updateEsGoodsIndex(t);
+        }
+        if (startTime != null && endTime != null) {
+            return this.update(new UpdateWrapper<T>().in("id", ids).set("start_time", new Date(startTime)).set("end_time", new Date(endTime)));
+        } else {
+            return this.update(new UpdateWrapper<T>().in("id", ids).set("start_time", null).set("end_time", null));
+        }
+    }
+
+    /**
+     * 移除促销活动
+     *
+     * @param ids 促销活动id集合
+     * @return 是否移除成功
+     */
+    @Override
+    @Transactional(rollbackFor = {Exception.class})
+    public boolean removePromotion(List<String> ids) {
+        for (String id : ids) {
+            T promotions = this.getById(id);
+            this.checkStatus(promotions);
+            promotions.setStartTime(null);
+            promotions.setEndTime(null);
+            this.updateEsGoodsIndex(promotions);
+        }
+        this.promotionGoodsService.deletePromotionGoods(ids);
+        return this.removeByIds(ids);
+    }
+
+    /**
+     * 初始化促销字段
+     *
+     * @param promotions 促销实体
+     */
+    @Override
+    public void initPromotion(T promotions) {
+        if (StringUtils.isEmpty(promotions.getScopeType())) {
+            promotions.setScopeType(PromotionScopeType.PORTION_GOODS.name());
+        }
+    }
+
+    /**
+     * 检查促销参数
+     *
+     * @param promotion 促销实体
+     */
+    @Override
+    public void checkPromotion(T promotion) {
+        PromotionUtils.checkPromotionTime(promotion.getStartTime(), promotion.getEndTime());
+    }
+
+    /**
+     * 检查促销状态
+     *
+     * @param promotion 促销实体
+     */
+    @Override
+    public void checkStatus(T promotion) {
+        T byId = this.getById(promotion.getId());
+        if (byId == null) {
+            throw new ResourceUnavailableException(ResultCode.PROMOTION_ACTIVITY_ERROR);
+        }
+    }
+
+    /**
+     * 更新促销商品信息
+     *
+     * @param promotion 促销实体
+     * @return
+     */
+    @Override
+    @Transactional(rollbackFor = {Exception.class})
+    public boolean updatePromotionsGoods(T promotion) {
+        if (promotion.getStartTime() == null && promotion.getEndTime() == null) {
+            this.promotionGoodsService.deletePromotionGoods(Collections.singletonList(promotions.getId()));
+            return true;
+        }
+        boolean result = true;
+        if (CharSequenceUtil.equalsAny(promotion.getScopeType(), PromotionScopeType.ALL.name(),
+                PromotionScopeType.PORTION_GOODS_CATEGORY.name())) {
+            PromotionGoods promotionGoods = new PromotionGoods();
+            promotionGoods.setScopeId(promotions.getScopeId());
+            promotionGoods.setScopeType(promotions.getScopeType());
+            promotionGoods.setPromotionId(promotions.getId());
+            promotionGoods.setStoreId(promotions.getStoreId());
+            promotionGoods.setStoreName(promotions.getStoreName());
+            promotionGoods.setStartTime(promotions.getStartTime());
+            promotionGoods.setEndTime(promotions.getEndTime());
+            promotionGoods.setPromotionType(this.getPromotionType().name());
+            promotionGoods.setTitle(promotions.getPromotionName());
+            this.promotionGoodsService.deletePromotionGoods(Collections.singletonList(promotions.getId()));
+            result = this.promotionGoodsService.save(promotionGoods);
+        }
+        return result;
+    }
+
+    /**
+     * 更新促销信息到商品索引
+     *
+     * @param promotions 促销实体
+     */
+    @Override
+    @Transactional(rollbackFor = {Exception.class})
+    public void updateEsGoodsIndex(T promotions) {
+        if (promotions.getStartTime() == null && promotions.getEndTime() == null) {
+            Map<Object, Object> build = MapBuilder.create().put("promotionKey", this.getPromotionType() + "-" + promotions.getId()).put("scopeId", promotions.getScopeId()).build();
+            //删除商品促销消息
+            applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("删除商品促销事件", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.DELETE_GOODS_INDEX_PROMOTIONS.name(), JSONUtil.toJsonStr(build)));
+        } else {
+
+            String esPromotionKey = this.getPromotionType().name() + "-" + promotions.getId();
+            Map<String, Object> map = new HashMap<>();
+            // es促销key
+            map.put("esPromotionKey", esPromotionKey);
+            // 促销类型全路径名
+            map.put("promotionsType", promotions.getClass().getName());
+            // 促销实体
+            map.put("promotions", promotions);
+            applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("更新商品索引促销事件", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.UPDATE_GOODS_INDEX_PROMOTIONS.name(), JSONUtil.toJsonStr(map)));
+        }
+    }
+
+}

+ 35 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/CouponActivityItemServiceImpl.java

@@ -0,0 +1,35 @@
+package com.chelvc.cloud.vehicle.server.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chelvc.cloud.vehicle.server.dao.CouponActivityItemMapper;
+import com.chelvc.cloud.vehicle.server.entity.CouponActivityItem;
+import com.chelvc.cloud.vehicle.server.service.CouponActivityItemService;
+import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+
+/**
+ * 优惠券活动项目业务操作实现
+ *
+ * @author qizai
+ * @date 2023/11/12
+ */
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
+@DubboService(interfaceClass = com.chelvc.cloud.vehicle.api.service.CouponActivityItemService.class)
+public class CouponActivityItemServiceImpl extends ServiceImpl<CouponActivityItemMapper, CouponActivityItem> implements CouponActivityItemService,
+        com.chelvc.cloud.vehicle.api.service.CouponActivityItemService {
+
+    @Override
+    public List<CouponActivityItem> listCouponActivityItems(Long activityId) {
+        return this.lambdaQuery().eq(CouponActivityItem::getActivityId, activityId).list();
+    }
+
+    @Override
+    public void removeByCouponId(List<Long> couponIds) {
+        this.remove(new LambdaQueryWrapper<CouponActivityItem>()
+                .in(CouponActivityItem::getCouponId, couponIds));
+    }
+}

+ 278 - 2
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/CouponActivityServiceImpl.java

@@ -1,23 +1,48 @@
 package com.chelvc.cloud.vehicle.server.service.impl;
 
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chelvc.cloud.uc.api.service.UserService;
+import com.chelvc.cloud.vehicle.api.constant.CouponActivitySendType;
+import com.chelvc.cloud.vehicle.api.constant.CouponActivityType;
+import com.chelvc.cloud.vehicle.api.constant.PromotionScopeType;
+import com.chelvc.cloud.vehicle.api.constant.PromotionStatus;
+import com.chelvc.cloud.vehicle.api.constant.PromotionType;
+import com.chelvc.cloud.vehicle.api.constant.UserCouponStatus;
 import com.chelvc.cloud.vehicle.api.dto.CouponActivityDTO;
 import com.chelvc.cloud.vehicle.api.param.CouponActivityModifyParam;
 import com.chelvc.cloud.vehicle.api.param.CouponActivityPagingParam;
+import com.chelvc.cloud.vehicle.api.util.PromotionUtils;
 import com.chelvc.cloud.vehicle.server.copier.CouponActivityCopier;
 import com.chelvc.cloud.vehicle.server.dao.CouponActivityMapper;
+import com.chelvc.cloud.vehicle.server.entity.Coupon;
 import com.chelvc.cloud.vehicle.server.entity.CouponActivity;
+import com.chelvc.cloud.vehicle.server.entity.CouponActivityItem;
+import com.chelvc.cloud.vehicle.server.entity.User;
+import com.chelvc.cloud.vehicle.server.entity.UserCoupon;
+import com.chelvc.cloud.vehicle.server.service.CouponActivityItemService;
 import com.chelvc.cloud.vehicle.server.service.CouponActivityService;
+import com.chelvc.cloud.vehicle.server.service.CouponService;
+import com.chelvc.cloud.vehicle.server.service.UserCouponService;
+import com.chelvc.framework.base.exception.ResourceUnavailableException;
 import com.chelvc.framework.base.util.ResourceUtils;
 import com.chelvc.framework.common.model.Pagination;
 import com.chelvc.framework.database.util.PagingUtils;
+import com.chelvc.framework.redis.cache.Cache;
+import com.chelvc.framework.redis.cache.CachePrefix;
 import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
 import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
@@ -27,10 +52,19 @@ import java.util.stream.Collectors;
  * @author qizai
  * @date 2023/11/10
  */
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
 @DubboService(interfaceClass = com.chelvc.cloud.vehicle.api.service.CouponActivityService.class)
-public class CouponActivityServiceImpl extends ServiceImpl<CouponActivityMapper, CouponActivity> implements CouponActivityService,
+public class CouponActivityServiceImpl extends AbstractPromotionServiceImpl<CouponActivityMapper, CouponActivity> implements CouponActivityService,
         com.chelvc.cloud.vehicle.api.service.CouponActivityService {
 
+    private final UserService userService;
+    private final CouponService couponService;
+    private final UserCouponService userCouponService;
+    private final CouponActivityItemService couponActivityItemService;
+
+    @Autowired
+    private Cache<List<CouponActivity>> cache;
+
     @Override
     public Long addCouponActivity(@NonNull CouponActivityModifyParam param) {
         CouponActivity couponActivity = CouponActivityCopier.INSTANCE.copying(param);
@@ -77,6 +111,248 @@ public class CouponActivityServiceImpl extends ServiceImpl<CouponActivityMapper,
         return PagingUtils.convert(page, clients);
     }
 
+    @Override
+    public void initPromotion(CouponActivity promotion) {
+        super.initPromotion(promotion);
+        //如果有会员,则写入会员信息
+        if (promotion.getUsers() != null && !promotion.getUsers().isEmpty()) {
+            promotion.setActivityScopeInfo(JSONUtil.toJsonStr(promotion.getUsers()));
+        }
+    }
+
+    /**
+     * 检查优惠券活动参数
+     *
+     * @param couponActivity 优惠券活动实体
+     */
+    @Override
+    public void checkPromotion(CouponActivity couponActivity) {
+        super.checkPromotion(couponActivity);
+        //指定会员判定
+        if (couponActivity.getActivityScope().equals(CouponActivitySendType.DESIGNATED.name())
+                && couponActivity.getUsers().isEmpty()) {
+            throw new ResourceUnavailableException("指定精准发券则必须指定会员,会员不可以为空");
+        }
+        // 检查优惠券
+        this.checkCouponActivityItem(couponActivity.getCouponActivityItems());
+    }
+
+    /**
+     * 更新优惠券活动商品信息
+     *
+     * @param couponActivity 优惠券活动实体
+     * @return 是否更新成功
+     */
+    @Override
+    @Transactional(rollbackFor = {Exception.class})
+    public boolean updatePromotionsGoods(CouponActivity couponActivity) {
+        boolean result = super.updatePromotionsGoods(couponActivity);
+        if (!PromotionStatus.CLOSE.name().equals(couponActivity.getPromotionStatus())
+                && PromotionScopeType.PORTION_GOODS.name().equals(couponActivity.getScopeType())) {
+            //创建优惠券活动子列表
+            for (CouponActivityItem couponActivityItem : couponActivity.getCouponActivityItems()) {
+                couponActivityItem.setActivityId(couponActivity.getId());
+            }
+            // 更新优惠券活动项信息
+            result = this.couponActivityItemService.saveBatch(couponActivity.getCouponActivityItems());
+        }
+        return result;
+    }
+
+    /**
+     * 更新优惠券活动信息到商品索引
+     *
+     * @param couponActivity 促销实体
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateEsGoodsIndex(CouponActivity couponActivity) {
+        switch (couponActivity.getCouponActivityType()) {
+            // 精准发券 则立即发放
+            case SPECIFY:
+                this.specify(couponActivity);
+                break;
+            //其他活动则是缓存模块,根据缓存中的优惠券活动信息来确认发放优惠券策略
+            case INVITE_NEW:
+            case AUTO_COUPON:
+            case REGISTERED:
+                this.resetCache(couponActivity.getCouponActivityType());
+                break;
+        }
+
+    }
+
+    @Override
+    public void specify(CouponActivity couponActivity) {
+        //获取活动优惠券发送范围
+        List<Map<String, Object>> users = this.getUserList(couponActivity);
+
+        //如果指定会员发券,则当下直接进行发送,如果是全体会员发券,则变更为用户登录首页进行请求发券
+        //PS:即不主动发券,需要用户在活动时间内登录自动领取优惠券,类似美团、饿了么 的发放方式
+        if (couponActivity.getActivityScope().equals(CouponActivitySendType.DESIGNATED.name())) {
+            //会员拆成多个小组进行发送
+            List<List<Map<String, Object>>> userGroup = new ArrayList<>();
+
+            //循环分组
+            for (int i = 0; i < (users.size() / 100 + (users.size() % 100 == 0 ? 0 : 1)); i++) {
+                int endPoint = Math.min((100 + (i * 100)), users.size());
+                userGroup.add(users.subList((i * 100), endPoint));
+            }
+
+            //优惠券活动的优惠券列表
+            List<CouponActivityItem> couponActivityItems = this.couponActivityItemService
+                    .listCouponActivityItems(couponActivity.getId());
+            //发送优惠券
+            for (List<Map<String, Object>> memberList : userGroup) {
+                sendCoupon(memberList, couponActivityItems);
+            }
+        }
+    }
+
+    /**
+     * 重写缓存中的活动优惠券信息
+     */
+    private List<CouponActivity> resetCache(CouponActivityType couponActivityType) {
+        //查询出结果,缓存后返回
+        List<CouponActivity> couponActivities = this.lambdaQuery()
+                .gt(CouponActivity::getEndTime, new Date())
+                .eq(CouponActivity::getActivityScope, CouponActivitySendType.ALL)
+                .eq(CouponActivity::getCouponActivityType, couponActivityType)
+                .list();
+        List<CouponActivity> couponActivitys = new ArrayList<>();
+        for (CouponActivity couponActivity : couponActivities) {
+            couponActivity.setCouponActivityItems(
+                    this.couponActivityItemService.listCouponActivityItems(couponActivity.getId()));
+            couponActivitys.add(couponActivity);
+        }
+
+        cache.put(cacheKey(couponActivityType.name()), couponActivitys);
+        return couponActivitys;
+    }
+
+    /**
+     * 缓存key生成策略
+     *
+     * @param couponActivityType 优惠券活动类型
+     * @return 缓存key
+     */
+    private String cacheKey(String couponActivityType) {
+        return CachePrefix.CURRENT_COUPON_ACTIVITY.getPrefix() + couponActivityType;
+    }
+
+    /**
+     * 定向发送优惠券
+     *
+     * @param userList          用户列表
+     * @param couponActivityItems 优惠券列表
+     */
+    private void sendCoupon(List<Map<String, Object>> userList, List<CouponActivityItem> couponActivityItems) {
+        for (Map<String, Object> map : userList) {
+            User user = new User();
+            user.setId(Long.valueOf(map.get("id").toString()));
+            user.setNickname(map.get("nick_name").toString());
+            this.sendCoupon(user, couponActivityItems);
+        }
+    }
+
+    /**
+     * 给当前用户发送优惠券
+     * 1.循环优惠券列表
+     * 2.判断优惠券每个会员发送数量
+     * 3.记录优惠券发送数量
+     *
+     * @param user            发送目标用户
+     * @param couponActivityItems 优惠券列表
+     */
+    private List<UserCoupon> sendCoupon(User user, List<? extends CouponActivityItem> couponActivityItems) {
+        //最终优惠券列表
+        List<UserCoupon> finalCoupons = new ArrayList<>();
+
+        //循环优惠券赠送列表
+        for (CouponActivityItem couponActivityItem : couponActivityItems) {
+            //获取优惠券
+            Coupon coupon = this.couponService.getById(couponActivityItem.getCouponId());
+            //判断优惠券是否存在
+            if (coupon != null) {
+                //循环优惠券的领取数量
+                int activitySendNum = couponActivityItem.getNum();
+
+                List<UserCoupon> userCouponList = new ArrayList<>();
+                for (int i = 1; i <= activitySendNum; i++) {
+                    UserCoupon userCoupon = UserCoupon.builder()
+                            .userId(user.getId())
+                            .couponId(coupon.getId())
+                            .userCouponStatus(UserCouponStatus.CLAIMED)
+                            .platformFlag(PromotionUtils.PLATFORM_ID.equals(coupon.getMerchantId()))
+                            .build();
+                    userCouponList.add(userCoupon);
+                }
+
+                finalCoupons.addAll(userCouponList);
+                //批量添加优惠券
+                this.userCouponService.saveBatch(userCouponList);
+                //添加优惠券已领取数量
+                this.couponService.updateCouponReceivedNum(couponActivityItem.getCouponId(), userCouponList.size());
+            } else {
+                log.error("赠送优惠券失败,当前优惠券不存在:" + couponActivityItem.getCouponId());
+            }
+        }
+        if (finalCoupons.isEmpty()) {
+            return new ArrayList<>();
+        }
+        return finalCoupons;
+    }
+
+    /**
+     * 当前促销类型
+     *
+     * @return 当前促销类型
+     */
+    @Override
+    public PromotionType getPromotionType() {
+        return PromotionType.COUPON_ACTIVITY;
+    }
+
+    /**
+     * 获取优惠券的范围
+     * 此方法用于精准发券
+     *
+     * @param couponActivity 优惠券活动
+     * @return 获取优惠券的用戶列表
+     */
+    private List<Map<String, Object>> getUserList(CouponActivity couponActivity) {
+        //判断优惠券的发送范围,获取用戶列表
+        List<String> ids = new ArrayList<>();
+        if (JSONUtil.isTypeJSONArray(couponActivity.getActivityScopeInfo())) {
+            JSONArray array = JSONUtil.parseArray(couponActivity.getActivityScopeInfo());
+            ids = array.toList(Map.class).stream().map(i -> i.get("id").toString()).collect(Collectors.toList());
+        }
+        return this.userService.listFieldsByUserIds("id,nick_name", ids);
+    }
+
+    /**
+     * 检查优惠券
+     *
+     * @param couponActivityItems 优惠券列表
+     */
+    private void checkCouponActivityItem(List<CouponActivityItem> couponActivityItems) {
+        //优惠券数量判定
+        if (couponActivityItems.isEmpty()) {
+            throw new ResourceUnavailableException("优惠券活动必须指定优惠券,不能为空");
+        } else if (couponActivityItems.size() > 10) {
+            throw new ResourceUnavailableException("优惠券活动最多指定10个优惠券");
+        } else {
+            for (CouponActivityItem item : couponActivityItems) {
+                if (item.getNum() == null || item.getNum() <= 0) {
+                    throw new ResourceUnavailableException("赠券数量必须大于0");
+                }
+                if (item.getNum() > 2) {
+                    throw new ResourceUnavailableException("赠券数量最大为2");
+                }
+            }
+        }
+    }
+
     /**
      * 转换优惠券活动信息
      *

+ 8 - 9
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/GoodsServiceImpl.java

@@ -1,20 +1,17 @@
 package com.chelvc.cloud.vehicle.server.service.impl;
 
-import java.util.List;
-
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.chelvc.cloud.vehicle.api.constant.CategoryType;
 import com.chelvc.cloud.vehicle.api.constant.GoodsStatus;
-import com.chelvc.cloud.vehicle.api.constant.MerchantStatus;
-import com.chelvc.cloud.vehicle.api.dto.CouponDTO;
 import com.chelvc.cloud.vehicle.api.dto.GoodsDTO;
 import com.chelvc.cloud.vehicle.api.dto.GoodsDetailDTO;
-import com.chelvc.cloud.vehicle.api.param.*;
-import com.chelvc.cloud.vehicle.server.copier.CouponCopier;
+import com.chelvc.cloud.vehicle.api.param.CommentQueryParam;
+import com.chelvc.cloud.vehicle.api.param.GoodsModifyParam;
+import com.chelvc.cloud.vehicle.api.param.GoodsPagingParam;
+import com.chelvc.cloud.vehicle.api.param.GoodsQueryParam;
 import com.chelvc.cloud.vehicle.server.copier.GoodsCopier;
 import com.chelvc.cloud.vehicle.server.dao.GoodsMapper;
-import com.chelvc.cloud.vehicle.server.entity.Coupon;
 import com.chelvc.cloud.vehicle.server.entity.Goods;
 import com.chelvc.cloud.vehicle.server.service.CommentService;
 import com.chelvc.cloud.vehicle.server.service.CouponService;
@@ -23,7 +20,6 @@ import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.util.ResourceUtils;
 import com.chelvc.framework.common.model.Pagination;
 import com.chelvc.framework.common.util.StringUtils;
-import com.chelvc.framework.database.context.DatabaseContextHolder;
 import com.chelvc.framework.database.util.PagingUtils;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -31,6 +27,8 @@ import org.apache.dubbo.config.annotation.DubboService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.CollectionUtils;
 
+import java.util.List;
+
 /**
  * 商品业务操作实现
  *
@@ -59,7 +57,8 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
 
         // 获取商品信息
         Goods goods = ResourceUtils.required(this.getById(id), "商品不存在");
-        detail.setGoods(GoodsCopier.INSTANCE.copying(goods));
+        GoodsDTO goodsDTO = GoodsCopier.INSTANCE.copying(goods);
+        detail.setGoods(goodsDTO);
 
         // 获取用户可用优惠券
         if (!CollectionUtils.isEmpty(goods.getCouponIds())) {

+ 2 - 2
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/UserCouponServiceImpl.java

@@ -72,7 +72,7 @@ public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCou
     @Override
     public List<UserCouponDTO> listUserCanUseCoupons(@NonNull UserCouponQueryParam param) {
         List<String> merchantIds = new ArrayList<>(Arrays.asList(param.getMerchantId().split(",")));
-        merchantIds.add(PromotionUtils.PLATFORM_ID);
+        merchantIds.add(PromotionUtils.PLATFORM_ID.toString());
         param.setMerchantIds(merchantIds);
         Long userId = SessionContextHolder.getId();
         // 当前用户对于当前商品可使用的优惠券列表
@@ -111,7 +111,7 @@ public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCou
         Page<UserCoupon> page = this.lambdaQuery()
                 .eq(Objects.nonNull(param.getType()), UserCoupon::getType, param.getType().name())
                 .eq(UserCouponStatus.USED.equals(param.getStatus()), UserCoupon::getUsed, 1)
-                .eq(UserCouponStatus.UNUSED.equals(param.getStatus()), UserCoupon::getUsed, 0)
+                .eq(UserCouponStatus.CLAIMED.equals(param.getStatus()), UserCoupon::getUsed, 0)
                 .orderByDesc(UserCoupon::getId).page(PagingUtils.convert(param.getPaging()));
         List<UserCoupon> records = page.getRecords();
         if (CollectionUtils.isEmpty(records)) {

+ 5 - 0
vehicle-server/src/main/resources/mapper/CouponActivityItemMapper.xml

@@ -0,0 +1,5 @@
+<?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="com.chelvc.cloud.vehicle.server.dao.CouponActivityItemMapper">
+
+</mapper>