Selaa lähdekoodia

add 优惠券功能

add 优惠卷代码

fix initial

fix

add 优惠卷接口

add 优惠卷接口

add 优惠卷服务端

add 优惠卷服务端

add 优惠卷服务端

fix 优惠卷

fix 立减卷校验规格

fix 优惠卷

fix 商品sku列表接口

fix 可用优惠卷;续费时使用校验

fix

fix 推荐优惠卷

fix 优惠卷前端展示

fix 停用优惠卷不展示

fix 优惠码详情

fix 优惠卷不可用原因

fix

fix 企业微信优惠卷发放

fix

fix 非空

fix

fix

fix 领取优惠卷

fix 重复提交返回信息

fix

fix 可用优惠卷展示

fix 实物可用优惠卷

fix 查询是否可以领取推荐优惠卷

fix

fix 个人优惠卷列表展示

fix

fix 实物

fix 变量位置

fix 物理删除

fix

fix 兼容实物虚物

fix 优惠卷优惠金额小于最小规格金额

fix

fix 折扣

fix 优惠券名称;优惠卷下单

fix 优惠卷排序

fix

fix 推荐可领取数量

fix 一键领取推荐优惠卷

fix 优惠卷退款

fix 删除优惠码兑换的优惠劵

fix 不展示优惠码优惠券

fix

fix 领取过后的优惠券不展示在列表

fix

fix 个人优惠卷列表

fix 推荐

fix

fix 过期优惠卷不展示

fix 小于

fix 排序

fix 实物返还优惠卷

fix 去重发送优惠券;过滤掉未生效优惠券

fix

fix

fix 优惠券

fix 未生效优惠卷不展示

fix

fix 忽略逻辑删除优惠卷

fix 过滤可用优惠券

fix reason

fix

fix

fix 优惠码

fix

fix 优惠券

fix

fix

fix

fix 优惠券失效

fix

fix 排序

fix 优惠码新用户校验

fix 订单优惠券信息

fix

fix 可用

fix

fix 推广

fix

fix 新用户优惠券

fix 座位号校验

fix

fix

fix

fix 优惠券规格描述

fix 条件

add 添加有效子账号导出
zoujiajian 3 vuotta sitten
vanhempi
sitoutus
f2e6804ae4
57 muutettua tiedostoa jossa 3427 lisäystä ja 24 poistoa
  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 12
      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. 10 7
      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. 15 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.ExpiredAccountDataView;
 import com.cyksj.redis.RedisService;
@@ -97,6 +98,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

@@ -23,7 +23,7 @@ import java.util.Map;
  */
 public interface OrderDonService extends IService<OrderDon> {
 
-    OrderDon submit(OrderPayRequest request);
+    OrderDon submit(OrderPayRequest request) throws Exception;
 
     /**
      * 订单支付
@@ -45,7 +45,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 - 12
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -34,10 +34,10 @@ import com.cyksj.dto.RedisKey;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.*;
-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;
@@ -45,24 +45,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;
@@ -120,10 +123,23 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final RefundOrderDonMapper refundOrderDonMapper;
 
+	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());
@@ -202,7 +218,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());
@@ -210,9 +247,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		orderDon.setStatus(OrderDon.Status.noPayment);
 		orderDon.setUserId(user.getId());
 		orderDon.setType(OrderDon.Type.getType(payRequest.getType()));
+		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());
 		}
@@ -392,6 +434,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 					}
 				}
 			}
+			//返还优惠券
+			if (orderDon.getCouponUserId() != 0) {
+				updateOrderDonCouponStatus(orderDon);
+			}
 		}
 	}
 
@@ -422,9 +468,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("缺少必填参数");
@@ -469,6 +521,25 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		orderDon.setStatus(OrderDon.Status.noPayment);
 		orderDon.setUserId(user.getId());
 		orderDon.setGoodsId(goodsDon.getId());
+		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, () -> {
@@ -586,6 +657,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("退款成功");
 	}
 
@@ -650,6 +726,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());
@@ -765,6 +845,12 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			orderDon.setRefundMoney(refundMoney);
 			orderDonMapper.updateById(orderDon);
 		}
+		if (orderDon.getCouponUserId() != 0) {
+			Object obj = redisService.get(String.format("%s-%s", orderDon.getId(), orderDon.getCouponUserId()));
+			if (obj == null) {
+				updateOrderDonCouponStatus(orderDon);
+			}
+		}
 		log.info("微信退款: 退款单号[{}] 退款成功.", outRefundNo);
 	}
 
@@ -936,4 +1022,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);
 

+ 10 - 7
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java

@@ -1,30 +1,25 @@
 package com.cyksj.web.controller.manage;
 
-import cn.hutool.core.date.DateUtil;
-import com.alibaba.excel.support.ExcelTypeEnum;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
 import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.entity.OrderDonTransport;
 import com.cyksj.model.entity.OrderDonWaybill;
-import com.cyksj.model.excel.ExcelOrderDonInfo;
 import com.cyksj.model.manage.request.ReqSetLogisticsPost;
 import com.cyksj.model.manage.views.OrderDonView;
 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.util.MapUtils;
 import lombok.RequiredArgsConstructor;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.util.Date;
-import java.util.List;
+import java.math.BigDecimal;
 
 /**
  * @author chan
@@ -45,10 +40,18 @@ public class CmsOrderController {
 
     private final BeanSearcher beanSearcher;
 
+    private final CouponMapper couponMapper;
+
 
     @GetMapping("/get")
     public Result<SearchResult<OrderDonView>> get(){
         SearchResult<OrderDonView> search = beanSearcher.search(OrderDonView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+        search.getDataList().forEach(data->{
+            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
      */

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

@@ -1,15 +1,21 @@
 package com.cyksj.web.controller.manage.statistics;
 
+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.service.mange.statistics.StatisticsDataService;
+import com.cyksj.web.util.EasyExcelUtils;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
 /*
  *项目名: netflix
  *文件名: StatisticsDataController
@@ -41,6 +47,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);
+	}
+
 	/**
 	 * 到期状态续费意愿修改
 	 */