Ver Fonte

Merge branch 'coupon'

# Conflicts:
#	netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
#	netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java
#	netflix-web/src/main/java/com/cyksj/web/controller/manage/statistics/StatisticsDataController.java
zoujiajian há 3 anos atrás
pai
commit
f8ab0546bc
57 ficheiros alterados com 3427 adições e 14 exclusões
  1. 3 0
      netflix-dao/src/main/java/com/cyksj/mapper/AccountMapper.java
  2. 9 0
      netflix-dao/src/main/java/com/cyksj/mapper/GoodsDonMapper.java
  3. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/GroupsRelationMapper.java
  4. 20 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponActiveMapper.java
  5. 16 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponDistributePopularizeMapper.java
  6. 22 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponMapper.java
  7. 27 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponRecommendMapper.java
  8. 26 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponSkuMapper.java
  9. 25 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponUserMapper.java
  10. 3 0
      netflix-dao/src/main/java/com/cyksj/model/dto/AuthRedirect.java
  11. 55 0
      netflix-dao/src/main/java/com/cyksj/model/dto/CouponPopularizeDto.java
  12. 147 0
      netflix-dao/src/main/java/com/cyksj/model/entity/Coupon.java
  13. 42 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CouponActive.java
  14. 30 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CouponDistributePopularize.java
  15. 25 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CouponRecommend.java
  16. 24 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CouponSku.java
  17. 85 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CouponUser.java
  18. 15 0
      netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java
  19. 84 0
      netflix-dao/src/main/java/com/cyksj/model/excel/ExcelAccountData.java
  20. 9 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonView.java
  21. 29 0
      netflix-dao/src/main/java/com/cyksj/model/request/CorpCouponSendReq.java
  22. 25 0
      netflix-dao/src/main/java/com/cyksj/model/request/CouponActiveReq.java
  23. 34 0
      netflix-dao/src/main/java/com/cyksj/model/request/CouponCollectReq.java
  24. 28 0
      netflix-dao/src/main/java/com/cyksj/model/request/CouponDistributeReq.java
  25. 10 0
      netflix-dao/src/main/java/com/cyksj/model/request/OrderPayRequest.java
  26. 170 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponActiveView.java
  27. 35 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponAvailableRecommendView.java
  28. 38 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponDistributePopularizeView.java
  29. 25 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponNewUserView.java
  30. 147 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponRecommendView.java
  31. 138 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponUserEnableView.java
  32. 165 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponUserView.java
  33. 107 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponView.java
  34. 37 0
      netflix-dao/src/main/java/com/cyksj/model/views/GoodsDonSkuView.java
  35. 53 0
      netflix-dao/src/main/resources/mapper/AccountMapper.xml
  36. 27 0
      netflix-dao/src/main/resources/mapper/CouponMapper.xml
  37. 54 0
      netflix-dao/src/main/resources/mapper/CouponRecommendMapper.xml
  38. 160 0
      netflix-dao/src/main/resources/mapper/CouponUserMapper.xml
  39. 4 0
      netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml
  40. 1 0
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  41. 47 0
      netflix-service/src/main/java/com/cyksj/service/coupon/CouponFontService.java
  42. 249 0
      netflix-service/src/main/java/com/cyksj/service/coupon/impl/CouponFontServiceImpl.java
  43. 3 0
      netflix-service/src/main/java/com/cyksj/service/mange/CmsAccountService.java
  44. 6 0
      netflix-service/src/main/java/com/cyksj/service/mange/account/CmsAccountServiceImpl.java
  45. 62 0
      netflix-service/src/main/java/com/cyksj/service/mange/coupon/CouponCommonService.java
  46. 287 0
      netflix-service/src/main/java/com/cyksj/service/mange/coupon/impl/CouponCommonServiceImpl.java
  47. 5 0
      netflix-service/src/main/java/com/cyksj/service/mange/statistics/StatisticsDataService.java
  48. 15 0
      netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataServiceImpl.java
  49. 2 2
      netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java
  50. 234 8
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  51. 2 2
      netflix-web/src/main/java/com/cyksj/web/aop/NoSubmitAop.java
  52. 230 0
      netflix-web/src/main/java/com/cyksj/web/controller/coupon/CouponFrontController.java
  53. 1 1
      netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java
  54. 8 1
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java
  55. 294 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/coupon/CouponController.java
  56. 9 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/CmsGoodsDonController.java
  57. 17 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/statistics/StatisticsDataController.java

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/mapper/AccountMapper.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.cyksj.model.entity.Account;
 import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.excel.ExcelAccountData;
 import com.cyksj.model.views.ExpiredAccountDataView;
 import org.apache.ibatis.annotations.Param;
 
@@ -22,4 +23,6 @@ public interface AccountMapper extends BaseMapper<Account> {
 	List<Account> getNoAppleOneAccount(String appleOne);
 
 	List<OrderDon> getNoAppleOneOrderDon(@Param("skuIds") List<Long> skuIds, @Param("yesZeroDate") Date yesZeroDate, @Param("thisZeroDate") Date thisZeroDate, @Param("noOrderStatus") List<String> noOrderStatus);
+
+	List<ExcelAccountData> getExpiredAccountViewNoPage(@Param("goodsId") Integer goodsId, @Param("account") String account, @Param("renewStatus") Boolean renewStatus, @Param("handleStatus") Boolean handleStatus, @Param("cnAccount") String cnAccount, @Param("usAccount") String usAccount, @Param("userId") Integer userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("now") String now);
 }

+ 9 - 0
netflix-dao/src/main/java/com/cyksj/mapper/GoodsDonMapper.java

@@ -2,10 +2,19 @@ package com.cyksj.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.GoodsDon;
+import org.apache.ibatis.annotations.Select;
+
+import java.math.BigDecimal;
 
 /**
  * @author chan
  * @date 2022/9/27 5:31 PM
  */
 public interface GoodsDonMapper extends BaseMapper<GoodsDon> {
+
+	@Select("select gdk.price from goods_don gd left join goods_don_sku gdk on gdk.goods_id = gd.id" +
+			" where gd.id = #{goodsId} and gd.deleted is true" +
+			" and gd.status is true and gd.type = 2" +
+			" order by gdk.price limit 1")
+	BigDecimal selectMinPriceSkuByGoodsId(Long goodsId);
 }

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/mapper/GroupsRelationMapper.java

@@ -16,4 +16,6 @@ public interface GroupsRelationMapper extends BaseMapper<GroupsRelation> {
     List<Long> getAccountIdsByUserIdOrNickNameOrSubmitAccount(@Param("userId") Long userId, @Param("nickName") String nickName, @Param("submitAccount") String submitAccount);
 
     Integer updateExpiryTime(@Param("entity") GroupsRelation relation, @Param("expireTime") Date expireTime);
+
+	Long getGroupTripsSkuIdByRelationId(Long relationId);
 }

+ 20 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponActiveMapper.java

@@ -0,0 +1,20 @@
+package com.cyksj.mapper.manage.coupon;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CouponActive;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
+
+/*
+ *项目名: netflix
+ *文件名: CouponActiveMapper
+ *创建者: JavaZou
+ *创建时间:2022/11/17 17:33
+ */
+public interface CouponActiveMapper extends BaseMapper<CouponActive> {
+	@Update("update coupon_active set deleted = false where coupon_id = #{id}")
+	void deleteByCouponId(Long id);
+
+	@Update("update coupon_active set remain_num = remain_num - 1 where id = #{id} and  remain_num = #{remainNum} and remain_num > 0")
+	Integer subRemainNum(@Param("id") Long id, @Param("remainNum") Integer remainNum);
+}

+ 16 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponDistributePopularizeMapper.java

@@ -0,0 +1,16 @@
+package com.cyksj.mapper.manage.coupon;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CouponDistributePopularize;
+import org.apache.ibatis.annotations.Update;
+
+/*
+ *项目名: netflix
+ *文件名: CouponDistributePopularizeMapper
+ *创建者: JavaZou
+ *创建时间:2022/11/17 18:07
+ */
+public interface CouponDistributePopularizeMapper extends BaseMapper<CouponDistributePopularize> {
+	@Update("update coupon_distribute_popularize set deleted = false where coupon_id = #{id}")
+	void deleteByCouponId(Long id);
+}

+ 22 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponMapper.java

@@ -0,0 +1,22 @@
+package com.cyksj.mapper.manage.coupon;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.dto.CouponPopularizeDto;
+import com.cyksj.model.entity.Coupon;
+import org.apache.ibatis.annotations.Select;
+
+/*
+ *项目名: netflix
+ *文件名: CouponMapper
+ *创建者: JavaZou
+ *创建时间:2022/11/17 17:23
+ */
+public interface CouponMapper extends BaseMapper<Coupon> {
+	CouponPopularizeDto checkExCode(String couponExCode);
+
+	@Select("select * from `coupon` where id = #{couponId}")
+	Coupon getCouponById(Long couponId);
+
+	@Select("select c.name from coupon c inner join coupon_user cu on cu.coupon_id = c.id where cu.id = #{couponUserId} limit 1")
+	String getCouponNameByCouponUserId(Long couponUserId);
+}

+ 27 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponRecommendMapper.java

@@ -0,0 +1,27 @@
+package com.cyksj.mapper.manage.coupon;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.model.entity.CouponRecommend;
+import com.cyksj.model.views.CouponAvailableRecommendView;
+import com.cyksj.model.views.CouponRecommendView;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CouponRecommendMapper
+ *创建者: JavaZou
+ *创建时间:2022/11/17 18:04
+ */
+public interface CouponRecommendMapper extends BaseMapper<CouponRecommend> {
+	@Update("update coupon_recommend set deleted = false where coupon_id = #{id}")
+	void deleteByCouponId(Long id);
+
+	List<CouponAvailableRecommendView> getRecommendCouponList(@Param("userId") Long userId, @Param("isNewUser") Boolean isNewUser);
+
+	Page<CouponRecommendView> getRecommendCouponViewList(@Param("userId") Long userId, @Param("isNewUser") Boolean isNewUser, @Param("page") IPage<CouponRecommendView> page);
+}

+ 26 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponSkuMapper.java

@@ -0,0 +1,26 @@
+package com.cyksj.mapper.manage.coupon;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CouponSku;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+/*
+ *项目名: netflix
+ *文件名: CouponSkuMapper
+ *创建者: JavaZou
+ *创建时间:2022/11/21 12:10
+ */
+public interface CouponSkuMapper extends BaseMapper<CouponSku> {
+
+	@Update("update coupon_sku set deleted = false where coupon_id = #{id}")
+	void deleteByCid(Long id);
+
+	@Select("select GROUP_CONCAT(gd.title, ifnull(gdk.spec_val,'')) from `coupon_sku` ck left join `goods_don` gd on gd.id = ck.goods_id" +
+			" left join `goods_don_sku` gdk on gdk.id = ck.sku_id where ck.coupon_id = #{couponId}")
+	String selectGoodsSkuStr(Long couponId);
+
+	@Delete("delete from coupon_sku where coupon_id = #{couponId}")
+	void deleteRelationSku(Long couponId);
+}

+ 25 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponUserMapper.java

@@ -0,0 +1,25 @@
+package com.cyksj.mapper.manage.coupon;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.model.entity.CouponUser;
+import com.cyksj.model.views.CouponActiveView;
+import com.cyksj.model.views.CouponUserView;
+import org.apache.ibatis.annotations.Param;
+
+/*
+ *项目名: netflix
+ *文件名: CouponUserMapper
+ *创建者: JavaZou
+ *创建时间:2022/11/18 11:43
+ */
+public interface CouponUserMapper extends BaseMapper<CouponUser> {
+	Page<CouponUserView> getAvailableCouponList(@Param("skuId") Long skuId, @Param("couponId") Long couponId, @Param("goodsId") Long goodsId, @Param("userId") Long userId, @Param("isRenew") Boolean isRenew, @Param("page") IPage<CouponUserView> page);
+
+	Integer countAvailableCouponList(@Param("goodsId") Long goodsId, @Param("skuId") Long skuId, @Param("userId") Long userId, @Param("isRenew") Boolean isRenew);
+
+	Page<CouponActiveView> couponCenterList(@Param("userId") Long userId, IPage<CouponUserView> page);
+
+	Page<CouponUserView> couponPersonalList(@Param("userId") Long userId, @Param("page") Page<CouponUserView> page);
+}

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/AuthRedirect.java

@@ -16,6 +16,9 @@ public class AuthRedirect {
 
     private String authType;
 
+    /**
+     * 分销用户id
+     */
     private Long sharedId;
 
     /**

+ 55 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/CouponPopularizeDto.java

@@ -0,0 +1,55 @@
+package com.cyksj.model.dto;
+
+import com.cyksj.model.entity.Coupon;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CouponPopularizeDto
+ *创建者: JavaZou
+ *创建时间:2022/11/21 15:03
+ */
+@Getter
+@Setter
+public class CouponPopularizeDto {
+	/**
+	 * 优惠券id
+	 */
+	private Long couponId;
+
+	private String goodsSkuIds;
+
+	private Coupon.Type type;
+
+	private Boolean isRenew;
+
+	private Coupon.Scope scope;
+
+	/**
+	 * 优惠券推广者关联id
+	 */
+	private Long popularizeId;
+
+	/**
+	 * 优惠券推广者关联优惠券id
+	 */
+	private Long popularizeCouponId;
+
+	private Coupon.GoodsType goodsType;
+
+	private BigDecimal reduceMoney;
+
+	private BigDecimal discount;
+
+	private Integer validDays;
+
+	private Boolean isValidTime;
+
+	private Date validStartTime;
+
+	private Date validEndTime;
+}

+ 147 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/Coupon.java

@@ -0,0 +1,147 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: Coupon
+ *创建者: JavaZou
+ *创建时间:2022/11/17 15:49
+ */
+@Getter
+@Setter
+public class Coupon extends BaseEntity {
+	/**
+	 * 优惠券名称
+	 */
+	@NotEmpty(message = "优惠券名称不能为空")
+	private String name;
+
+	/**
+	 * 优惠券平台类型
+	 */
+	@NotNull(message = "优惠券平台类型不能为空")
+	private GoodsType goodsType;
+
+	/**
+	 * 优惠券类型
+	 */
+	@NotNull(message = "优惠券类型不能为空")
+	private Type type;
+
+	/**
+	 * 抵扣金额
+	 */
+	private BigDecimal reduceMoney;
+
+	/**
+	 * 折扣
+	 */
+	private BigDecimal discount;
+
+	/**
+	 * 领取人范围
+	 */
+	@NotNull(message = "领取人不能为空")
+	private Scope scope;
+
+	/**
+	 * 平台规格集合
+	 */
+	@NotEmpty
+	private String goodsSkuIds;
+
+	/**
+	 * 是否是有效时间
+	 */
+	private Boolean isValidTime;
+
+	/**
+	 * 起始有效时间
+	 */
+	private Date validStartTime;
+
+	/**
+	 * 截止有效时间
+	 */
+	private Date validEndTime;
+
+	/**
+	 * 领取有效天数
+	 */
+	private Integer validDays;
+
+	/**
+	 * 是否可在续费时使用
+	 */
+	private Boolean isRenew;
+
+	/**
+	 * 兑换码
+	 */
+	private String exCode;
+
+	/**
+	 * 发放状态
+	 */
+	private Boolean sendStatus;
+
+	/**
+	 * 推荐状态
+	 */
+	private Boolean hotStatus;
+
+	@TableLogic(value = "1", delval = "0")
+	private Boolean deleted;
+
+	@Getter
+	public enum GoodsType {
+		realGoods("实物"),
+		virtualGoods("虚物"),
+		;
+		String type;
+
+		GoodsType(String type) {
+			this.type = type;
+		}
+	}
+
+	@Getter
+	public enum Type {
+		reduce("立减卷"),
+		discount("折扣卷"),
+		;
+		String desc;
+
+		Type(String desc) {
+			this.desc = desc;
+		}
+	}
+
+	@Getter
+	public enum Scope {
+		everyone("所有人"),
+		newUser("新用户"),
+		;
+		String desc;
+
+		Scope(String desc) {
+			this.desc = desc;
+		}
+	}
+
+	@Getter
+	@Setter
+	public static class CouponGoodsSku {
+		private Long goodsId;
+
+		private Long skuId;
+	}
+}

+ 42 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CouponActive.java

@@ -0,0 +1,42 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CouponActive
+ *创建者: JavaZou
+ *创建时间:2022/11/17 17:30
+ */
+@Getter
+@Setter
+public class CouponActive extends BaseEntity{
+	/**
+	 * 优惠券id
+	 */
+	private Long couponId;
+	/**
+	 * 发放数量
+	 */
+	private Integer num;
+
+	/**
+	 * 剩余数量
+	 */
+	private Integer remainNum;
+
+	/**
+	 * 状态(默认启用)
+	 */
+	private Boolean status;
+
+	/**
+	 * 有效状态(默认有效)
+	 */
+	private Boolean expiryStatus;
+
+	@TableLogic(value = "1", delval = "0")
+	private Boolean deleted;
+}

+ 30 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CouponDistributePopularize.java

@@ -0,0 +1,30 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CouponDistributePopularize
+ *创建者: JavaZou
+ *创建时间:2022/11/17 18:07
+ */
+@Getter
+@Setter
+public class CouponDistributePopularize extends BaseEntity {
+	private Long couponId;
+
+	/**
+	 * 固定推广者id
+	 */
+	private Long distributeId;
+
+	/**
+	 * 兑换码
+	 */
+	private String exCode;
+
+	@TableLogic(value = "1", delval = "0")
+	private Boolean deleted;
+}

+ 25 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CouponRecommend.java

@@ -0,0 +1,25 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CouponRecommend
+ *创建者: JavaZou
+ *创建时间:2022/11/17 18:01
+ */
+@Getter
+@Setter
+public class CouponRecommend extends BaseEntity {
+	private Long couponId;
+
+	/**
+	 * 默认推荐
+	 */
+	private Boolean status;
+
+	@TableLogic(value = "1", delval = "0")
+	private Boolean deleted;
+}

+ 24 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CouponSku.java

@@ -0,0 +1,24 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CouponSku
+ *创建者: JavaZou
+ *创建时间:2022/11/21 12:07
+ */
+@Getter
+@Setter
+public class CouponSku extends BaseEntity{
+	private Long couponId;
+
+	private Long goodsId;
+
+	private Long skuId;
+
+	@TableLogic(value = "1", delval = "0")
+	private Boolean deleted;
+}

+ 85 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CouponUser.java

@@ -0,0 +1,85 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CouponUser
+ *创建者: JavaZou
+ *创建时间:2022/11/18 11:43
+ */
+@Getter
+@Setter
+public class CouponUser extends BaseEntity {
+	private Long userId;
+
+	/**
+	 * 优惠券id
+	 */
+	private Long couponId;
+
+	/**
+	 * 发放id
+	 */
+	private Long couponActiveId;
+
+	/**
+	 * 推荐id
+	 */
+	private Long recommendId;
+
+	/**
+	 * 关联推广id
+	 */
+	private Long popularizeId;
+
+	/**
+	 * 获取渠道
+	 */
+	private Channel channel;
+
+	/**
+	 * 优惠券使用状态
+	 */
+	private Status status;
+
+	private Date validStartTime;
+
+	private Date validEndTime;
+
+	@Getter
+	public enum Channel {
+		recommend("recommend", "推荐渠道"),
+		center("unused", "领卷中心"),
+		exCode("exCode", "兑换码"),
+		wxCorp("wxCorp", "企业微信"),
+		;
+		private String channel;
+		private String desc;
+
+		Channel(String channel, String desc) {
+			this.channel = channel;
+			this.desc = desc;
+		}
+	}
+
+	@Getter
+	public enum Status {
+		used("used", "已使用"),
+		unused("unused", "未使用"),
+		expired("expired", "已过期"),
+		invalid("invalid", "已失效"),
+		notEffective("notEffective", "未生效"),
+		;
+		private String status;
+		private String desc;
+
+		Status(String status, String desc) {
+			this.status = status;
+			this.desc = desc;
+		}
+	}
+}

+ 15 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java

@@ -52,6 +52,21 @@ public class OrderDon extends BaseEntity implements Serializable {
      */
     private BigDecimal refundMoney;
 
+    /**
+     * 优惠金额
+     */
+    private BigDecimal realMoney;
+
+    /**
+     * 优惠金额
+     */
+    private BigDecimal couponMoney;
+
+    /**
+     * 优惠券用户领取id
+     */
+    private Long couponUserId;
+
     /**
      * 购买数量
      */

+ 84 - 0
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelAccountData.java

@@ -0,0 +1,84 @@
+package com.cyksj.model.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ExcelAccountData
+ *创建者: JavaZou
+ *创建时间:2022/12/6 14:10
+ */
+@Getter
+@Setter
+public class ExcelAccountData {
+
+	/**
+	 * 所属平台
+	 */
+	@ExcelProperty("所属平台")
+	private String platform;
+
+	/**
+	 * 主账号
+	 */
+	@ExcelProperty("账号")
+	private String account;
+
+	/**
+	 * 主账号密码
+	 */
+	@ExcelProperty("密码")
+	private String password;
+
+	/**
+	 * 类型
+	 */
+	@ExcelProperty("规格")
+	private String type;
+
+	/**
+	 * 用户id
+	 */
+	@ExcelProperty("用户ID")
+	private Integer userId;
+
+	/**
+	 * 用户头像
+	 */
+	@ExcelProperty("用户头像")
+	private String img;
+
+	/**
+	 * 用户昵称
+	 */
+	@ExcelProperty("用户昵称")
+	private String nickName;
+
+	/**
+	 * 购买时间
+	 */
+	@ExcelProperty("购买时间")
+	private Date payTime;
+
+	/**
+	 * 过期时间
+	 */
+	@ExcelProperty("过期时间")
+	private Date expiryTime;
+
+	/**
+	 * 用户续费意愿
+	 */
+	@ExcelProperty("用户续费意愿")
+	private Boolean renewStatus;
+
+	/**
+	 * 是否处理过
+	 */
+	@ExcelProperty("是否处理过")
+	private Boolean isHandle;
+}

+ 9 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonView.java

@@ -76,6 +76,15 @@ public class OrderDonView {
     @DbField("o.created_time")
     private Date createdTime;
 
+    @DbField("o.coupon_user_id")
+    private Long couponUserId;
+
+    @DbIgnore
+    private String couponName;
+
+    @DbField("o.coupon_money")
+    private BigDecimal couponMoney;
+
     @DbIgnore
     private OrderDonWaybill waybill;
 

+ 29 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CorpCouponSendReq.java

@@ -0,0 +1,29 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/*
+ *项目名: netflix
+ *文件名: CorpCouponSendReq
+ *创建者: JavaZou
+ *创建时间:2022/11/28 10:26
+ */
+@Getter
+@Setter
+public class CorpCouponSendReq {
+	/**
+	 * 外部联系人id
+	 */
+	@NotEmpty
+	private String externalUserId;
+
+	/**
+	 * 优惠券id
+	 */
+	@NotNull
+	private Long couponId;
+}

+ 25 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CouponActiveReq.java

@@ -0,0 +1,25 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+/*
+ *项目名: netflix
+ *文件名: CouponActiveReq
+ *创建者: JavaZou
+ *创建时间:2022/11/17 17:37
+ */
+@Getter
+@Setter
+public class CouponActiveReq {
+	@NotNull(message = "优惠券id不能为空")
+	@Min(value = 1)
+	private Long couponId;
+
+	@NotNull(message = "发放数量不能为空")
+	@Min(value = 1)
+	private Integer num;
+}

+ 34 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CouponCollectReq.java

@@ -0,0 +1,34 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+/*
+ *项目名: netflix
+ *文件名: CouponCollectReq
+ *创建者: JavaZou
+ *创建时间:2022/11/18 12:29
+ */
+@Getter
+@Setter
+public class CouponCollectReq {
+	/**
+	 * 优惠券id
+	 */
+	@NotNull(message = "优惠券id不能为空")
+	@Min(value = 1)
+	private Long couponId;
+
+	/**
+	 * 优惠券发放id
+	 */
+	private Long couponActiveId;
+
+	/**
+	 * 优惠券推荐id
+	 */
+	private Long couponRecommendId;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CouponDistributeReq.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+/*
+ *项目名: netflix
+ *文件名: CouponDistributeReq
+ *创建者: JavaZou
+ *创建时间:2022/11/17 18:16
+ */
+@Getter
+@Setter
+public class CouponDistributeReq {
+	@NotNull(message = "固定推广者id不能为空")
+	@Min(value = 1)
+	private Long couponId;
+
+	/**
+	 * 固定推广者id
+	 */
+	@NotNull(message = "固定推广者id不能为空")
+	@Min(value = 1)
+	private Long distributeId;
+}

+ 10 - 0
netflix-dao/src/main/java/com/cyksj/model/request/OrderPayRequest.java

@@ -56,4 +56,14 @@ public class OrderPayRequest {
     private Long addressId;
 
     private String type;
+
+    /**
+     * 优惠券id
+     */
+    private Long couponId;
+
+    /**
+     * 优惠券兑换码
+     */
+    private String couponExCode;
 }

+ 170 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponActiveView.java

@@ -0,0 +1,170 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.Coupon;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CouponActiveView
+ *创建者: JavaZou
+ *创建时间:2022/11/18 10:00
+ */
+@Getter
+@Setter
+@SearchBean(tables = "coupon_active ca left join coupon c on c.id = ca.coupon_id",
+		where = "ca.deleted is true")
+public class CouponActiveView {
+	@DbField("ca.id")
+	private Long id;
+
+	/**
+	 * 优惠券id
+	 */
+	@DbField("c.id")
+	private Long couponId;
+
+	/**
+	 * 优惠券名称
+	 */
+	@DbField("c.name")
+	private String name;
+
+	/**
+	 * 优惠券平台类型
+	 */
+	@DbField("c.goods_type")
+	private Coupon.GoodsType goodsType;
+
+	/**
+	 * 优惠券类型
+	 */
+	@DbField("c.type")
+	private Coupon.Type type;
+
+	/**
+	 * 抵扣金额
+	 */
+	@DbField("c.reduce_money")
+	private BigDecimal reduceMoney;
+
+	/**
+	 * 折扣
+	 */
+	@DbField("c.discount")
+	private BigDecimal discount;
+
+	/**
+	 * 领取人范围
+	 */
+	@DbField("c.scope")
+	private Coupon.Scope scope;
+
+	/**
+	 * 平台规格集合
+	 */
+	@DbField("c.goods_sku_ids")
+	private String goodsSkuIds;
+
+	@DbIgnore
+	private String goodsSkuStr;
+
+	/**
+	 * 是否是有效时间
+	 */
+	@DbField("c.is_valid_time")
+	private Boolean isValidTime;
+
+	/**
+	 * 起始有效时间
+	 */
+	@DbField("c.valid_start_time")
+	private Date validStartTime;
+
+	/**
+	 * 截止有效时间
+	 */
+	@DbField("c.valid_end_time")
+	private Date validEndTime;
+
+	/**
+	 * 领取有效天数
+	 */
+	@DbField("c.valid_days")
+	private Integer validDays;
+
+	/**
+	 * 是否可在续费时使用
+	 */
+	@DbField("c.is_renew")
+	private Boolean isRenew;
+
+	/**
+	 * 发放数量
+	 */
+	@DbField("ca.num")
+	private Integer num;
+
+	/**
+	 * 状态(默认启用)
+	 */
+	@DbField("ca.status")
+	private Boolean status;
+
+	/**
+	 * 有效状态(默认有效)
+	 */
+	@DbField("ca.expiry_status")
+	private Boolean expiryStatus;
+
+	/**
+	 * 剩余数量
+	 */
+	@DbField("ca.remain_num")
+	private Integer remainNum;
+
+	/**
+	 * 领取人数
+	 */
+	@DbIgnore
+	private Integer receiptNum;
+
+	/**
+	 * 使用人数
+	 */
+	@DbIgnore
+	private Integer usedNum;
+
+	@DbIgnore
+	private CouponStatus couponStatus;
+
+	@DbIgnore
+	@JsonIgnore
+	private Integer order;
+
+	@DbField("ca.created_time")
+	private Date createdTime;
+
+	@Getter
+	public enum CouponStatus {
+		collecting("collecting", "待领取"),
+		received("received", "已领取"),
+		over("over", "已领完"),
+		stop("stop", "停止领取"),
+		;
+		private String status;
+		private String desc;
+
+		CouponStatus(String status, String desc) {
+			this.status = status;
+			this.desc = desc;
+		}
+	}
+}

+ 35 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponAvailableRecommendView.java

@@ -0,0 +1,35 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.Coupon;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CouponAvailableRecommenView
+ *创建者: JavaZou
+ *创建时间:2022/11/18 15:19
+ */
+@Setter
+@Getter
+@SearchBean(tables = "coupon_recommend cr left join coupon c on c.id = cr.coupon_id" +
+		" left join coupon_user cu on cu.coupon_id = c.id",
+		where = "cr.deleted is true and c.deleted is true")
+public class CouponAvailableRecommendView {
+	@DbField("cr.id")
+	private Long recommendId;
+
+	@DbField("c.id")
+	private Long couponId;
+
+	@DbField("c.scope")
+	private Coupon.Scope scope;
+
+	@DbField("cu.coupon_id")
+	private Long userCouponId;
+
+	@DbField("cu.user_id")
+	private Long userId;
+}

+ 38 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponDistributePopularizeView.java

@@ -0,0 +1,38 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CouponDistributePopularizeView
+ *创建者: JavaZou
+ *创建时间:2022/11/18 10:37
+ */
+@Getter
+@Setter
+@SearchBean(tables = "coupon_distribute_popularize cp left join user_distribute ub on ub.id = cp.distribute_id" +
+		" left join user u on u.id = ub.user_id",
+		where = "cp.deleted is true")
+public class CouponDistributePopularizeView {
+	@DbField("cp.id")
+	private Long id;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("u.headimgurl")
+	private String headImgUrl;
+
+	@DbField("u.id")
+	private Long userId;
+
+	@DbField("cp.ex_code")
+	private String exCode;
+
+	@DbIgnore
+	private Integer usedNum;
+}

+ 25 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponNewUserView.java

@@ -0,0 +1,25 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CouponNewUserView
+ *创建者: JavaZou
+ *创建时间:2022/11/18 13:44
+ */
+@Getter
+@Setter
+@SearchBean(tables = "order_don od left join goods_don_sku gdk on gdk.id = od.sku_id" +
+		" left join goods_don gd on gd.id = gdk.goods_id",
+		where = "gd.type = 1 and od.status not in ('close','noPayment')")
+public class CouponNewUserView {
+	@DbField("od.id")
+	private Long orderId;
+
+	@DbField("od.user_id")
+	private Long userId;
+}

+ 147 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponRecommendView.java

@@ -0,0 +1,147 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.Coupon;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CouponRecommendView
+ *创建者: JavaZou
+ *创建时间:2022/11/18 10:07
+ */
+@Setter
+@Getter
+@SearchBean(tables = "coupon_recommend cr left join coupon c on c.id = cr.coupon_id",
+		where = "cr.deleted is true and c.deleted is true")
+public class CouponRecommendView {
+	@DbField("cr.id")
+	private Long id;
+
+	@DbField("cr.coupon_id")
+	private Long couponId;
+
+	/**
+	 * 优惠券名称
+	 */
+	@DbField("c.name")
+	private String name;
+
+	/**
+	 * 优惠券平台类型
+	 */
+	@DbField("c.goods_type")
+	private Coupon.GoodsType goodsType;
+
+	/**
+	 * 优惠券类型
+	 */
+	@DbField("c.type")
+	private Coupon.Type type;
+
+	/**
+	 * 抵扣金额
+	 */
+	@DbField("c.reduce_money")
+	private BigDecimal reduceMoney;
+
+	/**
+	 * 折扣
+	 */
+	@DbField("c.discount")
+	private BigDecimal discount;
+
+	/**
+	 * 平台规格集合
+	 */
+	@DbField("c.goods_sku_ids")
+	private String goodsSkuIds;
+
+	@DbIgnore
+	private String goodsSkuStr;
+
+	/**
+	 * 领取人范围
+	 */
+	@DbField("c.scope")
+	private Coupon.Scope scope;
+
+	/**
+	 * 是否是有效时间
+	 */
+	@DbField("c.is_valid_time")
+	private Boolean isValidTime;
+
+	/**
+	 * 起始有效时间
+	 */
+	@DbField("c.valid_start_time")
+	private Date validStartTime;
+
+	/**
+	 * 截止有效时间
+	 */
+	@DbField("c.valid_end_time")
+	private Date validEndTime;
+
+	/**
+	 * 用户领取后起始有效时间
+	 */
+	@DbField("c.valid_start_time")
+	@JsonIgnore
+	private Date userValidStartTime;
+
+	/**
+	 * 用户领取后截止有效时间
+	 */
+	@DbField("c.valid_end_time")
+	@JsonIgnore
+	private Date userValidEndTime;
+
+	/**
+	 * 领取有效天数
+	 */
+	@DbField("c.valid_days")
+	private Integer validDays;
+
+	/**
+	 * 是否可在续费时使用
+	 */
+	@DbField("c.is_renew")
+	private Boolean isRenew;
+
+	/**
+	 * 推荐状态
+	 */
+	@DbField("cr.status")
+	private Boolean status;
+
+	/**
+	 * 推荐时间
+	 */
+	@DbField("cr.created_time")
+	private Date createdTime;
+
+	/**
+	 * 推荐领取人数
+	 */
+	@DbIgnore
+	private Integer receiptNum;
+
+	/**
+	 * 使用人数
+	 */
+	@DbIgnore
+	private Integer usedNum;
+
+	@DbIgnore
+	private CouponActiveView.CouponStatus couponStatus;
+
+}

+ 138 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponUserEnableView.java

@@ -0,0 +1,138 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.Coupon;
+import com.cyksj.model.entity.CouponUser;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CouponUserEnableView
+ *创建者: JavaZou
+ *创建时间:2022/11/21 10:33
+ */
+@Getter
+@Setter
+@SearchBean(tables = "coupon_user cu left join coupon_active ca on ca.id = cu.coupon_active_id" +
+		" left join coupon c on c.id = cu.coupon_id",
+		where = "order by cu.id desc")
+public class CouponUserEnableView {
+	@DbField("cu.id")
+	private Long id;
+
+	/**
+	 * 获取渠道
+	 */
+	@DbField("cu.channel")
+	private CouponUser.Channel channel;
+
+	/**
+	 * 优惠券使用状态
+	 */
+	@DbField("cu.status")
+	private CouponUser.Status couponUserStatus;
+
+	/**
+	 * 用户id
+	 */
+	@DbField("cu.user_id")
+	private Long userId;
+
+	/**
+	 * 优惠券id
+	 */
+	@DbField("c.id")
+	private Long couponId;
+
+	/**
+	 * 优惠券发放id
+	 */
+	@DbField("ca.id")
+	private Long couponActiveId;
+
+	/**
+	 * 优惠券名称
+	 */
+	@DbField("ca.name")
+	private String name;
+
+	/**
+	 * 优惠券平台类型
+	 */
+	@DbField("ca.goods_type")
+	private Coupon.GoodsType goodsType;
+
+	/**
+	 * 优惠券类型
+	 */
+	@DbField("ca.type")
+	private Coupon.Type type;
+
+	/**
+	 * 抵扣金额
+	 */
+	@DbField("ca.reduce_money")
+	private BigDecimal reduceMoney;
+
+	/**
+	 * 折扣
+	 */
+	@DbField("ca.discount")
+	private BigDecimal discount;
+
+	/**
+	 * 平台规格集合
+	 */
+	@DbField("c.goods_sku_ids")
+	private String goodsSkuIds;
+
+	/**
+	 * 是否是有效时间
+	 */
+	@DbField("ca.is_valid_time")
+	private Boolean isValidTime;
+
+	/**
+	 * 起始有效时间
+	 */
+	@DbField("ca.valid_start_time")
+	private Date validStartTime;
+
+	/**
+	 * 领取有效天数
+	 */
+	@DbField("ca.valid_days")
+	private Integer validDays;
+
+	/**
+	 * 截止有效时间
+	 */
+	@DbField("ca.valid_end_time")
+	private Date validEndTime;
+
+	/**
+	 * 是否可在续费时使用
+	 */
+	@DbField("ca.is_renew")
+	private Boolean isRenew;
+
+	/**
+	 * 发放优惠券状态(默认启用)
+	 */
+	@DbField("ca.status")
+	private Boolean status;
+
+	/**
+	 * 有效状态(默认有效)
+	 */
+	@DbField("ca.expiry_status")
+	private Boolean expiryStatus;
+
+	@DbField("ca.created_time")
+	private Date createdTime;
+}

+ 165 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponUserView.java

@@ -0,0 +1,165 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.Coupon;
+import com.cyksj.model.entity.CouponUser;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CouponUserView
+ *创建者: JavaZou
+ *创建时间:2022/11/18 12:12
+ */
+@Getter
+@Setter
+@SearchBean(tables = "coupon_user cu left join coupon_active ca on ca.id = cu.coupon_active_id" +
+		" left join coupon c on c.id = cu.coupon_id")
+public class CouponUserView {
+	@DbField("cu.id")
+	private Long id;
+
+	/**
+	 * 获取渠道
+	 */
+	@DbField("cu.channel")
+	private CouponUser.Channel channel;
+
+	/**
+	 * 优惠券使用状态
+	 */
+	@DbField("cu.status")
+	private CouponUser.Status couponUserStatus;
+
+	/**
+	 * 用户id
+	 */
+	@DbField("cu.user_id")
+	private Long userId;
+
+	/**
+	 * 优惠券id
+	 */
+	@DbField("c.id")
+	private Long couponId;
+
+	/**
+	 * 优惠券发放id
+	 */
+	@DbField("ca.id")
+	private Long couponActiveId;
+
+	/**
+	 * 优惠券名称
+	 */
+	@DbField("c.name")
+	private String name;
+
+	/**
+	 * 优惠券平台类型
+	 */
+	@DbField("c.goods_type")
+	private Coupon.GoodsType goodsType;
+
+	/**
+	 * 优惠券类型
+	 */
+	@DbField("c.type")
+	private Coupon.Type type;
+
+	@DbField("c.scope")
+	private Coupon.Scope scope;
+
+	/**
+	 * 抵扣金额
+	 */
+	@DbField("c.reduce_money")
+	private BigDecimal reduceMoney;
+
+	/**
+	 * 折扣
+	 */
+	@DbField("c.discount")
+	private BigDecimal discount;
+
+	/**
+	 * 平台规格集合
+	 */
+	@DbField("c.goods_sku_ids")
+	private String goodsSkuIds;
+
+	@DbIgnore
+	private String goodsSkuStr;
+
+	/**
+	 * 是否是有效时间
+	 */
+	@DbField("c.is_valid_time")
+	private Boolean isValidTime;
+
+	/**
+	 * 起始有效时间
+	 */
+	@DbField("cu.valid_start_time")
+	private Date validStartTime;
+
+	/**
+	 * 领取有效天数
+	 */
+	@DbField("c.valid_days")
+	private Integer validDays;
+
+	/**
+	 * 截止有效时间
+	 */
+	@DbField("cu.valid_end_time")
+	private Date validEndTime;
+
+	/**
+	 * 是否可在续费时使用
+	 */
+	@DbField("c.is_renew")
+	private Boolean isRenew;
+
+	/**
+	 * 发放优惠券状态(默认启用)
+	 */
+	@DbField("ca.status")
+	private Boolean status;
+
+	/**
+	 * 有效状态(默认有效)
+	 */
+	@DbField("ca.expiry_status")
+	private Boolean expiryStatus;
+
+	@DbField("case when cu.status = 'unused' then 0" +
+			" when cu.status = 'expired' then 1" +
+			" when cu.status = 'invalid' then 2" +
+			" when cu.status = 'used' then 3" +
+			" end")
+	@JsonIgnore
+	private Integer orderBy;
+
+	/**
+	 * 0不可用/1可用
+	 */
+	@DbIgnore
+	private Boolean isAvailable;
+
+	/**
+	 * 不可用原因
+	 */
+	@DbIgnore
+	private String reason;
+
+	@DbField("cu.created_time")
+	private Date createdTime;
+}

+ 107 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CouponView.java

@@ -0,0 +1,107 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.Coupon;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CouponView
+ *创建者: JavaZou
+ *创建时间:2022/11/17 18:32
+ */
+@Getter
+@Setter
+@SearchBean(tables = "coupon c",
+		where = "c.deleted is true")
+public class CouponView {
+	private Long id;
+
+	/**
+	 * 优惠券名称
+	 */
+	private String name;
+
+	/**
+	 * 优惠券平台类型
+	 */
+	private Coupon.GoodsType goodsType;
+
+	/**
+	 * 优惠券类型
+	 */
+	private Coupon.Type type;
+
+	/**
+	 * 抵扣金额
+	 */
+	private BigDecimal reduceMoney;
+
+	/**
+	 * 折扣
+	 */
+	private BigDecimal discount;
+
+	/**
+	 * 领取人范围
+	 */
+	private Coupon.Scope scope;
+
+	/**
+	 * 平台规格集合
+	 */
+	private String goodsSkuIds;
+
+	/**
+	 * 是否是有效时间
+	 */
+	private Boolean isValidTime;
+
+	/**
+	 * 起始有效时间
+	 */
+	private Date validStartTime;
+
+	/**
+	 * 截止有效时间
+	 */
+	private Date validEndTime;
+
+	/**
+	 * 领取有效天数
+	 */
+	private Integer validDays;
+
+	/**
+	 * 是否可在续费时使用
+	 */
+	private Boolean isRenew;
+
+	/**
+	 * 兑换码
+	 */
+	private String exCode;
+
+	/**
+	 * 发放状态
+	 */
+	private Boolean sendStatus;
+
+	/**
+	 * 推荐状态
+	 */
+	private Boolean hotStatus;
+
+	@DbIgnore
+	private Integer relationNum;
+
+	@DbIgnore
+	private String goodsSkuStr;
+
+	private Date createdTime;
+}

+ 37 - 0
netflix-dao/src/main/java/com/cyksj/model/views/GoodsDonSkuView.java

@@ -0,0 +1,37 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/*
+ *项目名: netflix
+ *文件名: GoodsDonSkuView
+ *创建者: JavaZou
+ *创建时间:2022/11/28 15:05
+ */
+@Getter
+@Setter
+@SearchBean(tables = "goods_don gd left join goods_don_sku gdk on gdk.goods_id = gd.id")
+public class GoodsDonSkuView {
+	@DbField("gd.id")
+	private Long goodsId;
+
+	@DbField("gd.title")
+	private String goodsTitle;
+
+	@DbField("gd.type")
+	private Integer type;
+
+	@DbField("gdk.id")
+	private Long skuId;
+
+	@DbField("gdk.spec_val")
+	private String specVal;
+
+	@DbField("gdk.price")
+	private BigDecimal money;
+}

+ 53 - 0
netflix-dao/src/main/resources/mapper/AccountMapper.xml

@@ -75,4 +75,57 @@
         </foreach>
         and od.created_time between #{yesZeroDate} and #{thisZeroDate}
     </select>
+
+    <select id="getExpiredAccountViewNoPage" resultType="com.cyksj.model.excel.ExcelAccountData">
+        select gr.id as relationId,
+        a.title as platform,
+        a.account,
+        a.password,
+        a.us_account,
+        a.cn_account,
+        concat(gdk.spec_val,'¥',cast(gdk.price/100 as decimal(10,2))) as 'type',
+        gr.user_id,
+        gr.account as userAccount,
+        u.headimgurl as img,
+        u.nickname,
+        gr.start_time as payTime,
+        gr.expiry_time,
+        gr.renew_status,
+        gr.is_handle
+        from `account` a left join `groups_trips` gt on gt.account_id = a.id
+        left join `groups_relation` gr on gr.groups_id = gt.id
+        left join `goods_don_sku` gdk on gdk.id = gt.sku_id
+        left join `user` u on gr.user_id = u.id
+        <where>
+            <if test="goodsId != null">
+                a.goods_id = #{goodsId}
+            </if>
+            <if test="account != null and account != ''">
+                and a.account like '%${account}%'
+            </if>
+            <if test="renewStatus != null">
+                and gr.renew_status is ${renewStatus}
+            </if>
+            <if test="handleStatus != null">
+                and gr.is_handle is ${handleStatus}
+            </if>
+            <if test="cnAccount != null and cnAccount != ''">
+                and a.cn_account = #{cnAccount}
+            </if>
+            <if test="usAccount != null and usAccount != ''">
+                and a.us_account = #{usAccount}
+            </if>
+            <if test="userId != null">
+                and gr.user_id = #{userId}
+            </if>
+            <if test="startTime != null">
+                and gr.expiry_time >= #{startTime}
+            </if>
+            <if test="endTime != null">
+                and gr.expiry_time &lt; #{endTime}
+            </if>
+            and gr.expiry_time > #{now}
+            order by expiry_time
+        </where>
+    </select>
 </mapper>

+ 27 - 0
netflix-dao/src/main/resources/mapper/CouponMapper.xml

@@ -0,0 +1,27 @@
+<?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.cyksj.mapper.manage.coupon.CouponMapper">
+
+    <select id="checkExCode" resultType="com.cyksj.model.dto.CouponPopularizeDto">
+        select cp.id         as couponId,
+               cdp.id        as popularizeId,
+               cp.goods_sku_ids,
+               cp.type,
+               cp.is_renew,
+               cp.scope,
+               cdp.coupon_id as popularizeCouponId,
+               cp.goods_type,
+               cp.reduce_money,
+               cp.discount,
+               cp.valid_days,
+               cp.valid_start_time,
+               cp.valid_end_time,
+               cp.is_valid_time
+        from `coupon` cp
+                 left join (select id, coupon_id, ex_code from `coupon_distribute_popularize` where deleted is true) cdp
+                           on cdp.coupon_id = cp.id
+        where cp.deleted is true
+          and (cp.ex_code = #{couponExCode} or cdp.ex_code = #{couponExCode}) limit 1
+    </select>
+</mapper>

+ 54 - 0
netflix-dao/src/main/resources/mapper/CouponRecommendMapper.xml

@@ -0,0 +1,54 @@
+<?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.cyksj.mapper.manage.coupon.CouponRecommendMapper">
+
+    <select id="getRecommendCouponViewList" resultType="com.cyksj.model.views.CouponRecommendView">
+        select * from (select cr.id,
+        cr.coupon_id,
+        c.name,
+        c.goods_type,
+        c.type,
+        c.reduce_money,
+        c.discount,
+        c.goods_sku_ids,
+        c.is_valid_time,
+        c.valid_start_time,
+        c.valid_end_time,
+        cu.valid_start_time as userValidStartTime,
+        cu.valid_end_time as userValidEndTime,
+        c.valid_days,
+        c.is_renew,
+        c.scope,
+        (case
+        when cu.id is null then 'collecting'
+        when cu.id is not null and cu.status != 'unused' then 'used'
+        when cu.id is not null and cu.valid_end_time &lt; now() then 'expired'
+        when cu.id is not null and cu.valid_start_time > now() then 'noEffective'
+        when cu.id is not null and cu.status = 'unused' then 'received'
+        end) as couponStatus,
+        (case
+        when cu.id is null then 0
+        when cu.id is not null and cu.status = 'unused' then 1
+        end) as `order`,
+        if(c.is_valid_time is true and c.valid_end_time &lt; now(),false,true) as flag
+        from coupon_recommend cr left join coupon c on c.id = cr.coupon_id
+        left join coupon_user cu on cu.coupon_id = c.id and cu.user_id = #{userId}
+        where cr.deleted is true and c.deleted is true and cr.status = 1
+        <if test='!isNewUser'>
+            and c.scope !='newUser'
+        </if>)s where couponStatus not in ('used','expired','noEffective') and flag is true order by `order`
+    </select>
+
+    <select id="getRecommendCouponList" resultType="com.cyksj.model.views.CouponAvailableRecommendView">
+        select * from (select c.id as couponId,
+                              c.scope,
+                              cr.id as recommendId,
+        if(c.is_valid_time is true and c.valid_end_time &lt; now(),false,true) as flag
+        from coupon_recommend cr left join coupon c on c.id = cr.coupon_id
+        left join coupon_user cu on cu.coupon_id = c.id and cu.user_id = #{userId}
+        where cr.deleted is true and c.deleted is true and cr.status is true and cu.coupon_id is null
+        <if test='!isNewUser'>
+            and c.scope !='newUser'
+        </if>)s where flag is true
+    </select>
+</mapper>

+ 160 - 0
netflix-dao/src/main/resources/mapper/CouponUserMapper.xml

@@ -0,0 +1,160 @@
+<?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.cyksj.mapper.manage.coupon.CouponUserMapper">
+
+    <select id="getAvailableCouponList" resultType="com.cyksj.model.views.CouponUserView">
+        select * from (select distinct cu.id,
+        cp.id as coupon_id,
+        cp.name,
+        cp.goods_type,
+        cp.type,
+        cp.goods_sku_ids,
+        cp.discount,
+        cp.reduce_money,
+        cp.is_valid_time,
+        cu.valid_start_time,
+        cu.valid_end_time,
+        cp.valid_days,
+        cu.created_time,
+        cp.is_renew,
+        cu.status as couponUserStatus,
+        if(ck.id is null, 0, 1) as 'isAvailable',
+        if(ca.id is not null and ca.expiry_status = 0,false,true) flag,
+        (case
+        when cu.valid_start_time > now() then 1
+        when cu.status = 'unused' then 0
+        end)                 as orderby
+        from `coupon_user` cu
+        left join `coupon` cp on cp.id = cu.coupon_id
+        left join `coupon_active` ca on ca.id = cu.coupon_active_id
+        left join `coupon_sku` ck on ck.coupon_id = cp.id
+        <if test="goodsId != null">
+            and ck.goods_id = #{goodsId}
+        </if>
+        <if test="goodsId == null">
+            and ck.sku_id = #{skuId}
+        </if>
+        <if test="isRenew != null and isRenew">
+            and cp.is_renew = #{isRenew}
+        </if>
+        where cu.user_id = #{userId}
+        and cu.channel != 'exCode'
+        and cu.valid_end_time > now()
+        <if test="couponId == null">
+            order by isAvailable desc,orderby
+        </if>
+        <if test="couponId != null">
+            order by case when cp.id = #{couponId} then 2 else isAvailable end desc,orderby
+        </if>)s where flag = 1 and couponUserStatus != 'used'
+    </select>
+
+    <select id="countAvailableCouponList" resultType="java.lang.Integer">
+        select count(distinct id)
+        from (select cu.id,ca.expiry_status as flag
+        from `coupon_user` cu
+        left join `coupon` cp on cp.id = cu.coupon_id
+        left join `coupon_sku` ck on ck.coupon_id = cp.id
+        left join `coupon_active` ca on ca.id = cu.coupon_active_id
+        where
+        <if test="goodsId != null">
+            ck.goods_id = #{goodsId}
+        </if>
+        <if test="goodsId == null">
+            ck.sku_id = #{skuId}
+        </if>
+        and cu.user_id = #{userId}
+        and cu.status = 'unused'
+        and cu.channel != 'exCode'
+        and cu.valid_start_time &lt; now()
+        and cu.valid_end_time > now()
+        <if test="isRenew != null and isRenew">
+            and cp.is_renew = #{isRenew}
+        </if>)s where flag != 0 or flag is null
+    </select>
+
+    <select id="couponCenterList" resultType="com.cyksj.model.views.CouponActiveView">
+        select *
+        from (select ca.id,
+                     c.id                                                                     as coupon_id,
+                     c.name,
+                     c.goods_type,
+                     c.type,
+                     c.reduce_money,
+                     c.discount,
+                     c.scope,
+                     c.goods_sku_ids,
+                     c.is_valid_time,
+                     c.valid_start_time,
+                     c.valid_end_time,
+                     c.valid_days,
+                     c.is_renew,
+                     ca.num,
+                     ca.`status`,
+                     ca.expiry_status,
+                     ca.remain_num,
+                     (case
+                          when ca.remain_num &lt;= 0 then 'over'
+                          when cu.id is null then 'collecting'
+                          when cu.id is not null and cu.status != 'unused' then 'used'
+                          when cu.id is not null and cu.status = 'unused' then 'received'
+                         end)                                                                 as couponStatus,
+                     (case
+                          when remain_num &lt;= 0 then 2
+                          when cu.id is null then 0
+                          when cu.id is not null and cu.status = 'unused' then 1
+                         end)                                                                 as `order`,
+                     if(c.is_valid_time is true and c.valid_end_time &lt; now(), false, true) as flag
+              from coupon_active ca
+                       left join coupon c
+                                 on c.id = ca.coupon_id
+                       left join coupon_user cu on cu.coupon_id = c.id and cu.user_id = #{userId}
+              where ca.deleted is true
+                and c.deleted is true
+                and cu.id is null
+                and ca.expiry_status != 0
+        and ca.`status` != 0
+             ) s
+        where couponStatus in ('over', 'collecting')
+          and flag = 1
+        order by `order`
+    </select>
+
+    <select id="couponPersonalList" resultType="com.cyksj.model.views.CouponUserView">
+        select cu.id,
+               cu.channel,
+               cu.user_id,
+               c.id                     as couponId,
+               ca.id                    as couponActiveId,
+               c.name,
+               c.goods_type,
+               c.type,
+               c.reduce_money,
+               c.discount,
+               c.goods_sku_ids,
+               c.is_valid_time,
+               cu.valid_start_time,
+               c.valid_days,
+               cu.valid_end_time,
+               c.is_renew,
+               (case
+                    when cu.status = 'used' then 'used'
+                    when cu.valid_end_time &lt; now() then 'expired'
+                    when ca.id is not null and ca.expiry_status = 0 then 'invalid'
+                    when cu.valid_start_time > now() then 'notEffective'
+                    else cu.status end) as couponUserStatus,
+               (case
+                    when cu.status = 'used' then 4
+                    when cu.valid_end_time &lt; now() then 2
+                    when ca.id is not null and ca.expiry_status = 0 then 3
+                    when cu.valid_start_time > now() then 1
+                    when cu.status = 'unused' then 0
+                   end)                 as orderby
+        from `coupon_user` cu
+                 left join `coupon` c on c.id = cu.coupon_id
+                 left join `coupon_active` ca on ca.id = cu.coupon_active_id
+        where cu.user_id = #{userId}
+          and cu.channel != 'exCode'
+        order by `orderby`
+    </select>
+</mapper>

+ 4 - 0
netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml

@@ -30,4 +30,8 @@
         </if>
         group by gr.groups_id
     </select>
+
+    <select id="getGroupTripsSkuIdByRelationId" resultType="java.lang.Long">
+        select gt.sku_id from `groups_trips` gt left join `groups_relation` gr on gr.groups_id = gt.id where gr.id = #{relationId} limit 1
+    </select>
 </mapper>

+ 1 - 0
netflix-service/src/main/java/com/cyksj/redis/RedisService.java

@@ -591,6 +591,7 @@ public class RedisService {
 	    CORP_AGENT_JS_TICKET_KEY("corp_wechat:js_ticket:agent:", "js_ticket", 60 * 30L),
 	    MAPPING_INDEX_KEY("mapper:index:", "授权跳转", 60 * 10L),
         WX_LOGIN_AUTH_URI("wx_login_auth_uri:%s", "微信网页授权登录uri", 60 * 60 * 24L),
+        RECOMMEND_COUPON_DAY("recommend_coupon_day:%s", "推荐优惠券弹窗", 60 * 60 * 24L)
         ;
 
         private String name;

+ 47 - 0
netflix-service/src/main/java/com/cyksj/service/coupon/CouponFontService.java

@@ -0,0 +1,47 @@
+package com.cyksj.service.coupon;
+
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.model.entity.CouponUser;
+import com.cyksj.model.request.CouponCollectReq;
+import com.cyksj.model.views.CouponUserView;
+
+/*
+ *项目名: netflix
+ *文件名: CouponFontService
+ *创建者: JavaZou
+ *创建时间:2022/11/18 13:34
+ */
+public interface CouponFontService {
+	/**
+	 * 领取优惠券
+	 */
+	void collectCoupon(CouponCollectReq couponCollectReq, Long userId);
+
+	/**
+	 * 领取推荐优惠券
+	 */
+	void collectRecommendCoupon(CouponCollectReq couponCollectReq, Long userId);
+
+	/**
+	 * 一键领取推荐优惠券
+	 */
+	void collectWholeRecommendCoupon(Long userId);
+
+	/**
+	 * 可用/不可用优惠券
+	 */
+	Page<CouponUserView> getAvailableCouponList(Long skuId, Long couponId, Long userId, Boolean isRenew, Long start, Long limit);
+
+	/**
+	 * 优惠券状态
+	 */
+	void setCouponUserStatus(CouponUserView data, DateTime now);
+
+	Integer collectRecommendCouponNum(Long userId, Boolean isNewUser);
+
+	/**
+	 * 更新优惠券有效时间
+	 */
+	void updateCouponUserValidTime(Long couponId, CouponUser couponUser);
+}

+ 249 - 0
netflix-service/src/main/java/com/cyksj/service/coupon/impl/CouponFontServiceImpl.java

@@ -0,0 +1,249 @@
+package com.cyksj.service.coupon.impl;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.mapper.manage.coupon.*;
+import com.cyksj.model.entity.Coupon;
+import com.cyksj.model.entity.CouponActive;
+import com.cyksj.model.entity.CouponUser;
+import com.cyksj.model.request.CouponCollectReq;
+import com.cyksj.model.views.CouponAvailableRecommendView;
+import com.cyksj.model.views.CouponNewUserView;
+import com.cyksj.model.views.CouponUserView;
+import com.cyksj.model.views.GoodsDonSkuView;
+import com.cyksj.service.coupon.CouponFontService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/*
+ *项目名: netflix
+ *文件名: CouponFontServiceImpl
+ *创建者: JavaZou
+ *创建时间:2022/11/18 13:34
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class CouponFontServiceImpl implements CouponFontService {
+	private final CouponMapper couponMapper;
+
+	private final CouponActiveMapper couponActiveMapper;
+
+	private final CouponUserMapper couponUserMapper;
+
+	private final BeanSearcher beanSearcher;
+
+	private final CouponSkuMapper couponSkuMapper;
+
+	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+	private final CouponRecommendMapper recommendMapper;
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void collectCoupon(CouponCollectReq couponCollectReq, Long userId) {
+		collectCommonCheck(couponCollectReq, userId);
+		CouponActive couponActive = couponActiveMapper.selectById(couponCollectReq.getCouponActiveId());
+		if (couponActive == null) {
+			throw BusinessRuntimeException.getInstance("该发放优惠券不存在");
+		}
+		if (couponActive.getRemainNum() <= 0) {
+			throw BusinessRuntimeException.getInstance("该优惠券已领取完");
+		}
+		CouponUser couponUser = new CouponUser();
+		couponUser.setUserId(userId);
+		couponUser.setCouponId(couponCollectReq.getCouponId());
+		//领券中心
+		couponUser.setChannel(CouponUser.Channel.center);
+		couponUser.setStatus(CouponUser.Status.unused);
+		couponUser.setCouponActiveId(couponCollectReq.getCouponActiveId());
+		//优惠券有效时间
+		this.updateCouponUserValidTime(couponActive.getCouponId(), couponUser);
+		try {
+			couponUserMapper.insert(couponUser);
+		} catch (DuplicateKeyException e) {
+			log.info("用户:{}重复领取优惠券{}插入数据错误", userId, couponCollectReq.getCouponId());
+			throw BusinessRuntimeException.getInstance("已领取成功");
+		}
+		if (couponActive != null) {
+			//发放优惠券记录剩余数量
+			Integer remainNum = couponActive.getRemainNum();
+			Integer update = couponActiveMapper.subRemainNum(couponActive.getId(), remainNum);
+			if (update != 1) {
+				throw BusinessRuntimeException.getInstance("系统繁忙,请稍后再试..");
+			}
+		}
+	}
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void collectRecommendCoupon(CouponCollectReq couponCollectReq, Long userId) {
+		collectCommonCheck(couponCollectReq, userId);
+		CouponUser couponUser = new CouponUser();
+		couponUser.setUserId(userId);
+		couponUser.setCouponId(couponCollectReq.getCouponId());
+		//推荐渠道
+		couponUser.setChannel(CouponUser.Channel.recommend);
+		couponUser.setStatus(CouponUser.Status.unused);
+		couponUser.setRecommendId(couponCollectReq.getCouponRecommendId());
+		//优惠券有效时间
+		this.updateCouponUserValidTime(couponCollectReq.getCouponId(), couponUser);
+		try {
+			couponUserMapper.insert(couponUser);
+		} catch (DuplicateKeyException e) {
+			log.info("用户:{}重复领取推荐优惠券{}插入数据错误", userId, couponCollectReq.getCouponId());
+			throw BusinessRuntimeException.getInstance("已领取成功");
+		}
+	}
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void collectWholeRecommendCoupon(Long userId) {
+		CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
+		Boolean isNewUser = true;
+		if (couponNewUserView != null) {
+			isNewUser = false;
+		}
+		//查询所有可领取的推荐优惠券
+		List<CouponAvailableRecommendView> couponAvailableRecommendViews = recommendMapper.getRecommendCouponList(userId, isNewUser);
+		couponAvailableRecommendViews.stream().forEach(data -> {
+			//领取优惠券
+			CouponUser couponUser = new CouponUser();
+			couponUser.setUserId(userId);
+			couponUser.setCouponId(data.getCouponId());
+			//推荐渠道
+			couponUser.setChannel(CouponUser.Channel.recommend);
+			couponUser.setStatus(CouponUser.Status.unused);
+			couponUser.setRecommendId(data.getRecommendId());
+			//优惠券有效时间
+			this.updateCouponUserValidTime(data.getCouponId(), couponUser);
+			try {
+				couponUserMapper.insert(couponUser);
+			} catch (DuplicateKeyException e) {
+				//插入失败继续插入 不影响
+				log.info("用户:{}一键领取推荐优惠券{}插入数据错误", userId, data.getCouponId());
+			}
+		});
+	}
+
+	@Override
+	public Page<CouponUserView> getAvailableCouponList(Long skuId, Long couponId, Long userId, Boolean isRenew, Long start, Long limit) {
+		DateTime now = DateTime.now();
+		Long goodsId = null;
+		//实物还是虚物
+		GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getSkuId, skuId).op(Operator.Equal).build());
+		if (goodsDonSkuView.getType() == 2) {
+			goodsId = goodsDonSkuView.getGoodsId();
+		}
+		Page<CouponUserView> availableCouponList = couponUserMapper.getAvailableCouponList(skuId, couponId, goodsId, userId, isRenew, new Page<>(start, limit));
+		Optional.ofNullable(availableCouponList).ifPresent(page->{
+			page.getRecords().stream().forEach(data->{
+				String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId());
+				data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+				if (!data.getIsAvailable()) {
+					data.setReason(String.format("仅限%s", data.getGoodsSkuStr()));
+					if (isRenew != null && isRenew && data.getIsRenew()) {
+						data.setReason(String.format("%s续费时可使用", data.getReason()));
+					}
+				}
+				if (isRenew != null && isRenew && !data.getIsRenew()) {
+					data.setReason(String.format("续费时,不可使用"));
+				}
+				//为了重新校验
+				data.setCouponUserStatus(CouponUser.Status.unused);
+				this.setCouponUserStatus(data, now);
+			});
+		});
+		return availableCouponList;
+	}
+
+	/**
+	 * 设置用户优惠券状态
+	 */
+	@Override
+	public void setCouponUserStatus(CouponUserView data, DateTime now) {
+		Boolean isFlag = false;
+		if (data.getCouponUserStatus() == CouponUser.Status.unused) {
+			if (data.getExpiryStatus() != null && !data.getExpiryStatus()) {
+				data.setCouponUserStatus(CouponUser.Status.invalid);
+				data.setIsAvailable(false);
+				data.setReason("该优惠券已失效");
+				isFlag = true;
+			} else if (now.isAfter(data.getValidEndTime())) {
+				data.setCouponUserStatus(CouponUser.Status.expired);
+				data.setIsAvailable(false);
+				data.setReason("该优惠券已过期");
+				isFlag = true;
+			} else if (now.isBefore(data.getValidStartTime())) {
+				data.setCouponUserStatus(CouponUser.Status.notEffective);
+				data.setIsAvailable(false);
+				data.setReason("该优惠券未生效");
+				isFlag = true;
+			}
+		}
+		if (isFlag) {
+			TASK_EXECUTOR.execute(() -> {
+				if (data.getCouponUserStatus() != CouponUser.Status.notEffective) {
+					CouponUser couponUser = new CouponUser();
+					couponUser.setId(data.getId());
+					couponUser.setStatus(data.getCouponUserStatus());
+					couponUserMapper.updateById(couponUser);
+				}
+			});
+		}
+	}
+
+	@Override
+	public Integer collectRecommendCouponNum(Long userId, Boolean isNewUser) {
+		List<CouponAvailableRecommendView> recommendCouponList = Optional.ofNullable(recommendMapper.getRecommendCouponList(userId, isNewUser)).orElse(new ArrayList<>());
+		return recommendCouponList.size();
+	}
+
+	public void collectCommonCheck(CouponCollectReq couponCollectReq, Long userId) {
+		Coupon coupon = couponMapper.selectById(couponCollectReq.getCouponId());
+		if (coupon == null || !coupon.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该优惠券不存在");
+		}
+		//是否可领取
+		if (coupon.getScope() == Coupon.Scope.newUser) {
+			//注册后从没产生过虚拟订单的用户,产生过退款后也不是新用户
+			CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
+			if (couponNewUserView != null) {
+				throw BusinessRuntimeException.getInstance("该优惠券只能新用户才能领取");
+			}
+		}
+		CouponUser couponUser = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponCollectReq.getCouponId()));
+		if (couponUser != null) {
+			if (couponUser.getChannel() == CouponUser.Channel.exCode) {
+				throw BusinessRuntimeException.getInstance("您已兑换过该优惠券..");
+			}
+			throw BusinessRuntimeException.getInstance("您已领取该优惠券..");
+		}
+	}
+
+	@Override
+	public void updateCouponUserValidTime(Long couponId, CouponUser couponUser) {
+		Coupon coupon = couponMapper.getCouponById(couponId);
+		if (coupon.getIsValidTime()) {
+			couponUser.setValidStartTime(coupon.getValidStartTime());
+			couponUser.setValidEndTime(coupon.getValidEndTime());
+		} else {
+			couponUser.setValidStartTime(DateTime.now());
+			couponUser.setValidEndTime(DateUtil.offsetDay(couponUser.getValidStartTime(), coupon.getValidDays()));
+		}
+	}
+}

+ 3 - 0
netflix-service/src/main/java/com/cyksj/service/mange/CmsAccountService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.cyksj.model.entity.Account;
 import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.excel.ExcelAccountData;
 import com.cyksj.model.views.ExpiredAccountDataView;
 
 import java.util.Date;
@@ -19,4 +20,6 @@ public interface CmsAccountService extends IService<Account> {
 	List<Account> getNoAppleOneAccount(String appleOne);
 
 	List<OrderDon> getNoAppleOneOrderDon(List<Long> skuIds, Date yesZeroDate, Date thisZeroDate, List<String> noOrderStatus);
+
+	List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startDate, String endDate, String toString);
 }

+ 6 - 0
netflix-service/src/main/java/com/cyksj/service/mange/account/CmsAccountServiceImpl.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.cyksj.mapper.AccountMapper;
 import com.cyksj.model.entity.Account;
 import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.excel.ExcelAccountData;
 import com.cyksj.model.views.ExpiredAccountDataView;
 import com.cyksj.service.mange.CmsAccountService;
 import lombok.RequiredArgsConstructor;
@@ -30,6 +31,11 @@ public class CmsAccountServiceImpl extends ServiceImpl<AccountMapper, Account> i
 		return accountMapper.getExpiredAccountView(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, now, new Page<>(offset, limit));
 	}
 
+	@Override
+	public List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startTime, String endTime, String now) {
+		return accountMapper.getExpiredAccountViewNoPage(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, now);
+	}
+
 	@Override
 	public List<Account> getNoAppleOneAccount(String appleOne) {
 		return accountMapper.getNoAppleOneAccount(appleOne);

+ 62 - 0
netflix-service/src/main/java/com/cyksj/service/mange/coupon/CouponCommonService.java

@@ -0,0 +1,62 @@
+package com.cyksj.service.mange.coupon;
+
+import com.cyksj.model.entity.Coupon;
+import com.cyksj.model.entity.CouponDistributePopularize;
+import com.cyksj.model.request.CorpCouponSendReq;
+import com.cyksj.model.request.CouponActiveReq;
+import com.cyksj.model.request.CouponDistributeReq;
+
+/*
+ *项目名: netflix
+ *文件名: CouponCommonService
+ *创建者: JavaZou
+ *创建时间:2022/11/17 16:59
+ */
+public interface CouponCommonService {
+	/**
+	 * 新增优惠券
+	 *
+	 * @param coupon
+	 */
+	void saveCoupon(Coupon coupon) throws Exception;
+
+	/**
+	 * 更新优惠券
+	 *
+	 * @param coupon
+	 */
+	void updateCoupon(Coupon coupon) throws Exception;
+
+	/**
+	 * 发放优惠券
+	 *
+	 * @param couponActiveReq
+	 */
+	void sendCoupon(CouponActiveReq couponActiveReq);
+
+	/**
+	 * 设置为推荐优惠券
+	 *
+	 * @param couponId
+	 */
+	void setHotCoupon(Long couponId);
+
+	/**
+	 * 关联推广者
+	 *
+	 * @param couponDistributeReq
+	 */
+	void setPopularizeRelation(CouponDistributeReq couponDistributeReq);
+
+	/**
+	 * 设置推广者优惠券兑换码
+	 *
+	 * @param couponDistributePopularize
+	 */
+	void setPopularizeRelationCode(CouponDistributePopularize couponDistributePopularize);
+
+	/**
+	 * 发放优惠券
+	 */
+	void corpSendCoupon(CorpCouponSendReq corpCouponSendReq) throws Exception;
+}

+ 287 - 0
netflix-service/src/main/java/com/cyksj/service/mange/coupon/impl/CouponCommonServiceImpl.java

@@ -0,0 +1,287 @@
+package com.cyksj.service.mange.coupon.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.config.corp.YHLXWxCorpConfig;
+import com.cyksj.mapper.GoodsDonMapper;
+import com.cyksj.mapper.GoodsDonSkuMapper;
+import com.cyksj.mapper.UserMapper;
+import com.cyksj.mapper.corp.CorpUserMapper;
+import com.cyksj.mapper.manage.coupon.*;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.request.CorpCouponSendReq;
+import com.cyksj.model.request.CouponActiveReq;
+import com.cyksj.model.request.CouponDistributeReq;
+import com.cyksj.service.corp.msg.CorpEventActionService;
+import com.cyksj.service.mange.coupon.CouponCommonService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/*
+ *项目名: netflix
+ *文件名: CouponCommonServiceImpl
+ *创建者: JavaZou
+ *创建时间:2022/11/17 17:00
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class CouponCommonServiceImpl implements CouponCommonService {
+	private final GoodsDonSkuMapper goodsDonSkuMapper;
+
+	private final CouponMapper couponMapper;
+
+	private final CouponActiveMapper couponActiveMapper;
+
+	private final CouponRecommendMapper couponRecommendMapper;
+
+	private final CouponDistributePopularizeMapper couponDistributePopularizeMapper;
+
+	private final CouponSkuMapper couponSkuMapper;
+
+	private final CorpUserMapper corpUserMapper;
+
+	private final CorpEventActionService corpEventActionService;
+
+	private final YHLXWxCorpConfig yhlXWxCorpConfig;
+
+	private final UserMapper userMapper;
+
+	private final CouponUserMapper couponUserMapper;
+
+	private final GoodsDonMapper goodsDonMapper;
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void saveCoupon(Coupon coupon) throws Exception {
+		checkCouponParamsAndSaveOrUpdate(coupon);
+	}
+
+	@Override
+	public void updateCoupon(Coupon coupon) throws Exception {
+		if (coupon.getId() == null || coupon.getId() < 1) {
+			throw BusinessRuntimeException.getInstance("请输入正确的优惠券id");
+		}
+		Coupon entity = couponMapper.selectById(coupon.getId());
+		if (entity == null || !entity.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该优惠券不存在");
+		}
+		checkCouponParamsAndSaveOrUpdate(coupon);
+	}
+
+	@Override
+	public void sendCoupon(CouponActiveReq couponActiveReq) {
+		Coupon coupon = couponMapper.selectById(couponActiveReq.getCouponId());
+		if (coupon == null || !coupon.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该优惠券不存在");
+		}
+		if (!coupon.getSendStatus()) {
+			coupon.setSendStatus(!coupon.getSendStatus());
+			couponMapper.updateById(coupon);
+		}
+		CouponActive couponActive = new CouponActive();
+		couponActive.setCouponId(coupon.getId());
+		couponActive.setNum(couponActiveReq.getNum());
+		//发放优惠剩余数量
+		couponActive.setRemainNum(couponActiveReq.getNum());
+		couponActiveMapper.insert(couponActive);
+	}
+
+	@Override
+	public void setHotCoupon(Long couponId) {
+		Coupon coupon = couponMapper.selectById(couponId);
+		if (coupon == null || !coupon.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该优惠券不存在");
+		}
+		if (coupon.getHotStatus()) {
+			throw BusinessRuntimeException.getInstance("已推荐该优惠券");
+		}
+		if (!coupon.getHotStatus()) {
+			coupon.setHotStatus(!coupon.getHotStatus());
+		}
+		CouponRecommend couponRecommend = new CouponRecommend();
+		couponRecommend.setCouponId(coupon.getId());
+		couponRecommendMapper.insert(couponRecommend);
+		couponMapper.updateById(coupon);
+	}
+
+	@Override
+	public void setPopularizeRelation(CouponDistributeReq couponDistributeReq) {
+		Coupon coupon = couponMapper.selectById(couponDistributeReq.getCouponId());
+		if (coupon == null || !coupon.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该优惠券不存在");
+		}
+		CouponDistributePopularize exists = couponDistributePopularizeMapper.selectOne(Wrappers.lambdaQuery(CouponDistributePopularize.class).eq(CouponDistributePopularize::getCouponId, coupon.getId()).eq(CouponDistributePopularize::getDistributeId, couponDistributeReq.getDistributeId()).eq(CouponDistributePopularize::getDeleted, true).last("limit 1"));
+		if (exists != null) {
+			throw BusinessRuntimeException.getInstance("该优惠券已关联该固定推广者");
+		}
+		CouponDistributePopularize couponDistributePopularize = new CouponDistributePopularize();
+		couponDistributePopularize.setCouponId(coupon.getId());
+		couponDistributePopularize.setDistributeId(couponDistributeReq.getDistributeId());
+		couponDistributePopularizeMapper.insert(couponDistributePopularize);
+	}
+
+	@Override
+	public void setPopularizeRelationCode(CouponDistributePopularize couponDistributePopularize) {
+		if (couponDistributePopularize.getId() == null || couponDistributePopularize.getId() < 1) {
+			throw BusinessRuntimeException.getInstance("优惠券推广者关联id为空");
+		}
+		CouponDistributePopularize entity = couponDistributePopularizeMapper.selectById(couponDistributePopularize.getId());
+		if (entity == null) {
+			throw BusinessRuntimeException.getInstance("该优惠券推广关联记录不存在");
+		}
+		if (StrUtil.isEmpty(couponDistributePopularize.getExCode())) {
+			throw BusinessRuntimeException.getInstance("优惠券兑换码为空");
+		}
+		entity.setExCode(couponDistributePopularize.getExCode());
+		CouponDistributePopularize exists = couponDistributePopularizeMapper.selectOne(Wrappers.lambdaQuery(CouponDistributePopularize.class).eq(CouponDistributePopularize::getExCode, couponDistributePopularize.getExCode()).eq(CouponDistributePopularize::getDeleted, true).ne(CouponDistributePopularize::getId, entity.getId()).select(CouponDistributePopularize::getId).last("limit 1"));
+		if (exists != null) {
+			throw BusinessRuntimeException.getInstance("推荐官兑换码不能重复");
+		} else {
+			Coupon coupon = couponMapper.selectOne(Wrappers.lambdaQuery(Coupon.class).eq(Coupon::getExCode, couponDistributePopularize.getExCode()).eq(Coupon::getDeleted, true).select(Coupon::getId).last("limit 1"));
+			if (coupon != null) {
+				throw BusinessRuntimeException.getInstance("推荐官兑换码与优惠券兑换码设置重复");
+			}
+		}
+		couponDistributePopularizeMapper.updateById(entity);
+	}
+
+	@Override
+	public void corpSendCoupon(CorpCouponSendReq corpCouponSendReq) throws Exception {
+		String externalUserId = corpCouponSendReq.getExternalUserId();
+		CorpUser corpUser = corpUserMapper.selectOne(Wrappers.lambdaQuery(CorpUser.class).eq(CorpUser::getExternalUserid, externalUserId).last("limit 1"));
+		if (corpUser == null) {
+			//尝试添加客户
+			corpEventActionService.addExternalContact(yhlXWxCorpConfig.getCorpId(), null, externalUserId, null, null);
+			corpUser = corpUserMapper.selectOne(Wrappers.lambdaQuery(CorpUser.class).eq(CorpUser::getExternalUserid, externalUserId).last("limit 1"));
+		}
+		if (corpUser == null) {
+			log.info("该客户{}不存在", externalUserId);
+			throw BusinessRuntimeException.getInstance("该企业微信用户不存在..");
+		}
+		String unionid = corpUser.getUnionid();
+		if (StrUtil.isEmpty(unionid)) {
+			throw BusinessRuntimeException.getInstance("该用户不是微信用户");
+		}
+		User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUnionid, unionid).select(User::getId).last("limit 1"));
+		if (user == null) {
+			throw BusinessRuntimeException.getInstance("该用户未在银河录像注册");
+		}
+		Long couponId = corpCouponSendReq.getCouponId();
+		Coupon coupon = couponMapper.selectById(couponId);
+		if (coupon == null || !coupon.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该优惠券已被删除");
+		}
+		CouponUser couponUser = new CouponUser();
+		couponUser.setUserId(user.getId());
+		couponUser.setCouponId(corpCouponSendReq.getCouponId());
+		//领卷中心
+		couponUser.setChannel(CouponUser.Channel.wxCorp);
+		couponUser.setStatus(CouponUser.Status.unused);
+		try {
+			couponUserMapper.insert(couponUser);
+		} catch (DuplicateKeyException e) {
+			log.info("用户:{}重复领取优惠券{}插入数据错误", user.getId(), corpCouponSendReq.getCouponId());
+			throw BusinessRuntimeException.getInstance("已发放该优惠券");
+		}
+	}
+
+
+	public void checkCouponParamsAndSaveOrUpdate(Coupon coupon) throws Exception {
+		List<Coupon.CouponGoodsSku> goodsSkuList = Jsons.parseList(coupon.getGoodsSkuIds(), Coupon.CouponGoodsSku.class);
+		if (goodsSkuList.isEmpty()) {
+			throw BusinessRuntimeException.getInstance("所选规格不存在");
+		}
+		if (coupon.getType() == Coupon.Type.discount) {
+			if (coupon.getDiscount() == null || (coupon.getDiscount().compareTo(BigDecimal.ZERO) < 0 && coupon.getDiscount().compareTo(BigDecimal.ZERO) > 0)) {
+				throw BusinessRuntimeException.getInstance("请输入正确的折扣");
+			}
+		}
+		if (coupon.getType() == Coupon.Type.reduce) {
+			if (coupon.getReduceMoney() == null) {
+				throw BusinessRuntimeException.getInstance("请配置其立减金额");
+			}
+
+			if (coupon.getGoodsType() == Coupon.GoodsType.virtualGoods) {
+				List<Long> skuIds = goodsSkuList.stream().map(Coupon.CouponGoodsSku::getSkuId).collect(Collectors.toList());
+				skuIds.forEach(skuId -> {
+					GoodsDonSku sku = goodsDonSkuMapper.selectOne(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getId, skuId).select(GoodsDonSku::getPrice).last("limit 1"));
+					if (sku == null) {
+						throw BusinessRuntimeException.getInstance("所选规格不存在");
+					}
+					if (coupon.getReduceMoney().compareTo(sku.getPrice()) > 0) {
+						throw BusinessRuntimeException.getInstance("可减金额必须小于规格金额");
+					}
+				});
+			} else {
+				List<Long> goodsIds = goodsSkuList.stream().map(Coupon.CouponGoodsSku::getGoodsId).collect(Collectors.toList());
+				goodsIds.forEach(goodsId -> {
+					BigDecimal price = goodsDonMapper.selectMinPriceSkuByGoodsId(goodsId);
+					if (price == null) {
+						throw BusinessRuntimeException.getInstance("实物商品不存在");
+					}
+					if (coupon.getReduceMoney().compareTo(price) > 0) {
+						throw BusinessRuntimeException.getInstance("可减金额必须小于实物最小规格金额");
+					}
+				});
+			}
+			if (coupon.getIsValidTime()) {
+				if (coupon.getValidStartTime() == null || coupon.getValidEndTime() == null || (coupon.getValidStartTime().getTime() > coupon.getValidEndTime().getTime())) {
+					throw BusinessRuntimeException.getInstance("请输入正确的有效起始和截止时间");
+				}
+			} else {
+				if (coupon.getValidDays() == null || coupon.getValidDays() < 0) {
+					throw BusinessRuntimeException.getInstance("请输入正确的有效天数");
+				}
+			}
+		}
+		if (StrUtil.isNotEmpty(coupon.getExCode())) {
+			Coupon exists = couponMapper.selectOne(Wrappers.lambdaQuery(Coupon.class).eq(Coupon::getExCode, coupon.getExCode()).eq(Coupon::getDeleted, true).ne(coupon.getId() != null, Coupon::getId, coupon.getId()).select(Coupon::getId).last("limit 1"));
+			if (exists != null) {
+				throw BusinessRuntimeException.getInstance("优惠券兑换码重复");
+			} else {
+				CouponDistributePopularize couponDistributePopularize = couponDistributePopularizeMapper.selectOne(Wrappers.lambdaQuery(CouponDistributePopularize.class).eq(CouponDistributePopularize::getExCode, coupon.getExCode()).eq(CouponDistributePopularize::getDeleted, true).select(CouponDistributePopularize::getId).last("limit 1"));
+				if (couponDistributePopularize != null) {
+					throw BusinessRuntimeException.getInstance("优惠券兑换码与推荐管兑换码设置重复");
+				}
+			}
+		}
+		if (coupon.getId() == null) {
+			try {
+				couponMapper.insert(coupon);
+			} catch (DuplicateKeyException e) {
+				throw BusinessRuntimeException.getInstance("优惠券名称重复");
+			}
+			goodsSkuList.forEach(goodsSkuId -> {
+				CouponSku couponSku = new CouponSku();
+				couponSku.setCouponId(coupon.getId());
+				couponSku.setGoodsId(goodsSkuId.getGoodsId());
+				couponSku.setSkuId(goodsSkuId.getSkuId());
+				couponSkuMapper.insert(couponSku);
+			});
+		} else {
+			try {
+				couponMapper.updateById(coupon);
+			} catch (DuplicateKeyException e) {
+				throw BusinessRuntimeException.getInstance("优惠券名称重复");
+			}
+			couponSkuMapper.deleteRelationSku(coupon.getId());
+			goodsSkuList.forEach(goodsSkuId -> {
+				CouponSku couponSku = new CouponSku();
+				couponSku.setCouponId(coupon.getId());
+				couponSku.setGoodsId(goodsSkuId.getGoodsId());
+				couponSku.setSkuId(goodsSkuId.getSkuId());
+				couponSkuMapper.insert(couponSku);
+			});
+		}
+	}
+}

+ 5 - 0
netflix-service/src/main/java/com/cyksj/service/mange/statistics/StatisticsDataService.java

@@ -2,9 +2,12 @@ package com.cyksj.service.mange.statistics;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.cyksj.dto.Result;
+import com.cyksj.model.excel.ExcelAccountData;
 import com.cyksj.model.views.BackgroundDataView;
 import com.cyksj.model.views.ExpiredAccountDataView;
 
+import java.util.List;
+
 /*
  *项目名: netflix
  *文件名: StatisticsService
@@ -19,4 +22,6 @@ public interface StatisticsDataService {
 	Result<String> updateRenewStatus(Integer relationId);
 
 	Result<String> updateHandleStatus(Integer relationId);
+
+	List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime);
 }

+ 15 - 0
netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataServiceImpl.java

@@ -14,6 +14,7 @@ import com.cyksj.mapper.manage.statistics.AppleOneDataMapper;
 import com.cyksj.mapper.manage.statistics.RealGoodsDataMapper;
 import com.cyksj.mapper.manage.statistics.StreamingAccountDataMapper;
 import com.cyksj.model.entity.*;
+import com.cyksj.model.excel.ExcelAccountData;
 import com.cyksj.model.views.BackgroundDataView;
 import com.cyksj.model.views.DistributeOrdersView;
 import com.cyksj.model.views.ExpiredAccountDataView;
@@ -125,6 +126,20 @@ public class StatisticsDataServiceImpl implements StatisticsDataService {
 		return expiredAccountDataViews;
 	}
 
+	@Override
+	public List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime) {
+		String startDate = null;
+		String endDate = null;
+		if (startTime != null) {
+			startDate = DateTime.of(startTime).toString();
+		}
+		if (endTime != null) {
+			endDate = DateTime.of(endTime).toString();
+		}
+		List<ExcelAccountData> expiredAccountDataViews = accountService.getExpiredAccountViewNoPage(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startDate, endDate, DateTime.now().toString());
+		return expiredAccountDataViews;
+	}
+
 	@Override
 	public Result<String> updateRenewStatus(Integer relationId) {
 		GroupsRelation groupsRelation = groupsRelationMapper.selectById(relationId);

+ 2 - 2
netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java

@@ -20,7 +20,7 @@ import java.util.Map;
  */
 public interface OrderDonService extends IService<OrderDon> {
 
-    OrderDon submit(OrderPayRequest request);
+    OrderDon submit(OrderPayRequest request) throws Exception;
 
     /**
      * 订单支付
@@ -42,7 +42,7 @@ public interface OrderDonService extends IService<OrderDon> {
      */
     void closeOrder(Long orderId);
 
-    OrderDon renewal(OrderPayRequest request);
+    OrderDon renewal(OrderPayRequest request) throws Exception;
 
     OrderDonView getDetail(Long orderId, long userId);
 

+ 234 - 8
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -39,6 +39,10 @@ import com.cyksj.model.dto.AliPayParams;
 import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.dto.WxMpTemplateData;
 import com.cyksj.model.dto.WxMpTemplateMessage;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
+import com.cyksj.mapper.manage.coupon.CouponSkuMapper;
+import com.cyksj.mapper.manage.coupon.CouponUserMapper;
+import com.cyksj.model.dto.*;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.OrderDonView;
 import com.cyksj.model.request.OrderAttach;
@@ -46,24 +50,27 @@ import com.cyksj.model.request.OrderPayRequest;
 import com.cyksj.model.request.WxOrderRefundReq;
 import com.cyksj.model.request.ZfbOrderRefundReq;
 import com.cyksj.model.response.AliPayRefundRep;
+import com.cyksj.model.views.CouponNewUserView;
+import com.cyksj.model.views.CouponUserView;
 import com.cyksj.redis.RedisService;
+import com.cyksj.service.coupon.CouponFontService;
 import com.cyksj.service.order.OrderDonService;
 import com.cyksj.service.wechat.WeChatService;
 import com.cyksj.task.JobManager;
 import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
+import java.util.stream.Collectors;
 
 import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_ALREADY_BE_USER;
 import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_NOT_FIND;
@@ -131,10 +138,23 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final UserPlatDistributeRateMapper userPlatDistributeRateMapper;
 
+	private final CouponMapper couponMapper;
+
+	private final CouponFontService couponFontService;
+
+	private final CouponUserMapper couponUserMapper;
+
+	private final CouponSkuMapper couponSkuMapper;
 
 	@Transactional(rollbackFor = Throwable.class)
 	@Override
-	public OrderDon submit(OrderPayRequest payRequest) {
+	public OrderDon submit(OrderPayRequest payRequest) throws Exception {
+		CouponUser couponUser = null;
+		if (StrUtil.isNotBlank(payRequest.getCouponExCode()) || payRequest.getCouponId() != null) {
+			checkCouponStatus(payRequest.getSkuId(), payRequest.getCouponExCode(), payRequest.getCouponId(), payRequest.getUserId(), false);
+			//记录优惠券使用状态
+			couponUser = couponStatusRecord(payRequest.getCouponExCode(), payRequest.getCouponId(), payRequest.getUserId());
+		}
 		User user = userMapper.selectById(payRequest.getUserId());
 		GoodsDonSku sku = goodsDonSkuMapper.selectById(payRequest.getSkuId());
 		GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
@@ -215,7 +235,28 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		OrderDon orderDon = new OrderDon();
 		orderDon.setOrderNo(donNo);
 		orderDon.setOpenId(payOpenId);
+		if (sku.getPrice() != null && payRequest.getDonMoney().compareTo(sku.getPrice()) < 0) {
+			throw BusinessRuntimeException.getInstance("车位所付金额错误");
+		}
 		orderDon.setMoney(payRequest.getDonMoney());
+		orderDon.setRealMoney(payRequest.getDonMoney());
+		//存在优惠券
+		if (couponUser != null) {
+			Long couponId = couponUser.getCouponId();
+			Coupon coupon = couponMapper.getCouponById(couponId);
+			BigDecimal couponMoney = null;
+			if (coupon.getType() == Coupon.Type.discount) {
+				BigDecimal discountMoney = payRequest.getDonMoney().multiply(coupon.getDiscount());
+				orderDon.setMoney(discountMoney);
+				couponMoney = payRequest.getDonMoney().subtract(discountMoney);
+			} else if (coupon.getType() == Coupon.Type.reduce) {
+				BigDecimal subtractMoney = payRequest.getDonMoney().subtract(coupon.getReduceMoney());
+				orderDon.setMoney(subtractMoney);
+				couponMoney = payRequest.getDonMoney().subtract(subtractMoney);
+			}
+			orderDon.setCouponMoney(couponMoney);
+			orderDon.setCouponUserId(couponUser.getId());
+		}
 		orderDon.setSkuId(payRequest.getSkuId());
 		orderDon.setRelationId(relationId);
 		orderDon.setNum(payRequest.getNum());
@@ -234,9 +275,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				}
 			}
 		}
+		if (relationId != 0) {
+			Long gtSkuId = groupsRelationMapper.getGroupTripsSkuIdByRelationId(relationId);
+			if (!gtSkuId.equals(orderDon.getSkuId())) {
+				throw BusinessRuntimeException.getInstance("车位与车队规格不一致");
+			}
+		}
 		this.saveOrUpdate(orderDon);
 
-
 		if (goodsDon.getType() == 2) {
 			saveTransport(payRequest.getAddressId(), orderDon.getId());
 		}
@@ -447,6 +493,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 					}
 				}
 			}
+			//返还优惠券
+			if (orderDon.getCouponUserId() != 0) {
+				updateOrderDonCouponStatus(orderDon);
+			}
 		}
 	}
 
@@ -477,9 +527,15 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	}
 
 	@Override
-	public OrderDon renewal(OrderPayRequest payRequest) {
+	@Transactional(rollbackFor = Throwable.class)
+	public OrderDon renewal(OrderPayRequest payRequest) throws Exception {
 		User user = userMapper.selectById(payRequest.getUserId());
-
+		CouponUser couponUser = null;
+		if (StrUtil.isNotBlank(payRequest.getCouponExCode()) || payRequest.getCouponId() != null) {
+			checkCouponStatus(payRequest.getSkuId(), payRequest.getCouponExCode(), payRequest.getCouponId(), payRequest.getUserId(), true);
+			//记录优惠券使用状态
+			couponUser = couponStatusRecord(payRequest.getCouponExCode(), payRequest.getCouponId(), payRequest.getUserId());
+		}
 		Long relationId = payRequest.getRelationId();
 		if (relationId == null || relationId <= 0 || payRequest.getSkuId() == null || payRequest.getNum() == null) {
 			throw new BusinessRuntimeException("缺少必填参数");
@@ -535,6 +591,25 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				}
 			}
 		}
+		orderDon.setRealMoney(orderDon.getMoney());
+		//存在优惠券
+		if (couponUser != null) {
+			Long couponId = couponUser.getCouponId();
+			Coupon coupon = couponMapper.getCouponById(couponId);
+			BigDecimal couponMoney = null;
+			if (coupon.getType() == Coupon.Type.discount) {
+				BigDecimal discountMoney = orderDon.getMoney().multiply(coupon.getDiscount());
+				orderDon.setMoney(discountMoney);
+				couponMoney = orderDon.getMoney().subtract(discountMoney);
+			} else if (coupon.getType() == Coupon.Type.reduce) {
+				BigDecimal subtractMoney = orderDon.getMoney().subtract(coupon.getReduceMoney());
+				orderDon.setMoney(subtractMoney);
+				couponMoney = orderDon.getMoney().subtract(subtractMoney);
+			}
+			orderDon.setCouponMoney(couponMoney);
+			orderDon.setCouponUserId(couponUser.getId());
+		}
+
 		this.saveOrUpdate(orderDon);
 
 		jobManager.addJob(EXPIRY_TIME, () -> {
@@ -658,6 +733,11 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		});
 		//虚物订单退款,清出车队,车票变为已过期
 		clearUnrealGoodsDetail(orderDon, goodsDon);
+		//若存在优惠券使用,退还优惠券
+		if (orderDon.getCouponUserId() != 0) {
+			redisService.set(String.format("%s-%s", orderDon.getId(), orderDon.getCouponUserId()), orderDon.getId(), 120l);
+			updateOrderDonCouponStatus(orderDon);
+		}
 		return GatewayResponse.SUCCESS.newBuilder().toResult("退款成功");
 	}
 
@@ -725,6 +805,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			});
 			//虚物订单退款,清出车队,车票变为已过期
 			clearUnrealGoodsDetail(orderDon, goodsDon);
+			if (orderDon.getCouponUserId() != 0) {
+				//zfb订单退款,返还优惠券
+				updateOrderDonCouponStatus(orderDon);
+			}
 			return GatewayResponse.SUCCESS.newBuilder().toResult("退款成功");
 		}
 		throw BusinessRuntimeException.getInstance("退款失败,e:{0}", response.getBody());
@@ -841,6 +925,12 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				distributeWaitingSendPointsMapper.updateOrderStatus(orderDon.getId(), DistributeWaitingSendPoints.Status.close);
 			}
 		}
+		if (orderDon.getCouponUserId() != 0) {
+			Object obj = redisService.get(String.format("%s-%s", orderDon.getId(), orderDon.getCouponUserId()));
+			if (obj == null) {
+				updateOrderDonCouponStatus(orderDon);
+			}
+		}
 		log.info("微信退款: 退款单号[{}] 退款成功.", outRefundNo);
 	}
 
@@ -1065,4 +1155,140 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			}
 		}
 	}
+
+	public void checkCouponStatus(Long skuId, String couponExCode, Long couponId, Long userId, Boolean isRenew) throws Exception {
+		log.info("开始校验兑换码:{},优惠券id:{}状态", couponExCode, couponId);
+		//实物还是虚物商品
+		GoodsDonSku goodsDonSku = goodsDonSkuMapper.selectById(skuId);
+		GoodsDon goodsDon = goodsDonMapper.selectById(goodsDonSku.getGoodsId());
+		if (StrUtil.isNotBlank(couponExCode) && couponId != null) {
+			throw BusinessRuntimeException.getInstance("优惠券与优惠码不可同时使用..");
+		}
+		if (StrUtil.isNotBlank(couponExCode)) {
+			CouponPopularizeDto exists = couponMapper.checkExCode(couponExCode);
+			if (exists == null) {
+				throw BusinessRuntimeException.getInstance("该优惠码不存在");
+			}
+			checkCouponGoods(goodsDon, skuId, exists.getType(), exists.getGoodsSkuIds(), true);
+			//检验是否续费可使用
+			if (isRenew && !exists.getIsRenew()) {
+				throw BusinessRuntimeException.getInstance("该优惠券续费时不可使用");
+			}
+			if (exists.getScope() == Coupon.Scope.newUser) {
+				CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
+				if (couponNewUserView != null) {
+					throw BusinessRuntimeException.getInstance("该新用户优惠码有且仅可使用一次");
+				}
+			}
+			if (exists.getIsValidTime() && exists.getValidEndTime().before(DateTime.now())) {
+				throw BusinessRuntimeException.getInstance("该优惠码已失效");
+			}
+			if (exists.getIsValidTime() && exists.getValidStartTime().after(DateTime.now())) {
+				throw BusinessRuntimeException.getInstance("该优惠码待生效");
+			}
+		} else if (couponId != null) {
+			CouponUserView couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
+					.field(CouponUserView::getCouponId, couponId).op(Operator.Equal)
+					.field(CouponUserView::getUserId, userId).op(Operator.Equal)
+					.build());
+			if (couponUserView == null) {
+				throw BusinessRuntimeException.getInstance("该优惠券不存在");
+			}
+			if (couponUserView.getCouponUserStatus() == CouponUser.Status.used) {
+				throw BusinessRuntimeException.getInstance("该优惠券已使用");
+			}
+			couponFontService.setCouponUserStatus(couponUserView, DateTime.now());
+			if (couponUserView.getCouponUserStatus() != CouponUser.Status.unused) {
+				throw BusinessRuntimeException.getInstance(couponUserView.getReason());
+			}
+			checkCouponGoods(goodsDon, skuId, couponUserView.getType(), couponUserView.getGoodsSkuIds(), false);
+			//检验是否续费可使用
+			if (isRenew && !couponUserView.getIsRenew()) {
+				throw BusinessRuntimeException.getInstance("该优惠券续费时不可使用");
+			}
+			if (couponUserView.getScope() == Coupon.Scope.newUser) {
+				CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
+				if (couponNewUserView != null) {
+					throw BusinessRuntimeException.getInstance("该新用户优惠券有且仅可使用一次");
+				}
+			}
+		}
+	}
+
+	private CouponUser couponStatusRecord(String couponExCode, Long couponId, Long userId) {
+		log.info("兑换码:{},优惠券id:{},更新其优惠券状态", couponExCode, couponId);
+		CouponUser couponUser = null;
+		if (StrUtil.isNotBlank(couponExCode)) {
+			CouponPopularizeDto exists = couponMapper.checkExCode(couponExCode);
+			if (exists == null) {
+				throw BusinessRuntimeException.getInstance("该优惠码不存在");
+			}
+			couponUser = new CouponUser();
+			couponUser.setCouponId(exists.getCouponId());
+			if (exists.getPopularizeId() != null) {
+				couponUser.setCouponId(exists.getPopularizeCouponId());
+				couponUser.setPopularizeId(exists.getPopularizeId());
+			}
+			CouponUser received = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponUser.getCouponId()).last("limit 1"));
+			if (received != null && received.getStatus() != CouponUser.Status.unused) {
+				throw BusinessRuntimeException.getInstance("您已使用该优惠码所对应的优惠券");
+			}
+			couponUser.setUserId(userId);
+			couponUser.setStatus(CouponUser.Status.used);
+			couponUser.setChannel(CouponUser.Channel.exCode);
+			couponFontService.updateCouponUserValidTime(couponUser.getCouponId(), couponUser);
+			try {
+				couponUserMapper.insert(couponUser);
+			} catch (DuplicateKeyException e) {
+				log.info("用户已领取该{}优惠券", couponUser.getCouponId());
+				throw BusinessRuntimeException.getInstance("您已领取该优惠码对应的优惠券,请使用优惠券");
+			}
+		} else {
+			couponUser = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponId).last("limit 1"));
+			if (couponUser == null) {
+				throw BusinessRuntimeException.getInstance("该优惠券不存在");
+			}
+			couponUser.setStatus(CouponUser.Status.used);
+			couponUserMapper.updateById(couponUser);
+		}
+		return couponUser;
+	}
+
+	/**
+	 * 退款时,归还优惠券
+	 */
+	private void updateOrderDonCouponStatus(OrderDon orderDon) {
+		log.info("订单:{}退款,优惠券返还start...", orderDon.getId());
+		CouponUser couponUser = couponUserMapper.selectById(orderDon.getCouponUserId());
+		if (CouponUser.Channel.exCode == couponUser.getChannel()) {
+			//兑换码兑换的优惠券退款时,直接删除
+			couponUserMapper.deleteById(couponUser.getId());
+		}else {
+			CouponUserView couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
+					.field(CouponUserView::getCouponId, couponUser.getCouponId()).op(Operator.Equal)
+					.field(CouponUserView::getUserId, orderDon.getUserId()).op(Operator.Equal)
+					.build());
+			couponUserView.setCouponUserStatus(CouponUser.Status.unused);
+			couponFontService.setCouponUserStatus(couponUserView, DateTime.now());
+			couponUser.setStatus(couponUserView.getCouponUserStatus());
+			couponUserMapper.updateById(couponUser);
+		}
+		log.info("订单:{}退款,优惠券返还end...", orderDon.getId());
+	}
+
+	public void checkCouponGoods(GoodsDon goodsDon, Long skuId, Coupon.Type type, String goodsSkuIds, Boolean isCouponCode) throws Exception {
+		if (goodsDon.getType() == 1) {
+			List<Coupon.CouponGoodsSku> skuIds = Jsons.parseList(goodsSkuIds, Coupon.CouponGoodsSku.class);
+			Set<Long> collect = skuIds.stream().map(Coupon.CouponGoodsSku::getSkuId).collect(Collectors.toSet());
+			if (!collect.contains(skuId)) {
+				throw BusinessRuntimeException.getInstance(String.format("该%s不适用于该规格", isCouponCode ? "优惠码" : "优惠券"));
+			}
+		} else {
+			Long gid = goodsDon.getId();
+			CouponSku couponSku = couponSkuMapper.selectOne(Wrappers.lambdaQuery(CouponSku.class).eq(CouponSku::getGoodsId, gid).eq(CouponSku::getDeleted, true).select(CouponSku::getId).last("limit 1"));
+			if (couponSku == null) {
+				throw BusinessRuntimeException.getInstance(String.format("该%s不可用于实物商品", isCouponCode ? "优惠码" : "优惠券"));
+			}
+		}
+	}
 }

+ 2 - 2
netflix-web/src/main/java/com/cyksj/web/aop/NoSubmitAop.java

@@ -2,7 +2,7 @@ package com.cyksj.web.aop;
 
 import cn.hutool.extra.servlet.ServletUtil;
 import com.cyksj.common.annotation.NoSubmit;
-import com.cyksj.enums.GatewayResponse;
+import com.cyksj.common.exception.BusinessRuntimeException;
 import com.google.common.cache.Cache;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -41,7 +41,7 @@ public class NoSubmitAop {
 			return pjp.proceed();
 		} else {
 			logger.error("重复提交");
-			return GatewayResponse.FAIL.newBuilder().toResult("请勿重复提交");
+			throw BusinessRuntimeException.getInstance("请勿重复提交");
 		}
 	}
 

+ 230 - 0
netflix-web/src/main/java/com/cyksj/web/controller/coupon/CouponFrontController.java

@@ -0,0 +1,230 @@
+package com.cyksj.web.controller.coupon;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.common.annotation.NoSubmit;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
+import com.cyksj.mapper.manage.coupon.CouponRecommendMapper;
+import com.cyksj.mapper.manage.coupon.CouponSkuMapper;
+import com.cyksj.mapper.manage.coupon.CouponUserMapper;
+import com.cyksj.model.dto.CouponPopularizeDto;
+import com.cyksj.model.entity.Coupon;
+import com.cyksj.model.request.CouponCollectReq;
+import com.cyksj.model.views.*;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.coupon.CouponFontService;
+import com.cyksj.web.util.StpUserUtil;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/*
+ *项目名: netflix
+ *文件名: CouponFrontController
+ *创建者: JavaZou
+ *创建时间:2022/11/18 10:56
+ */
+@RestController
+@RequestMapping("/applet/coupon")
+@RequiredArgsConstructor
+public class CouponFrontController {
+	private final CouponUserMapper couponUserMapper;
+
+	private final BeanSearcher beanSearcher;
+
+	private final CouponFontService couponFontService;
+
+	private final RedisService redisService;
+
+	private final CouponSkuMapper couponSkuMapper;
+
+	private final CouponMapper couponMapper;
+
+	private final CouponRecommendMapper couponRecommendMapper;
+
+	/**
+	 * 优惠券领取中心列表
+	 */
+	@GetMapping("/center/list")
+	public Result<Page<CouponActiveView>> couponCenterList(@RequestParam(defaultValue = "1") Long start, @RequestParam(defaultValue = "5") Long limit) {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		Page<CouponActiveView> page = couponUserMapper.couponCenterList(userId, new Page<>(start, limit));
+		page.getRecords().forEach(data -> {
+			String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId());
+			data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(page);
+	}
+
+	/**
+	 * 个人优惠券列表
+	 */
+	@GetMapping("/personal/list")
+	public Result<Page<CouponUserView>> couponPersonalList(@RequestParam(defaultValue = "1") Long start, @RequestParam(defaultValue = "5") Long limit) {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		Page<CouponUserView> page = couponUserMapper.couponPersonalList(userId, new Page<>(start, limit));
+		page.getRecords().forEach(data -> {
+			String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId());
+			data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(page);
+	}
+
+	/**
+	 * 可用优惠券展示数量
+	 */
+	@GetMapping("/available/num")
+	public Result<Integer> availableCouponNum(Long skuId, Boolean isRenew) {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		Long goodsId = null;
+		//实物还是虚物
+		GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getSkuId, skuId).op(Operator.Equal).build());
+		if (goodsDonSkuView == null) {
+			throw BusinessRuntimeException.getInstance("规格不存在");
+		}
+		if (goodsDonSkuView.getType() == 2) {
+			goodsId = goodsDonSkuView.getGoodsId();
+		}
+		Integer count = couponUserMapper.countAvailableCouponList(goodsId, skuId, userId, isRenew);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(count);
+	}
+
+	@GetMapping("/available/list")
+	public Result<Page<CouponUserView>> availableCouponList(Long skuId, Long couponId, Boolean isRenew, @RequestParam(defaultValue = "1") Long start, @RequestParam(defaultValue = "5") Long limit) {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		Page<CouponUserView> couponActiveViewList = couponFontService.getAvailableCouponList(skuId, couponId, userId, isRenew, start, limit);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(couponActiveViewList);
+	}
+
+	/**
+	 * 推荐优惠券列表
+	 */
+	@GetMapping("/recommend/list")
+	public Result<Page<CouponRecommendView>> getRecommendList(@RequestParam(defaultValue = "1") Long start, @RequestParam(defaultValue = "5") Long limit) {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
+		Boolean isNewUser = true;
+		if (couponNewUserView != null) {
+			isNewUser = false;
+		}
+		Page<CouponRecommendView> page = couponRecommendMapper.getRecommendCouponViewList(userId, isNewUser, new Page<>(start, limit));
+		page.getRecords().forEach(data -> {
+			if (data.getCouponStatus() == CouponActiveView.CouponStatus.received) {
+				data.setValidStartTime(data.getUserValidStartTime());
+				data.setValidEndTime(data.getUserValidEndTime());
+			}
+			String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId());
+			data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(page);
+	}
+
+	/**
+	 * 一键领取推荐优惠券
+	 */
+	@PostMapping("/collect/recommend/whole")
+	@NoSubmit
+	public Result<String> collectWholeRecommendCoupon() {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		couponFontService.collectWholeRecommendCoupon(userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("领取成功");
+	}
+
+	/**
+	 * 今日推荐优惠券弹窗已弹
+	 */
+	@GetMapping("/get/recommend/appear")
+	public Result<Boolean> recommendIsAppear() {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		String zero = DateUtil.beginOfDay(DateTime.now()).toString();
+		Object exists = redisService.get(RedisService.key.RECOMMEND_COUPON_DAY.getNameFormat(String.format("%s-%s", zero, userId)));
+		if (exists == null) {
+			redisService.set(RedisService.key.RECOMMEND_COUPON_DAY.getNameFormat(String.format("%s-%s", zero, userId)), userId);
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult(exists == null ? false : true);
+	}
+
+	/**
+	 * 领取优惠券
+	 */
+	@PostMapping("/collect")
+	public Result<String> collectCoupon(@RequestBody CouponCollectReq couponCollectReq) {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		couponFontService.collectCoupon(couponCollectReq, userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("领取优惠券成功");
+	}
+
+	/**
+	 * 领取推荐优惠券
+	 */
+	@PostMapping("/recommend/collect")
+	public Result<String> collectRecommendCoupon(@RequestBody CouponCollectReq couponCollectReq) {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		couponFontService.collectRecommendCoupon(couponCollectReq, userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("领取优惠券成功");
+	}
+
+	/**
+	 * 查询有可以领取的推荐优惠券
+	 */
+	@GetMapping("/recommend/collect/num")
+	public Result<Integer> collectRecommendCouponNum() {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
+		Boolean isNewUser = true;
+		if (couponNewUserView != null) {
+			isNewUser = false;
+		}
+		Integer num = couponFontService.collectRecommendCouponNum(userId, isNewUser);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(num);
+	}
+
+
+	/**
+	 * 优惠码详情
+	 */
+	@GetMapping("/get/exCode/detail")
+	public Result<CouponPopularizeDto> getExCodeDetail(Long skuId, String exCode) throws Exception {
+		Long userId = StpUserUtil.getLoginIdAsLong();
+		GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getSkuId, skuId).op(Operator.Equal).build());
+		if (goodsDonSkuView == null) {
+			throw BusinessRuntimeException.getInstance("该规格不存在");
+		}
+		if (goodsDonSkuView.getType() == 2) {
+			throw BusinessRuntimeException.getInstance("实物不支持使用优惠码");
+		}
+		CouponPopularizeDto dto = couponMapper.checkExCode(exCode);
+		if (dto == null) {
+			throw BusinessRuntimeException.getInstance("该优惠码不存在");
+		}
+		if (dto.getScope() == Coupon.Scope.newUser) {
+			CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
+			if (couponNewUserView != null) {
+				throw BusinessRuntimeException.getInstance("该优惠码只适用于新用户");
+			}
+		}
+		if (dto.getIsValidTime() && dto.getValidStartTime().after(DateTime.now())) {
+			throw BusinessRuntimeException.getInstance("该优惠码待生效");
+		}
+		if (dto.getIsValidTime() && dto.getValidEndTime().before(DateTime.now())) {
+			throw BusinessRuntimeException.getInstance("该优惠码已失效");
+		}
+		List<Coupon.CouponGoodsSku> skuIds = Jsons.parseList(dto.getGoodsSkuIds(), Coupon.CouponGoodsSku.class);
+		Set<Long> collect = skuIds.stream().map(Coupon.CouponGoodsSku::getSkuId).collect(Collectors.toSet());
+		if (!collect.contains(skuId)) {
+			throw BusinessRuntimeException.getInstance("该优惠码不适用于该规格");
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult(dto);
+	}
+}

+ 1 - 1
netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java

@@ -62,7 +62,7 @@ public class GroupRelationController {
     }
 
     @PostMapping("/post/renewal")
-    public Result<OrderDon> renewal(@RequestBody OrderPayRequest request) {
+    public Result<OrderDon> renewal(@RequestBody OrderPayRequest request) throws Exception {
         long userId = StpUserUtil.getLoginIdAsLong();
         request.setUserId(userId);
 

+ 8 - 1
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java

@@ -6,6 +6,7 @@ import com.cyksj.common.annotation.NoSubmit;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
 import com.cyksj.mapper.RealGoodsInterestUserMapper;
 import com.cyksj.mapper.manage.RealGoodsOrderHandleMapper;
 import com.cyksj.model.entity.OrderDon;
@@ -21,7 +22,6 @@ import com.cyksj.model.views.OrderDonTransportView;
 import com.cyksj.model.views.RealGoodsInterestUserView;
 import com.cyksj.service.mange.CmsOrderDonService;
 import com.cyksj.service.user.UserService;
-import com.cyksj.web.util.EasyExcelUtils;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.param.Operator;
@@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Optional;
 
@@ -60,6 +61,8 @@ public class CmsOrderController {
 
     private final RealGoodsOrderHandleMapper realGoodsOrderHandleMapper;
 
+    private final CouponMapper couponMapper;
+
 
     @GetMapping("/get")
     public Result<SearchResult<OrderDonView>> get() {
@@ -72,6 +75,10 @@ public class CmsOrderController {
                     data.setAfterServiceName(realGoodsOrderHandle.getAfterServiceName());
                 }
             }
+            if (data.getCouponUserId() != 0) {
+                data.setCouponName(couponMapper.getCouponNameByCouponUserId(data.getCouponUserId()));
+                data.setCouponMoney(data.getCouponMoney() == null ? BigDecimal.ZERO : data.getCouponMoney());
+            }
         });
         return GatewayResponse.SUCCESS.newBuilder().toResult(search);
     }

+ 294 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/coupon/CouponController.java

@@ -0,0 +1,294 @@
+package com.cyksj.web.controller.manage.coupon;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.manage.coupon.*;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.request.CorpCouponSendReq;
+import com.cyksj.model.request.CouponActiveReq;
+import com.cyksj.model.request.CouponDistributeReq;
+import com.cyksj.model.views.CouponActiveView;
+import com.cyksj.model.views.CouponDistributePopularizeView;
+import com.cyksj.model.views.CouponRecommendView;
+import com.cyksj.model.views.CouponView;
+import com.cyksj.service.mange.coupon.CouponCommonService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.SearchResult;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.Min;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/*
+ *项目名: netflix
+ *文件名: CouponController
+ *创建者: JavaZou
+ *创建时间:2022/11/17 13:44
+ */
+@RestController
+@RequestMapping("/manage/coupon")
+@RequiredArgsConstructor
+public class CouponController {
+	private final CouponCommonService couponCommonService;
+
+	private final CouponMapper couponMapper;
+
+	private final BeanSearcher beanSearcher;
+
+	private final CouponRecommendMapper couponRecommendMapper;
+
+	private final CouponDistributePopularizeMapper couponDistributePopularizeMapper;
+
+	private final CouponActiveMapper couponActiveMapper;
+
+	private final CouponUserMapper couponUserMapper;
+
+	private final CouponSkuMapper couponSkuMapper;
+
+	/**
+	 * 新增优惠券
+	 */
+	@PostMapping
+	public Result<String> couponSave(@RequestBody @Validated Coupon coupon) throws Exception {
+		couponCommonService.saveCoupon(coupon);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("新增优惠券成功");
+	}
+
+	/**
+	 * 删除优惠券
+	 */
+	@DeleteMapping("/{id}")
+	@Transactional(rollbackFor = Throwable.class)
+	public Result<String> couponSave(@PathVariable Long id) throws Exception {
+		couponMapper.deleteById(id);
+		couponSkuMapper.deleteByCid(id);
+		couponActiveMapper.deleteByCouponId(id);
+		couponRecommendMapper.deleteByCouponId(id);
+		couponDistributePopularizeMapper.deleteByCouponId(id);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("删除优惠券成功");
+	}
+
+	/**
+	 * 修改优惠券
+	 */
+	@PutMapping
+	public Result<String> putCoupon(@RequestBody @Validated Coupon coupon) throws Exception {
+		couponCommonService.updateCoupon(coupon);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("修改优惠券成功");
+	}
+
+	/**
+	 * 发放优惠券
+	 */
+	@PostMapping("/send")
+	public Result<String> senCoupon(@RequestBody @Validated CouponActiveReq couponActiveReq) throws Exception {
+		couponCommonService.sendCoupon(couponActiveReq);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("发放优惠券成功");
+	}
+
+	/**
+	 * 优惠券 设置为推荐优惠券
+	 */
+	@PutMapping("/set/hot/{couponId}")
+	public Result<String> setHotCoupon(@PathVariable @Min(value = 1) Long couponId) {
+		couponCommonService.setHotCoupon(couponId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("推荐优惠券成功");
+	}
+
+	/**
+	 * 优惠券关联固定推广者
+	 */
+	@PostMapping("/set/relation")
+	public Result<String> setPopularizeRelation(@RequestBody CouponDistributeReq couponDistributeReq) {
+		couponCommonService.setPopularizeRelation(couponDistributeReq);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("优惠券关联固定推广者成功");
+	}
+
+	/**
+	 * 设置固定推广者兑换码
+	 */
+	@PutMapping("/set/relation/exCode")
+	public Result<String> setPopularizeRelationCode(@RequestBody CouponDistributePopularize couponDistributePopularize) {
+		couponCommonService.setPopularizeRelationCode(couponDistributePopularize);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("设置固定推广者兑换码成功");
+	}
+
+	/**
+	 * 关联某优惠券列表
+	 */
+	@GetMapping("/get/relation/list")
+	public Result<SearchResult<CouponDistributePopularizeView>> getRelationList(HttpServletRequest request) {
+		SearchResult<CouponDistributePopularizeView> search = beanSearcher.search(CouponDistributePopularizeView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		search.getDataList().forEach(data -> {
+			List<CouponUser> couponUsers = couponUserMapper.selectList(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getPopularizeId, data.getId()).select(CouponUser::getId, CouponUser::getStatus));
+			Optional.ofNullable(couponUsers).ifPresentOrElse(list -> {
+				Map<CouponUser.Status, List<CouponUser>> listMap = list.stream().collect(Collectors.groupingBy(CouponUser::getStatus));
+				List<CouponUser> used = listMap.get(CouponUser.Status.used);
+				data.setUsedNum(used == null ? 0 : used.size());
+			}, () -> {
+				data.setUsedNum(0);
+			});
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 删除固定推广者关联
+	 */
+	@DeleteMapping("/relation/del/{relationId}")
+	public Result<String> deleteRelationById(@PathVariable Long relationId) {
+		couponDistributePopularizeMapper.deleteById(relationId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("删除推广者关联成功");
+	}
+
+	/**
+	 * 优惠券列表
+	 */
+	@GetMapping
+	public Result<SearchResult<CouponView>> couponList(HttpServletRequest request) {
+		SearchResult<CouponView> search = beanSearcher.search(CouponView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		search.getDataList().forEach(data -> {
+			data.setRelationNum(couponDistributePopularizeMapper.selectCount(Wrappers.lambdaQuery(CouponDistributePopularize.class).eq(CouponDistributePopularize::getCouponId, data.getId())));
+			String str = couponSkuMapper.selectGoodsSkuStr(data.getId());
+			data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 优惠券发放列表
+	 */
+	@GetMapping("/active/list")
+	public Result<SearchResult<CouponActiveView>> couponActiveList(HttpServletRequest request) {
+		SearchResult<CouponActiveView> search = beanSearcher.search(CouponActiveView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		search.getDataList().forEach(data -> {
+			List<CouponUser> couponUsers = couponUserMapper.selectList(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getCouponActiveId, data.getId()).select(CouponUser::getId, CouponUser::getStatus));
+			String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId());
+			data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+			Optional.ofNullable(couponUsers).ifPresentOrElse(list -> {
+				data.setReceiptNum(list.size());
+				Map<CouponUser.Status, List<CouponUser>> listMap = list.stream().collect(Collectors.groupingBy(CouponUser::getStatus));
+				List<CouponUser> used = listMap.get(CouponUser.Status.used);
+				data.setUsedNum(used == null ? 0 : used.size());
+			}, () -> {
+				data.setReceiptNum(0);
+				data.setUsedNum(0);
+			});
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 发放优惠券停用
+	 */
+	@PutMapping("/active/putStatus/{couponActiveId}")
+	public Result<String> activePutStatus(@PathVariable Long couponActiveId) {
+		CouponActive couponActive = couponActiveMapper.selectById(couponActiveId);
+		if (couponActive == null || !couponActive.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("发放记录已不存在");
+		}
+		if (!couponActive.getStatus()) {
+			throw BusinessRuntimeException.getInstance("该发放优惠券已停用");
+		}
+		couponActive.setStatus(false);
+		couponActiveMapper.updateById(couponActive);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("停用发放优惠券成功");
+	}
+
+	/**
+	 * 发放优惠券失效
+	 */
+	@PutMapping("/active/putExpiryStatus/{couponActiveId}")
+	public Result<String> activePutExpiryStatus(@PathVariable Long couponActiveId) {
+		CouponActive couponActive = couponActiveMapper.selectById(couponActiveId);
+		if (couponActive == null || !couponActive.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("发放记录已不存在");
+		}
+		if (!couponActive.getExpiryStatus()) {
+			throw BusinessRuntimeException.getInstance("该发放优惠券已失效");
+		}
+		couponActive.setExpiryStatus(false);
+		couponActive.setStatus(false);
+		couponActiveMapper.updateById(couponActive);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("失效发放优惠券成功");
+	}
+
+
+	/**
+	 * 推荐发放列表
+	 */
+	@GetMapping("/recommend/list")
+	public Result<SearchResult<CouponRecommendView>> couponRecommendList(HttpServletRequest request) {
+		SearchResult<CouponRecommendView> search = beanSearcher.search(CouponRecommendView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		search.getDataList().forEach(data -> {
+			List<CouponUser> couponUsers = couponUserMapper.selectList(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getRecommendId, data.getId()).select(CouponUser::getId, CouponUser::getStatus));
+			String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId());
+			data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+			Optional.ofNullable(couponUsers).ifPresentOrElse(list -> {
+				data.setReceiptNum(list.size());
+				Map<CouponUser.Status, List<CouponUser>> listMap = list.stream().collect(Collectors.groupingBy(CouponUser::getStatus));
+				List<CouponUser> used = listMap.get(CouponUser.Status.used);
+				data.setUsedNum(used == null ? 0 : used.size());
+			}, () -> {
+				data.setReceiptNum(0);
+				data.setUsedNum(0);
+			});
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 推荐优惠券设置/取消推荐
+	 */
+	@PutMapping("/recommend/putStatus/{recommendId}")
+	public Result<String> putRecommendStatusById(@PathVariable Long recommendId) {
+		CouponRecommend couponRecommend = couponRecommendMapper.selectById(recommendId);
+		if (couponRecommend == null || !couponRecommend.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该推荐优惠券不存在");
+		}
+		couponRecommend.setStatus(!couponRecommend.getStatus());
+		couponRecommendMapper.updateById(couponRecommend);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("操作成功");
+	}
+
+	/**
+	 * 删除推荐优惠券
+	 */
+	@DeleteMapping("/recommend/del/{recommendId}")
+	public Result<String> deleteRecommendById(@PathVariable Long recommendId) {
+		CouponRecommend couponRecommend = couponRecommendMapper.selectById(recommendId);
+		if (couponRecommend == null || !couponRecommend.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("该推荐优惠券不存在");
+		}
+		if (couponRecommend.getStatus()) {
+			throw BusinessRuntimeException.getInstance("取消推荐优惠券,才可删除");
+		}
+		couponRecommendMapper.deleteById(recommendId);
+		Coupon coupon = couponMapper.selectById(couponRecommend.getCouponId());
+		if (coupon != null && coupon.getDeleted()) {
+			coupon.setHotStatus(false);
+			couponMapper.updateById(coupon);
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult("删除推荐优惠券成功");
+	}
+
+	/**
+	 * 企业微信发放优惠券
+	 */
+	@PostMapping("/corp/send")
+	public Result<String> corpSendCoupon(@RequestBody @Validated CorpCouponSendReq corpCouponSendReq) throws Exception {
+		couponCommonService.corpSendCoupon(corpCouponSendReq);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("发放优惠券成功");
+	}
+
+}

+ 9 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/CmsGoodsDonController.java

@@ -55,6 +55,15 @@ public class CmsGoodsDonController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(goodsDonViewer);
     }
 
+    /**
+     * 商品规格详情
+     */
+    @GetMapping("/get/sku/{id}")
+    public Result<List<GoodsDonSku>> getSkuById(@PathVariable Long id, HttpServletRequest request) {
+        List<GoodsDonSku> goodsDonSkus = beanSearcher.searchAll(GoodsDonSku.class, MapUtils.flatBuilder(request.getParameterMap()).field(GoodsDonSku::getGoodsId, id).build());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(goodsDonSkus);
+    }
+
     /**
      * 新增 商品spu
      */

+ 17 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/statistics/StatisticsDataController.java

@@ -2,10 +2,14 @@ package com.cyksj.web.controller.manage.statistics;
 
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
+import com.alibaba.excel.support.ExcelTypeEnum;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.excel.ExcelAccountData;
+import com.cyksj.model.views.BackgroundDataView;
+import com.cyksj.model.views.ExpiredAccountDataView;
 import com.cyksj.mapper.GroupsRelationMapper;
 import com.cyksj.mapper.UserMapper;
 import com.cyksj.mapper.manage.CustomerServiceEveryMonthDetailMapper;
@@ -19,6 +23,7 @@ import com.cyksj.model.entity.StatisticsUserData;
 import com.cyksj.model.request.RenewTimeUpReq;
 import com.cyksj.model.views.*;
 import com.cyksj.service.mange.statistics.StatisticsDataService;
+import com.cyksj.web.util.EasyExcelUtils;
 import com.cyksj.task.OrderCustomerHandleScheduler;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
@@ -29,6 +34,9 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
 import javax.servlet.http.HttpServletRequest;
 import java.util.ArrayList;
 import java.util.List;
@@ -80,6 +88,15 @@ public class StatisticsDataController {
 		return GatewayResponse.SUCCESS.newBuilder().toResult(statisticsDataService.getExpiredAccountView(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, start, limit));
 	}
 
+	/**
+	 * 导出有效子账号数
+	 */
+	@GetMapping("/export/account")
+	public void exportAccount(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, HttpServletResponse response) {
+		List<ExcelAccountData> expiredAccountView = statisticsDataService.getExpiredAccountViewNoPage(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime);
+		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelAccountData.class, expiredAccountView, "有效子账号", "有效子账号", ExcelTypeEnum.XLSX, null);
+	}
+
 	/**
 	 * 到期状态续费意愿修改
 	 */