Преглед на файлове

Merge branch 'user_benefits'

# Conflicts:
#	netflix-common/src/main/java/com/cyksj/common/constant/Constant.java
#	netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
#	netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java
#	netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java
zoujiajian преди 1 година
родител
ревизия
6152a1c1cd
променени са 44 файла, в които са добавени 1527 реда и са изтрити 104 реда
  1. 5 0
      netflix-common/src/main/java/com/cyksj/common/constant/Constant.java
  2. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/UserBenefitsComboRelationRecordMapper.java
  3. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/UserBenefitsMapper.java
  4. 22 0
      netflix-dao/src/main/java/com/cyksj/mapper/UserBenefitsOrderRecordMapper.java
  5. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponUserMapper.java
  6. 8 0
      netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java
  7. 95 0
      netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefits.java
  8. 20 0
      netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefitsComboRelationRecord.java
  9. 32 0
      netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefitsOrderRecord.java
  10. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/UserPayEquipmentAvailableBenefits.java
  11. 2 1
      netflix-dao/src/main/java/com/cyksj/model/excel/ExcelOrderDonData.java
  12. 2 1
      netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonBackView.java
  13. 2 0
      netflix-dao/src/main/java/com/cyksj/model/request/AlipayOrderDonReq.java
  14. 5 0
      netflix-dao/src/main/java/com/cyksj/model/request/OrderPayRequest.java
  15. 3 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponUserView.java
  16. 6 0
      netflix-dao/src/main/java/com/cyksj/model/views/CouponView.java
  17. 3 0
      netflix-dao/src/main/java/com/cyksj/model/views/GiftCardGoodsSkuFrontView.java
  18. 12 0
      netflix-dao/src/main/java/com/cyksj/model/views/RenewalView.java
  19. 130 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserBenefitsFrontView.java
  20. 86 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserBenefitsView.java
  21. 47 0
      netflix-dao/src/main/resources/mapper/CouponUserMapper.xml
  22. 1 0
      netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml
  23. 40 0
      netflix-dao/src/main/resources/mapper/UserBenefitsOrderRecordMapper.xml
  24. 1 0
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  25. 0 26
      netflix-service/src/main/java/com/cyksj/service/exchange/impl/ExchangeCodeServiceImpl.java
  26. 5 0
      netflix-service/src/main/java/com/cyksj/service/mange/RefreshCacheService.java
  27. 5 1
      netflix-service/src/main/java/com/cyksj/service/mange/goods/CmsGoodsDonSpecServiceImpl.java
  28. 22 0
      netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java
  29. 6 0
      netflix-service/src/main/java/com/cyksj/service/mange/impl/RefreshCacheServiceImpl.java
  30. 2 0
      netflix-service/src/main/java/com/cyksj/service/order/OrderCommonService.java
  31. 5 4
      netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java
  32. 3 0
      netflix-service/src/main/java/com/cyksj/service/order/UserRealOrderBenefitsService.java
  33. 57 0
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderCommonServiceImpl.java
  34. 283 59
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  35. 54 0
      netflix-service/src/main/java/com/cyksj/service/order/impl/UserRealOrderBenefitsImpl.java
  36. 22 5
      netflix-service/src/main/java/com/cyksj/service/pay/impl/AliPayCommonServiceImpl.java
  37. 18 0
      netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationFrontServiceImpl.java
  38. 14 0
      netflix-service/src/main/java/com/cyksj/service/user/UserBenefitsBackService.java
  39. 30 0
      netflix-service/src/main/java/com/cyksj/service/user/impl/UserBenefitsBackServiceImpl.java
  40. 29 0
      netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java
  41. 22 2
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java
  42. 110 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/benefits/UserBenefitsController.java
  43. 44 5
      netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java
  44. 241 0
      netflix-web/src/main/java/com/cyksj/web/controller/user/UserBenefitsFrontController.java

+ 5 - 0
netflix-common/src/main/java/com/cyksj/common/constant/Constant.java

@@ -410,4 +410,9 @@ public interface Constant {
 	 * 小时
 	 */
 	String HOUR_DAY_OF_DATE = "hour";
+
+	/**
+	 * 奈飞设备平台id
+	 */
+	Long NF_EP_GID = 15l;
 }

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/UserBenefitsComboRelationRecordMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.UserBenefitsComboRelationRecord;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsComboRelationRecordMapper
+ * 创建者: JavaZou
+ * 创建时间:2024/9/29 10:43
+ */
+public interface UserBenefitsComboRelationRecordMapper extends BaseMapper<UserBenefitsComboRelationRecord> {
+}

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/UserBenefitsMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.UserBenefits;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsMapper
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 14:09
+ */
+public interface UserBenefitsMapper extends BaseMapper<UserBenefits> {
+}

+ 22 - 0
netflix-dao/src/main/java/com/cyksj/mapper/UserBenefitsOrderRecordMapper.java

@@ -0,0 +1,22 @@
+package com.cyksj.mapper;
+
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.UserBenefits;
+import com.cyksj.model.entity.UserBenefitsOrderRecord;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsOrderRecordMapper
+ * 创建者: JavaZou
+ * 创建时间:2024/9/24 15:49
+ */
+public interface UserBenefitsOrderRecordMapper extends BaseMapper<UserBenefitsOrderRecord> {
+	Integer selectUseComboRecordByUid(@Param("userIds") List<Long> userIdList, @Param("type") UserBenefits.Type type, @Param("goodsId") Long goodsId, @Param("skuId") Long skuId, @Param("orderStatus") List<String> orderStatus, @Param("lastYear") DateTime lastYear, @Param("startTime") Date startTime, @Param("relationId") Long relationId);
+
+	Integer selectNoPaymentRecord(@Param("userIds") List<Long> userIdList, @Param("benefitsId") Long benefitsId);
+}

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

@@ -35,4 +35,6 @@ public interface CouponUserMapper extends BaseMapper<CouponUser> {
 	CouponUserView getCouponDetailById(@Param("userId") long userId, @Param("couponId") Long couponId);
 
 	Map<String, Long> selectCouponUserNumData(Long couponId);
+
+	List<CouponUserView> couponPersonalByCouponIds(@Param("userIds") List<Long> userIds, @Param("couponIds") List<Long> couponIds);
 }

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

@@ -1,5 +1,6 @@
 package com.cyksj.model.entity;
 
+import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.ejlchina.searcher.bean.DbIgnore;
@@ -304,6 +305,10 @@ public class OrderDon extends BaseEntity implements Serializable {
     @JsonIgnore
     private OrderDonUserDiscountPopularizeRecord discountPopularizeRecord;
 
+    @TableField(exist = false)
+    @JsonIgnore
+    private Integer goodsDonType;
+
     public enum Status {
         /**
          * 订单状态
@@ -345,6 +350,9 @@ public class OrderDon extends BaseEntity implements Serializable {
         String desc;
 
         public static Type getType(String type) {
+            if (StrUtil.isEmpty(type)) {
+                return ALI_PAY;
+            }
             return Arrays.stream(Type.values()).filter(tp -> tp.getType().equals(type)).findFirst().orElse(ALI_PAY);
         }
     }

+ 95 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefits.java

@@ -0,0 +1,95 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefits
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 14:02
+ */
+@Getter
+@Setter
+public class UserBenefits extends BaseEntity {
+
+	/**
+	 * 权益类型
+	 */
+	private Type type;
+
+	/**
+	 * 生效平台
+	 */
+	private Long goodsId;
+
+	/**
+	 * 实物套餐权益 生效套餐ids
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String comboSkuIds;
+
+	/**
+	 * 续费减免生效规格(购买那个规格可享受权益)
+	 */
+	private Long skuId;
+
+	/**
+	 * 续费减免配置str
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String renewConfig;
+
+	/**
+	 * 续费减免配置 传入参数
+	 */
+	@DbIgnore
+	@TableField(exist = false)
+	private List<RenewConfig> renewConfigList;
+
+	/**
+	 * 描述
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String description;
+
+	/**
+	 * 续费减免前几次减免
+	 */
+	private Integer startRenew;
+
+	private Integer sorted;
+
+	private Boolean status;
+
+	@Getter
+	@AllArgsConstructor
+	public enum Type {
+		RENEW("续费减免"),
+		CERT("大学生认证"),
+		COMBO("套餐优惠"),
+		;
+		private String desc;
+	}
+
+	@Getter
+	@Setter
+	public static class RenewConfig {
+		/**
+		 * 名词
+		 */
+		private Integer index;
+
+		/**
+		 * 减免金额
+		 */
+		private BigDecimal reduce;
+	}
+}

+ 20 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefitsComboRelationRecord.java

@@ -0,0 +1,20 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsComboRelationRecord
+ * 创建者: JavaZou
+ * 创建时间:2024/9/29 10:42
+ */
+@Getter
+@Setter
+public class UserBenefitsComboRelationRecord extends BaseEntity{
+	private Long userId;
+
+	private Long orderId;
+
+	private String relationIds;
+}

+ 32 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefitsOrderRecord.java

@@ -0,0 +1,32 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsOrderRecord
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 16:08
+ */
+@Getter
+@Setter
+public class UserBenefitsOrderRecord extends BaseEntity{
+	private Long orderId;
+
+	private Long userId;
+
+	/**
+	 * 权益id
+	 */
+	private Long benefitsId;
+
+	private Long goodsId;
+
+	private Long skuId;
+
+	/**
+	 * 权益类型
+	 */
+	private String type;
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/UserPayEquipmentAvailableBenefits.java

@@ -29,4 +29,9 @@ public class UserPayEquipmentAvailableBenefits extends BaseEntity {
 	 * 1成功领取,0否
 	 */
 	private Integer statusCode;
+
+	/**
+	 * 默认0正常实物订单,1.实物权益订单类型
+	 */
+	private Integer type;
 }

+ 2 - 1
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelOrderDonData.java

@@ -21,7 +21,8 @@ import java.util.Date;
 @Getter
 @Setter
 @SearchBean(tables = "order_don o left join goods_don g on g.id = o.goods_id" +
-		" left join goods_don_sku sku on sku.id = o.sku_id",
+		" left join goods_don_sku sku on sku.id = o.sku_id" +
+		" :tb:",
 		where = ":condition:"
 )
 public class ExcelOrderDonData {

+ 2 - 1
netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonBackView.java

@@ -25,7 +25,8 @@ import java.util.List;
 @Setter
 @SearchBean(tables = "order_don o left join goods_don g on g.id = o.goods_id" +
 		" left join order_don_transport odt on odt.order_id = o.id" +
-		" left join goods_don_sku sku on sku.id = o.sku_id",
+		" left join goods_don_sku sku on sku.id = o.sku_id" +
+		" :tb:",
 		where = ":condition:"
 )
 public class OrderDonBackView {

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/model/request/AlipayOrderDonReq.java

@@ -32,4 +32,6 @@ public class AlipayOrderDonReq {
 	private String returnUrl;
 
 	private Boolean isDeposit = false;
+
+	private Boolean isUserBenefits = false;
 }

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

@@ -185,4 +185,9 @@ public class OrderPayRequest {
      * 用户海报记录id
      */
     private Long rid;
+
+    /**
+     * 权益id
+     */
+    private Long benefitsId;
 }

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

@@ -177,4 +177,7 @@ public class CouponUserView {
 
 	@DbField("cu.created_time")
 	private Date createdTime;
+
+	@DbIgnore
+	private String goodsImg;
 }

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

@@ -150,4 +150,10 @@ public class CouponView {
 
 	@DbField("c.created_time")
 	private Date createdTime;
+
+	@DbIgnore
+	private String goodsImg;
+
+	@DbField("c.id")
+	private Long couponId;
 }

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/model/views/GiftCardGoodsSkuFrontView.java

@@ -46,6 +46,9 @@ public class GiftCardGoodsSkuFrontView {
 	@DbField("g.title")
 	private String gcGoodsTitle;
 
+	@DbField("g.logo")
+	private String goodsLogo;
+
 	@DbField("sku.spec_val")
 	private String gcSpecVal;
 

+ 12 - 0
netflix-dao/src/main/java/com/cyksj/model/views/RenewalView.java

@@ -96,9 +96,15 @@ public class RenewalView {
     @JsonIgnore
     private Long userId;
 
+    @DbField("gr.start_time")
+    private Date startTime;
+
     @DbField("gr.expiry_time")
     private Date expiryTime;
 
+    @DbIgnore
+    private Long total;
+
     @DbIgnore
     private String expiryTimeStr;
 
@@ -314,4 +320,10 @@ public class RenewalView {
      */
     @DbIgnore
     private BigDecimal discount;
+
+    /**
+     * 是否是续费减免skuId
+     */
+    @DbIgnore
+    private Boolean isRenewReduce = false;
 }

+ 130 - 0
netflix-dao/src/main/java/com/cyksj/model/views/UserBenefitsFrontView.java

@@ -0,0 +1,130 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.UserBenefits;
+import com.ejlchina.searcher.bean.DbField;
+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.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsFrontView
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 15:34
+ */
+@Getter
+@Setter
+@SearchBean(tables = "user_benefits ub left join goods_don g on g.id = ub.goods_id" +
+		" left join goods_don_sku sku on sku.id = ub.sku_id",
+		where = "ub.status is true",
+		orderBy = "ub.sorted asc")
+public class UserBenefitsFrontView {
+	@DbField("ub.id")
+	private Long id;
+
+	/**
+	 * 权益类型
+	 */
+	@DbField("ub.type")
+	private UserBenefits.Type type;
+
+	/**
+	 * 生效平台
+	 */
+	@DbField("ub.goods_id")
+	private Long goodsId;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("g.logo")
+	private String goodsLogo;
+
+	/**
+	 * 实物套餐权益 生效套餐ids
+	 */
+	@DbField("ub.combo_sku_ids")
+	private String comboSkuIds;
+
+	/**
+	 * 实物套餐信息
+	 */
+	@DbIgnore
+	private List<GoodsDonSkuFrontView> comboSkuInfo;
+
+	/**
+	 * 续费减免生效规格(购买那个规格可享受权益)
+	 */
+	@DbField("ub.sku_id")
+	private Long skuId;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+
+	@DbField("sku.price")
+	private BigDecimal price;
+
+	/**
+	 * 续费减免配置
+	 */
+	@DbField("ub.renew_config")
+	private String renewConfig;
+
+	/**
+	 * 续费减免配置
+	 */
+	@DbIgnore
+	private List<UserBenefits.RenewConfig> renewConfigList;
+
+	/**
+	 * 图片撇脂
+	 */
+	@DbField("ub.img_config")
+	private String imgConfig;
+
+	/**
+	 * 续费减免前几次减免
+	 */
+	@DbField("ub.start_renew")
+	private Integer startRenew;
+
+	@DbField("ub.description")
+	private String description;
+
+	/**
+	 * 是否符合条件
+	 */
+	@DbIgnore
+	private Boolean isFh;
+
+	/**
+	 * 使用过套餐权益
+	 */
+	@DbIgnore
+	private Boolean isComboUsed;
+
+	/**
+	 * 续费次数
+	 */
+	@DbIgnore
+	private Integer renewCount;
+
+	/**
+	 * 用户认证优惠券详情
+	 */
+	@DbIgnore
+	private List<CouponUserView> couponUserViews;
+
+	/**
+	 * 认证优惠券展示
+	 */
+	@DbIgnore
+	private List<CouponView> couponViews;
+
+	@DbIgnore
+	private String goodsSpecJson;
+}

+ 86 - 0
netflix-dao/src/main/java/com/cyksj/model/views/UserBenefitsView.java

@@ -0,0 +1,86 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.UserBenefits;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsView
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 14:29
+ */
+@Getter
+@Setter
+@SearchBean(tables = "user_benefits ub left join goods_don g on g.id = ub.goods_id" +
+		" left join goods_don_sku sku on sku.id = ub.sku_id")
+public class UserBenefitsView {
+	/**
+	 * 权益类型
+	 */
+	@DbField("ub.type")
+	private UserBenefits.Type type;
+
+	/**
+	 * 生效平台
+	 */
+	@DbField("ub.goods_id")
+	private Long goodsId;
+
+	@DbField("g.title")
+	private String title;
+
+	/**
+	 * 实物套餐权益 生效套餐ids
+	 */
+	@DbField("ub.combo_sku_ids")
+	private String comboSkuIds;
+
+	/**
+	 * 续费减免生效规格(购买那个规格可享受权益)
+	 */
+	@DbField("ub.sku_id")
+	private Long skuId;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+
+	/**
+	 * 续费减免配置
+	 */
+	@DbField("ub.renew_config")
+	private String renewConfig;
+
+	/**
+	 * 续费减免配置
+	 */
+	@DbIgnore
+	private List<UserBenefits.RenewConfig> renewConfigList;
+
+	/**
+	 * 续费减免前几次减免
+	 */
+	@DbField("ub.start_renew")
+	private Integer startRenew;
+
+	@DbField("ub.sorted")
+	private Integer sorted;
+
+	@DbField("ub.status")
+	private Boolean status;
+
+	@DbField("ub.description")
+	private String description;
+
+	@DbField("ub.created_time")
+	private Date createdTime;
+
+	@DbField("ub.update_time")
+	private Date updateTime;
+}

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

@@ -240,4 +240,51 @@
         from coupon_user
         where coupon_id = #{couponId}
     </select>
+
+    <select id="couponPersonalByCouponIds" 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_condition,
+        c.reduce_money,
+        c.discount,
+        c.use_scope,
+        c.goods_sku_ids,
+        c.is_valid_time,
+        cu.valid_start_time,
+        c.valid_days,
+        c.valid_hours,
+        cu.valid_end_time,
+        (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 in
+        <foreach collection="userIds" item="userId" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+        and cu.coupon_id in
+        <foreach collection="couponIds" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+        and cu.channel != 'exCode'
+        order by `orderby`
+    </select>
 </mapper>

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

@@ -82,6 +82,7 @@
           and gt.status = 'validity'
           and gr.status = 'validity'
           and gr.yhs_id = #{yhsId}
+          and gr.expiry_time > now()
     </select>
 
     <select id="getGroupRelationViewByUserIdAndRelationId"

+ 40 - 0
netflix-dao/src/main/resources/mapper/UserBenefitsOrderRecordMapper.xml

@@ -0,0 +1,40 @@
+<?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.UserBenefitsOrderRecordMapper">
+
+    <select id="selectUseComboRecordByUid" resultType="java.lang.Integer">
+        select count(1) from (select order_id from user_benefits_order_record where goods_id = #{goodsId}
+        <if test="skuId != null">
+            and sku_id = #{skuId}
+        </if>
+        and type = #{type}
+        and user_id in
+        <foreach collection="userIds" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+        <if test="startTime != null">
+            and created_time > #{startTime}
+        </if>
+        ) bu inner join order_don o on o.id = bu.order_id
+        where o.status not in
+        <foreach collection="orderStatus" item="status" open="(" separator="," close=")">
+            #{status}
+        </foreach>
+        <if test="lastYear != null">
+            and o.created_time >= #{lastYear}
+        </if>
+        <if test="relationId != null">
+            and o.relation_id = #{relationId}
+        </if>
+    </select>
+
+    <select id="selectNoPaymentRecord" resultType="java.lang.Integer">
+        select count(1) from (select order_id from user_benefits_order_record where benefits_id = #{benefitsId}
+        and user_id in
+        <foreach collection="userIds" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>) bu inner join order_don o on o.id = bu.order_id
+        where o.status = 'noPayment'
+    </select>
+</mapper>

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

@@ -848,6 +848,7 @@ public class RedisService {
         NF_SET_TIME_LIMIT("nf_set_time_limit:", "设置pin码", 60l),
         NF_KICKOUT_TIME_LIMIT("nf_kickout_time_limit:", "踢出奈飞设备", 60l),
         GOODS_WHOLESALE_KEY("yh_home_paghe_cache:wholesale_key", "平台批发", 60 * 60 * 4L),
+        USER_BENEFITS_KEY("yh_home_paghe_cache:user_benefits_key", "用户权益", 60 * 60 * 12L),
         ;
 
         private String name;

+ 0 - 26
netflix-service/src/main/java/com/cyksj/service/exchange/impl/ExchangeCodeServiceImpl.java

@@ -383,32 +383,6 @@ public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, Exc
 				groupsRelationExpiryRecordService.syncGroupsRelationExpiryRecord(orderDon, goodsDon, sku);
 			});
 		}
-//		if (StrUtil.isNotEmpty(user.getOpenId())) {
-//			//发送模板消息
-//			THREAD_POOL_TASK_EXECUTOR.execute(() -> {
-//				try {
-//					if (StrUtil.isNotEmpty(user.getOpenId())) {
-//						log.info("用户{}兑换车位成功,发送新订单模板消息", user.getId());
-//						templateCommonService.newOderSendMsgByType(orderDon, goodsDon, user, user.getOpenId(), false);
-//					}
-//				} catch (Exception e) {
-//					log.info("用户{}兑换车位成功,发送消息失败:{}", user.getId(), e);
-//				}
-//			});
-//		}
-//
-//		THREAD_POOL_TASK_EXECUTOR.execute(() -> {
-//			try {
-//				//发送至客服消息
-//				List<CustomerService> customerServices = customerServiceMapper.selectList(null);
-//				for (CustomerService customerService : customerServices) {
-//					log.info("向客服{}发送用户兑换码兑换车票消息通知..", customerService.getNickName());
-//					templateCommonService.newOderSendMsgByType(orderDon, goodsDon, user, customerService.getOpenId(), true);
-//				}
-//			} catch (Exception e) {
-//				log.error("向客服发送用户{}兑换码兑换车票成功,发送消息失败:{}", user.getId(), e);
-//			}
-//		});
 		relation.setOrderId(orderDon.getId());
 		return relation;
 	}

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

@@ -34,4 +34,9 @@ public interface RefreshCacheService {
 	void goodsQuestionCache();
 
 	void refreshWholesaleGoods();
+
+	/**
+	 * 清除用户权益配置缓存
+	 */
+	void refreshUserBenefitsConfigCache();
 }

+ 5 - 1
netflix-service/src/main/java/com/cyksj/service/mange/goods/CmsGoodsDonSpecServiceImpl.java

@@ -485,7 +485,11 @@ public class CmsGoodsDonSpecServiceImpl extends ServiceImpl<GoodsDonSpecMapper,
 								sku.setSpecVal(StrUtil.replace(sku.getSpecVal(), dbNode.getValue(), e.getValue()));
 							}
 							if (isUpdatePrice.get()) {
-								sku.setPrice(sku.getPrice().subtract(dbNode.getPrice()).add(e.getPrice()));
+								if (specIdsList.size() == 1) {
+									sku.setPrice(e.getPrice());
+								} else {
+									sku.setPrice(sku.getPrice().subtract(dbNode.getPrice()).add(e.getPrice()));
+								}
 							}
 							if (isUpdateBenefits.get()) {
 								if (StrUtil.isEmpty(e.getBenefitsList())) {

+ 22 - 0
netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java

@@ -203,6 +203,10 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
 
     private final LumaUserMapper lumaUserMapper;
 
+    private final UserBenefitsOrderRecordMapper userBenefitsOrderRecordMapper;
+
+    private final UserBenefitsComboRelationRecordMapper userBenefitsComboRelationRecordMapper;
+
     private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
 
     @Override
@@ -1114,6 +1118,24 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
             //是否是分销订单
             //处理下级渠道分销提成
             handleSubRewards(orderDon);
+
+            //是否是用户套餐优惠权益订单
+            if (orderDon.getGoodsId()==15) {
+                UserBenefitsComboRelationRecord userBenefitsComboRelationRecord = userBenefitsComboRelationRecordMapper.selectOne(Wrappers.lambdaQuery(UserBenefitsComboRelationRecord.class)
+                        .eq(UserBenefitsComboRelationRecord::getOrderId, orderDon.getId()).last("limit 1"));
+                if (userBenefitsComboRelationRecord != null) {
+                    try {
+                        String relationIds = userBenefitsComboRelationRecord.getRelationIds();
+                        List<Long> relationIdList = Jsons.parseList(relationIds, Long.class);
+                        //清空车票或减少车票时间
+                        relationIdList.forEach(relationId->{
+                            orderCommonService.clearUserTicketTime(relationId);
+                        });
+                    } catch (Exception e) {
+
+                    }
+                }
+            }
         });
     }
 

+ 6 - 0
netflix-service/src/main/java/com/cyksj/service/mange/impl/RefreshCacheServiceImpl.java

@@ -66,4 +66,10 @@ public class RefreshCacheServiceImpl implements RefreshCacheService {
 		Set<String> keys = redisService.keys(RedisService.key.GOODS_WHOLESALE_KEY.getName() + "*");
 		redisService.del(keys.toArray(String[]::new));
 	}
+
+	@Override
+	public void refreshUserBenefitsConfigCache() {
+		Set<String> keys = redisService.keys(RedisService.key.USER_BENEFITS_KEY.getName() + "*");
+		redisService.del(keys.toArray(String[]::new));
+	}
 }

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

@@ -21,4 +21,6 @@ public interface OrderCommonService {
 	 * 清除座位缓存,可用座位数增加
 	 */
 	void delRelation(GroupsRelation relation);
+
+	void clearUserTicketTime(Long relationId);
 }

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

@@ -7,10 +7,7 @@ import com.alipay.api.response.AlipayTradeQueryResponse;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.cyksj.model.dto.AliPayParams;
 import com.cyksj.model.dto.H5JsPayParams;
-import com.cyksj.model.entity.OrderDetailView;
-import com.cyksj.model.entity.OrderDon;
-import com.cyksj.model.entity.RealGoodsInterestUser;
-import com.cyksj.model.entity.UpgradeOrderDon;
+import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.UpgradeOrderDetailView;
 import com.cyksj.model.request.AlipayOrderDonReq;
 import com.cyksj.model.request.OrderPayRequest;
@@ -106,4 +103,8 @@ public interface OrderDonService extends IService<OrderDon> {
     UpgradeOrderDetailView getUpgradeOrderDetail(Long orderId, Long userId);
 
 	OrderDon submitRenewUpgrade(OrderPayRequest payRequest);
+
+	OrderDon userBenefitsSubmit(OrderPayRequest orderPayRequest) throws Exception;
+
+    UserPayEquipmentAvailableBenefits getComboOptionalTicketRecord(long userId, Long orderId);
 }

+ 3 - 0
netflix-service/src/main/java/com/cyksj/service/order/UserRealOrderBenefitsService.java

@@ -1,6 +1,7 @@
 package com.cyksj.service.order;
 
 import com.cyksj.model.entity.GoodsDonSku;
+import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.manage.views.GroupsRelationView;
 import com.cyksj.model.views.UserAvailableGoodsTicketView;
 
@@ -33,4 +34,6 @@ public interface UserRealOrderBenefitsService {
 	 * 实物权益可选车票
 	 */
 	List<UserAvailableGoodsTicketView> getAvailableBenefitsTickets(Long userId, Long orderId, Long yhsId);
+
+	void sendUserBenefitsTicket(OrderDon orderDon, List<GoodsDonSku> goodsDonSkus);
 }

+ 57 - 0
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderCommonServiceImpl.java

@@ -156,4 +156,61 @@ public class OrderCommonServiceImpl implements OrderCommonService {
 			throw new BusinessRuntimeException("车位异常");
 		}
 	}
+
+	@Override
+	public void clearUserTicketTime(Long relationId) {
+		GroupsRelation relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+				.eq(GroupsRelation::getId, relationId)
+				.in(GroupsRelation::getStatus, List.of(GroupsRelation.Status.validity, GroupsRelation.Status.outside)));
+		GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
+		GoodsDonSku sku = goodsDonSkuMapper.selectById(groupsTrips.getSkuId());
+		Date expiryTime = relation.getExpiryTime();
+		DateTime now = DateTime.now();
+		Boolean isFlag = true;
+		if (expiryTime != null) {
+			DateTime refundExpiryTime;
+			if (sku.getDays() != null && sku.getDays() > 0) {
+				refundExpiryTime = DateUtil.offsetDay(expiryTime, -sku.getDays());
+			} else {
+				if (sku.getGoodsId() == 1 && sku.getMonths() == 1) {
+					refundExpiryTime = DateUtil.offsetDay(expiryTime, -30);
+				} else {
+					refundExpiryTime = DateUtil.offsetMonth(expiryTime, -sku.getMonths());
+				}
+			}
+			if (now.isBefore(refundExpiryTime)) {
+				isFlag = false;
+			}
+		}
+
+		if (isFlag) {
+			relation.setUserId(0l);
+			if (relation.getStatus() != GroupsRelation.Status.outside) {
+				relation.setStatus(GroupsRelation.Status.none);
+			}
+			relation.setStartTime(null);
+			relation.setExpiryTime(null);
+			relation.setIsHandle(false);
+			relation.setRenewStatus(false);
+			relation.setAccount(null);
+			groupsRelationMapper.updateExpiryTime(relation, expiryTime);
+			if (relation.getStatus() != GroupsRelation.Status.outside) {
+				delRelation(relation);
+			}
+		} else {
+			if (expiryTime != null) {
+				if (sku.getDays() != null && sku.getDays() > 0) {
+					expiryTime = DateUtil.offsetDay(expiryTime, -sku.getDays());
+				} else {
+					if (sku.getGoodsId() == 1 && sku.getMonths() == 1) {
+						expiryTime = DateUtil.offsetDay(expiryTime, -30);
+					} else {
+						expiryTime = DateUtil.offsetMonth(expiryTime, -sku.getMonths());
+					}
+				}
+				relation.setExpiryTime(expiryTime);
+				groupsRelationMapper.updateById(relation);
+			}
+		}
+	}
 }

+ 283 - 59
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -43,6 +43,7 @@ import com.cyksj.enums.GatewayApiCode;
 import com.cyksj.mapper.*;
 import com.cyksj.mapper.channel.ChannelPopularizeMapper;
 import com.cyksj.mapper.channel.ShopConfigMapper;
+import com.cyksj.mapper.channel.UserPayEquipmentAvailableBenefitsMapper;
 import com.cyksj.mapper.corp.CorpInteractiveUserOrderRelationMapper;
 import com.cyksj.mapper.corp.CorpUserMapper;
 import com.cyksj.mapper.corp.CorpUserTagMapper;
@@ -81,10 +82,7 @@ import com.cyksj.service.groups.GroupsFuncService;
 import com.cyksj.service.mange.coupon.CouponCommonService;
 import com.cyksj.service.market.MarketFrontService;
 import com.cyksj.service.market.task.TaskService;
-import com.cyksj.service.order.OrderCommonService;
-import com.cyksj.service.order.OrderDonPostService;
-import com.cyksj.service.order.OrderDonService;
-import com.cyksj.service.order.OrderRefundService;
+import com.cyksj.service.order.*;
 import com.cyksj.service.pay.AliPayCommonService;
 import com.cyksj.service.relation.GroupRelationClearService;
 import com.cyksj.service.relation.GroupRelationFrontService;
@@ -331,6 +329,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final OrderDonIndependentRenewRecordMapper independentRenewRecordMapper;
 
+	private final UserBenefitsMapper userBenefitsMapper;
+
+	private final UserBenefitsOrderRecordMapper userBenefitsOrderRecordMapper;
+
+	private final UserRealOrderBenefitsService userRealOrderBenefitsService;
+
+	private final UserPayEquipmentAvailableBenefitsMapper userPayEquipmentAvailableBenefitsMapper;
+
 	private final List<String> noChekGoodsTemp = List.of("Apple One", "Youtube", "Spotify");
 
 	@Transactional(rollbackFor = Throwable.class)
@@ -579,7 +585,9 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		orderDon.setPhone(orderPhone);
 		this.saveOrUpdate(orderDon);
 
-		if (orderDon.getIsDp()) {
+		Long orderId = orderDon.getId();
+		Long fUid = orderDon.getUserId();
+		if (orderDon.getIsDp() != null && orderDon.getIsDp()) {
 			//保存订单优惠加购规格 关系
 			OrderDiscountPlusPurchaseSkuRelation orderDiscountPlusPurchaseSkuRelation = new OrderDiscountPlusPurchaseSkuRelation();
 			orderDiscountPlusPurchaseSkuRelation.setOrderId(orderDon.getId());
@@ -592,7 +600,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			saveTransport(payRequest.getAddressId(), orderDon.getId());
 		}
 		//保存加购发货地址
-		if (orderDon.getIsDp() && payRequest.getAddressId() != null) {
+		if (orderDon.getIsDp() != null && orderDon.getIsDp() && payRequest.getAddressId() != null) {
 			redisService.set(RedisService.key.PLUS_GOODS_ADDRESS_KEY.getName() + orderDon.getId(), payRequest.getAddressId(), RedisService.key.PLUS_GOODS_ADDRESS_KEY.getTimeout());
 		}
 		if (orderDon.getDiscountPopularizeRecord() != null) {
@@ -601,11 +609,6 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			discountPopularizeRecord.setIp(payRequest.getIp());
 			orderDonUserDiscountPopularizeRecordMapper.insert(discountPopularizeRecord);
 		}
-		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
-			jobManager.addJob(EXPIRY_TIME, () -> {
-				closeOrder(orderDon.getId(), null);
-			});
-		}
 
 		//数据回传
 		if (StringUtils.isNotBlank(payRequest.getCallback())) {
@@ -618,8 +621,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				try {
 					Optional.ofNullable(Jsons.parseObject(val, OrderDonPostDataDto.class))
 							.ifPresent(orderDonPostDataDto -> {
-								log.info("用户ID:{}预下单 回传用户缓存 订单回传记录保存,callback:{},callbackType:{}", orderDon.getUserId(), orderDonPostDataDto.getCallback(), orderDonPostDataDto.getCallbackType());
-								orderDonPostService.savePostData(orderDon.getId(), orderDonPostDataDto.getCallback(), orderDonPostDataDto.getCallbackType());
+								log.info("用户ID:{}预下单 回传用户缓存 订单回传记录保存,callback:{},callbackType:{}", fUid, orderDonPostDataDto.getCallback(), orderDonPostDataDto.getCallbackType());
+								orderDonPostService.savePostData(orderId, orderDonPostDataDto.getCallback(), orderDonPostDataDto.getCallbackType());
 							});
 				} catch (Exception e) {
 					log.error("解析用户缓存回传信息错误,msg:{}", StringUtil.getErrorText(e));
@@ -639,7 +642,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		//礼品卡现金使用记录
 		if (CollUtil.isNotEmpty(orderDon.getGcUseRecords())) {
 			orderDon.getGcUseRecords().forEach(data -> {
-				data.setOrderId(orderDon.getId());
+				data.setOrderId(orderId);
 			});
 			giftCardOrderUseRecordService.saveBatch(orderDon.getGcUseRecords());
 		}
@@ -661,9 +664,26 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			informationClickUserRecord.setOrderId(orderDon.getId());
 			informationClickUserRecordMapper.insert(informationClickUserRecord);
 		}
+		if (goodsDon != null) {
+			orderDon.setGoodsDonType(goodsDon.getType());
+		}
+
+		if (sku != null) {
+			orderDon.setIsMirror(sku.getIsMirror());
+		}
 
 		//统一处理订单记录操作
 		unifiedHandlerOrderRecordFunc(orderDon.getId(), payRequest);
+		//余额支付订单
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+			orderDon = orderDonMapper.selectById(orderId);
+			unifiedHandlerOrderNotify(orderDon, goodsDon, sku);
+		}
+		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
+			jobManager.addJob(EXPIRY_TIME, () -> {
+				closeOrder(orderId, null);
+			});
+		}
 		return orderDon;
 	}
 
@@ -680,7 +700,20 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		recordOrderTrialTicketJumpPay(orderId,payRequest.getIsTrial());
 		//记录用户推广海报下单记录
 		recordPosterUserOrder(orderId, payRequest.getRid());
+	}
 
+	/**
+	 * 记录用户权益下单
+	 */
+	private void recordUserBenefitsOrderRelate(OrderDon orderDon, UserBenefits userBenefits) {
+		UserBenefitsOrderRecord userBenefitsOrderRecord = new UserBenefitsOrderRecord();
+		userBenefitsOrderRecord.setOrderId(orderDon.getId());
+		userBenefitsOrderRecord.setGoodsId(orderDon.getGoodsId());
+		userBenefitsOrderRecord.setSkuId(orderDon.getSkuId());
+		userBenefitsOrderRecord.setUserId(orderDon.getUserId());
+		userBenefitsOrderRecord.setType(userBenefits.getType().name());
+		userBenefitsOrderRecord.setBenefitsId(userBenefits.getId());
+		userBenefitsOrderRecordMapper.insert(userBenefitsOrderRecord);
 	}
 
 	private Boolean checkIsNeedGroupsRelation(GoodsDon goodsDon) {
@@ -1055,15 +1088,12 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		//扣除使用优惠券、余额的订单金额
 		deductOrderMoney(orderDon, couponUser, payRequest.getBalance(), user, sku.getPrice(), goodsDon.getType(), payRequest.getGcCash(), payRequest.getGcpIds(), false, null);
 		this.saveOrUpdate(orderDon);
-		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
-			jobManager.addJob(EXPIRY_TIME, () -> {
-				closeOrder(orderDon.getId(), null);
-			});
-		}
+
+		Long orderId = orderDon.getId();
 		//礼品卡现金使用记录
 		if (CollUtil.isNotEmpty(orderDon.getGcUseRecords())) {
 			orderDon.getGcUseRecords().forEach(data -> {
-				data.setOrderId(orderDon.getId());
+				data.setOrderId(orderId);
 			});
 			giftCardOrderUseRecordService.saveBatch(orderDon.getGcUseRecords());
 		}
@@ -1122,27 +1152,13 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		}
 		//订单金额为0 且订单为已完成 更新虚物车票
 		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
-			OrderDon updateOrder = orderDonMapper.selectById(orderDon.getId());
-			//mj镜像续费订单
-			if(orderDon.getOrderType() == 2 && orderDon.getGoodsId() == 26){
-				GroupsRelation groupsRelation = groupsRelationMapper.selectById(orderDon.getRelationId());
-				if(groupsRelation != null){
-					Date expiryTime = groupsRelation.getExpiryTime();
-
-					//mj续费订单当天过期直接加次数
-					if (expiryTime != null && DateUtil.isSameDay(new Date(), expiryTime)) {
-						mjRenewOrder(orderDon);
-					}
-				}
-			}
-			updateOrder.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
-			updateGroupsTicket(updateOrder, goodsDon, sku);
-			orderDonMapper.updateById(updateOrder);
-			//余额使用记录
-			if (orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) {
-				userBenefitsService.recordBalanceBySource(orderDon.getUserId(), orderDon.getBalance().negate(), UserBalanceSourceRecord.Source.payment, orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.payment.getDesc());
-			}
-			return updateOrder;
+			orderDon = orderDonMapper.selectById(orderId);
+			unifiedHandlerOrderNotify(orderDon, goodsDon, sku);
+		}
+		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
+			jobManager.addJob(EXPIRY_TIME, () -> {
+				closeOrder(orderId, null);
+			});
 		}
 		return orderDon;
 	}
@@ -1218,7 +1234,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			return;
 		}
 		Long goodsId = orderDon.getGoodsId();
-		if (orderDon.getIsNoLogin()) {
+		if (orderDon.getIsNoLogin() != null && orderDon.getIsNoLogin()) {
 			orderDon.setExCode(exchangeCodeService.getExCode(goodsId, orderDon.getSkuId(), 1));
 		}
 		GoodsDonSku sku = null;
@@ -1441,7 +1457,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				return;
 			}
 			log.info("虚物订单orderNo:{}开始更新车票信息", order.getOrderNo());
-			if (order.getIsDp()) {
+			if (order.getIsDp() != null && order.getIsDp()) {
 				//是否已经发送优惠加购车票
 				OrderDiscountPlusPurchaseSkuRelation purchaseSkuRelation = orderDiscountPlusPurchaseSkuRelationMapper.selectOne(Wrappers.lambdaQuery(OrderDiscountPlusPurchaseSkuRelation.class)
 						.eq(OrderDiscountPlusPurchaseSkuRelation::getOrderId, order.getId()));
@@ -1616,7 +1632,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		distributeWaitingSendPoints.setUserId(order.getUserId());
 		BigDecimal orderMoney = order.getMoney();
 		//是否是加购订单
-		if (order.getIsDp()) {
+		if (order.getIsDp() != null && order.getIsDp()) {
 			//加购订单使用 主平台金额 分销
 			orderMoney = order.getOriMoney();
 			if (orderMoney.compareTo(BigDecimal.ZERO) == 0) {
@@ -2482,7 +2498,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	 * 更新车票状态
 	 */
 	public void updateGroupsTicket(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku) {
-		if (orderDon.getOrderType() == 2 && orderDon.getIsChangeTicket()) {
+		if (orderDon.getOrderType() != null && orderDon.getOrderType() == 2 && orderDon.getIsChangeTicket() != null && orderDon.getIsChangeTicket()) {
 			//将奈飞月付车票更换成其他规格的车票
 			changeTicketAndRecord(orderDon, sku);
 		} else {
@@ -2547,7 +2563,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	@Override
 	public void orderNotify(OrderDon orderDon) {
 		Long goodsId = orderDon.getGoodsId();
-		if (orderDon.getIsNoLogin()) {
+		if (orderDon.getIsNoLogin() != null && orderDon.getIsNoLogin()) {
 			orderDon.setExCode(exchangeCodeService.getExCode(goodsId, orderDon.getSkuId(), 1));
 		}
 		GoodsDonSku sku = null;
@@ -2563,6 +2579,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	 */
 	public void unifiedHandlerOrderNotify(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku) {
 		orderDon.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
+		//实物权益优惠订单
+		if (orderDon.getGoodsId() == 15) {
+			Integer selectCount = userBenefitsOrderRecordMapper.selectCount(Wrappers.lambdaQuery(UserBenefitsOrderRecord.class)
+					.eq(UserBenefitsOrderRecord::getOrderId, orderDon.getId()));
+			if (selectCount > 0) {
+				orderDon.setStatus(OrderDon.Status.complete);
+			}
+		}
 		boolean isSuccess = update(Wrappers.lambdaUpdate(OrderDon.class)
 				.set(OrderDon::getStatus, orderDon.getStatus())
 				.in(OrderDon::getStatus, List.of(OrderDon.Status.noPayment, OrderDon.Status.close))
@@ -2572,25 +2596,29 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			return;
 		}
 
-		if (orderDon.getIsNoLogin()) {
+		if (orderDon.getIsNoLogin() != null && orderDon.getIsNoLogin()) {
 			orderDon.setStatus(OrderDon.Status.hasPayment);
 		}
 
 		//mj镜像续费订单
-		if(orderDon.getOrderType() == 2 && orderDon.getGoodsId() == 26){
+		if (orderDon.getOrderType() != null && orderDon.getOrderType() == 2 && orderDon.getGoodsId() == 26) {
 			GroupsRelation groupsRelation = groupsRelationMapper.selectById(orderDon.getRelationId());
-			if(groupsRelation != null){
+			if (groupsRelation != null) {
 				Date expiryTime = groupsRelation.getExpiryTime();
 
 				//mj续费订单当天过期直接加次数
 				if (expiryTime != null && DateUtil.isSameDay(new Date(), expiryTime)) {
 					mjRenewOrder(orderDon);
 				}
+				//同步user时间
+				midjourneyUserMapper.update(null, Wrappers.lambdaUpdate(MidjourneyUser.class)
+						.set(MidjourneyUser::getExpireTime, DateUtil.offsetMonth(expiryTime, sku.getMonths()))
+						.eq(MidjourneyUser::getRelationId, orderDon.getRelationId()));
 			}
 		}
 
 		Long goodsId = goodsDon.getId();
-		if (!orderDon.getIsNoLogin() && sku != null) {
+		if (orderDon.getIsNoLogin() != null && !orderDon.getIsNoLogin() && sku != null) {
 			//更新座位信息
 			updateGroupsTicket(orderDon, goodsDon, sku);
 			if (goodsDon.getType() == 1) {
@@ -2719,6 +2747,23 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				}
 			}
 		}
+
+		//套餐优惠
+		if (orderDon.getGoodsId() == 15) {
+			Integer selectCount = userBenefitsOrderRecordMapper.selectCount(Wrappers.lambdaQuery(UserBenefitsOrderRecord.class)
+					.eq(UserBenefitsOrderRecord::getOrderId, orderDon.getId()));
+			if (selectCount > 0) {
+				//发放用户车票套餐
+				if (StrUtil.isNotEmpty(sku.getBenefitsList())) {
+					try {
+						List<GoodsDonSku> goodsDonSkus = Jsons.parseList(sku.getBenefitsList(), GoodsDonSku.class);
+						userRealOrderBenefitsService.sendUserBenefitsTicket(orderDon, goodsDonSkus);
+					} catch (Exception e) {
+						log.error("发放用户:{}车票权益套餐错误:{}", orderDon.getUserId(), StringUtil.getErrorText(e));
+					}
+				}
+			}
+		}
 	}
 
 	private void setTripDownIfFull(OrderDon orderDon) {
@@ -3336,7 +3381,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (sku != null) {
 			payTitle.append(sku.getSpecVal());
 		}
-		if (isGiftCardPay.get()) {
+		if (isGiftCardPay != null && isGiftCardPay.get()) {
 			//礼品卡规格
 			Map<Long, Integer> giftCardPayNumMap = new ConcurrentHashMap<>();
 			giftCards.stream().forEach(data -> {
@@ -3383,7 +3428,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				orderDon.setRealMoney(gift_card_pay_money);
 			}
 		}
-		if (orderDon.getIsDp()) {
+		if (orderDon.getIsDp() != null && orderDon.getIsDp()) {
 			List<GoodsDonSkuView> dp_skuViewList = beanSearcher.searchAll(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getSkuId, dpSkuIds).op(Operator.InList).build());
 			if (dp_skuViewList.size() != dpSkuIds.size()) {
 				throw BusinessRuntimeException.getInstance("系统异常,请刷新页面重新下单");
@@ -3577,11 +3622,15 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			upgradeOrderDon.setType(UpgradeOrderDon.Type.ALI_PAY);
 			upgradeOrderDonMapper.insert(upgradeOrderDon);
 
-			deductUpgradeBalancePayOrderMoney(upgradeOrderDon, payRequest.getBalance(),user );
-
+			deductUpgradeBalancePayOrderMoney(upgradeOrderDon, payRequest.getBalance(),user);
+			Long orderId = upgradeOrderDon.getId();
+			if (upgradeOrderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+				upgradeOrderDon = upgradeOrderDonMapper.selectById(orderId);
+				unifiedHandlerUpgradeOrderNotify(upgradeOrderDon, upgradePackage);
+			}
 			if (upgradeOrderDon.getStatus() == UpgradeOrderDon.Status.noPayment) {
 				jobManager.addJob(EXPIRY_TIME, () -> {
-					closeUpgradeOrder(upgradeOrderDon.getId(), null);
+					closeUpgradeOrder(orderId, null);
 				});
 			}
 			return upgradeOrderDon;
@@ -3865,7 +3914,6 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
         return beanSearcher.searchFirst(UpgradeOrderDetailView.class, builder.build());
 	}
 
-
 	@Override
 	public OrderDon submitRenewUpgrade(OrderPayRequest payRequest) {
 		Long packageId = payRequest.getPackageId();
@@ -3935,13 +3983,189 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		orderDonMapper.insert(orderDon);
 		//记录续费升级的规格
 		recordRenewOrderDon(orderDon, renewUpgradePackage, groupsTrips);
+		Long orderId = orderDon.getId();
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+			orderDon = orderDonMapper.selectById(orderId);
+			unifiedHandlerOrderNotify(orderDon, goodsDon, renewSku);
+		}
+		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
+			jobManager.addJob(EXPIRY_TIME, () -> {
+				closeOrder(orderId, null);
+			});
+		}
+		return orderDon;
+	}
 
-		jobManager.addJob(EXPIRY_TIME, () -> {
-			closeOrder(orderDon.getId(), null);
-		});
+	@Override
+	public OrderDon userBenefitsSubmit(OrderPayRequest orderPayRequest) throws Exception {
+		Long userId = orderPayRequest.getUserId();
+		String ip = orderPayRequest.getIp();
+		Long benefitsId = orderPayRequest.getBenefitsId();
+		Assert.notNull(benefitsId, "下单错误");
+		UserBenefits userBenefits = userBenefitsMapper.selectOne(Wrappers.lambdaQuery(UserBenefits.class)
+				.eq(UserBenefits::getId, benefitsId)
+				.eq(UserBenefits::getStatus, true)
+				.last("limit 1"));
+		if (userBenefits == null) {
+			throw BusinessRuntimeException.getInstance("权益已不存在");
+		}
+		User user = userMapper.selectById(userId);
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, user);
+		Integer count = userBenefitsOrderRecordMapper.selectNoPaymentRecord(userIdList, benefitsId);
+		if (count > 0) {
+			throw BusinessRuntimeException.getInstance("存在待支付权益订单");
+		}
+		//用户是否满足该权益 并且未领取
+		checkUserBenefitsStatus(userBenefits, userIdList);
+		OrderDon orderDon = new OrderDon();
+		orderDon.setOrderNo(SEQUENCE.nextId().toString());
+		if (userBenefits.getType() == UserBenefits.Type.COMBO) {
+			if (orderPayRequest.getSkuId() == null) {
+				throw BusinessRuntimeException.getInstance("请选择对应套餐");
+			}
+			String comboSkuIds = userBenefits.getComboSkuIds();
+			if (!Jsons.parseList(comboSkuIds, Long.class).contains(orderPayRequest.getSkuId())) {
+				throw BusinessRuntimeException.getInstance("错误套餐");
+			}
+		}
+		GoodsDonSku sku;
+		if (userBenefits.getType() == UserBenefits.Type.RENEW) {
+			sku = goodsDonSkuMapper.selectById(userBenefits.getSkuId());
+		} else {
+			sku = goodsDonSkuMapper.selectById(orderPayRequest.getSkuId());
+		}
+		orderDon.setMoney(sku.getPrice());
+		orderDon.setOrderType(1);
+		GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
+		Long relationId = 0l;
+		if (userBenefits.getType() == UserBenefits.Type.RENEW) {
+			relationId = orderPayRequest.getRelationId();
+			Assert.notNull(relationId, "请选择对应车票");
+			GroupsRelation relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+					.eq(GroupsRelation::getId, relationId)
+					.in(GroupsRelation::getUserId, userIdList)
+					.isNotNull(GroupsRelation::getExpiryTime));
+			Assert.notNull(relation, "车票不存在");
+			GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
+			if (groupsTrips.getSkuId().intValue() != userBenefits.getSkuId().intValue()) {
+				throw BusinessRuntimeException.getInstance("续费规格错误");
+			}
+			if (relation != null && relation.getStatus() == GroupsRelation.Status.outside) {
+				orderDon.setIsOt(true);
+			}
+			//目前续费减免下单次数
+			Integer renewCount = userBenefitsOrderRecordMapper.selectUseComboRecordByUid(userIdList, userBenefits.getType(), userBenefits.getGoodsId(), userBenefits.getSkuId(), Constant.noOrderAllStatus, null, relation.getStartTime(), relation.getId());
+			String renewConfig = userBenefits.getRenewConfig();
+			List<UserBenefits.RenewConfig> renewConfigs = Jsons.parseList(renewConfig, UserBenefits.RenewConfig.class);
+			Integer start = userBenefits.getStartRenew();
+			BigDecimal reduce;
+			if (renewCount > start) {
+				UserBenefits.RenewConfig thisLevel = renewConfigs.get(start);
+				reduce = thisLevel.getReduce();
+			} else {
+				Optional<UserBenefits.RenewConfig> first = renewConfigs.stream().filter(e -> e.getIndex() == renewCount + 1).findFirst();
+				if (!first.isPresent()) {
+					log.error("暂无skuId:{}目前等级:{}的权益", sku.getId(), renewCount + 1);
+					throw BusinessRuntimeException.getInstance("系统异常");
+				}
+				reduce = first.get().getReduce();
+			}
+			orderDon.setMoney(orderDon.getMoney().subtract(reduce));
+			//续费订单
+			orderDon.setOrderType(2);
+		}
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) < 0) {
+			throw BusinessRuntimeException.getInstance("订单金额错误");
+		}
+		orderDon.setRealMoney(sku.getPrice());
+		orderDon.setOriMoney(sku != null ? sku.getPrice() : orderDon.getMoney());
+		//渠道链接id
+		if (orderPayRequest.getPopularizeId() != null) {
+			orderDon.setPopularizeId(orderPayRequest.getPopularizeId());
+		}
+		orderDon.setSkuId(sku.getId());
+		orderDon.setRelationId(relationId);
+		orderDon.setNum(1);
+		orderDon.setGoodsId(goodsDon.getId());
+		orderDon.setStatus(OrderDon.Status.noPayment);
+		orderDon.setUserId(user.getId());
+		orderDon.setType(OrderDon.Type.getType(orderPayRequest.getType()));
+		orderDon.setIsWebPay(orderPayRequest.getIsWebPay());
+		orderDon.setSource(orderPayRequest.getSource());
+		orderDon.setCouponExCode(orderPayRequest.getCouponExCode());
+		orderDon.setIsNoLogin(false);
+
+		String customId = orderPayRequest.getCustomId();
+		Long yhsId = yhShopFrontService.checkShopCustomIdAndReturnShopId(customId);
+		orderDon.setYhsId(yhsId);
+		if (orderDon.getYhsId() == 0) {
+			//虚拟商品订单,是否是分销订单
+			setDistributeOrderMark(orderDon, goodsDon.getType(), user.getId());
+		}
+		BigDecimal balance = orderPayRequest.getBalance();
+		//存在余额
+		deductBalancePayOrderMoney(orderDon, balance, user);
+		orderDon.setPayDesc(goodsDon.getPayDesc());
+		//设置订单支付商品信息
+		setOrderDonPayTile(null, null, null, orderDon, goodsDon, sku, null);
+		this.saveOrUpdate(orderDon);
+		if (orderDon.getStatus() == OrderDon.Status.noPayment) {
+			jobManager.addJob(EXPIRY_TIME, () -> {
+				closeOrder(orderDon.getId(), null);
+			});
+		}
+		//统一处理订单记录操作
+		unifiedHandlerOrderRecordFunc(orderDon.getId(), orderPayRequest);
+		//记录用户权益下单
+		recordUserBenefitsOrderRelate(orderDon, userBenefits);
+
+		//订单金额为0 且订单为已完成
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+			// 更改订单状态
+			orderDon.setPayTime(DateTime.now());
+			unifiedHandlerOrderNotify(orderDon, goodsDon, sku);
+		}
 		return orderDon;
 	}
 
+	@Override
+	public UserPayEquipmentAvailableBenefits getComboOptionalTicketRecord(long userId, Long orderId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		UserPayEquipmentAvailableBenefits userPayEquipmentAvailableBenefits = userPayEquipmentAvailableBenefitsMapper.selectOne(Wrappers.lambdaQuery(UserPayEquipmentAvailableBenefits.class)
+				.eq(UserPayEquipmentAvailableBenefits::getRealOrderId, orderId)
+				.in(UserPayEquipmentAvailableBenefits::getUserId, userIdList)
+				.eq(UserPayEquipmentAvailableBenefits::getDeleted, 1)
+				.last("limit 1"));
+		return userPayEquipmentAvailableBenefits;
+	}
+
+	private void checkUserBenefitsStatus(UserBenefits userBenefits, List<Long> userIdList) {
+		UserBenefits.Type type = userBenefits.getType();
+		int fhCount = 0;
+		if (type == UserBenefits.Type.COMBO) {
+			//是否已经购买实物
+			fhCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class).in(OrderDon::getUserId, userIdList).eq(OrderDon::getGoodsId, Constant.NF_EP_GID).notIn(OrderDon::getStatus, Constant.noOrderAllStatus));
+			if (fhCount > 0) {
+				//近一年是否已经使用过该权益
+				DateTime lastYear = DateUtil.offsetMonth(DateTime.now(), -12);
+				Integer comboCount = userBenefitsOrderRecordMapper.selectUseComboRecordByUid(userIdList, userBenefits.getType(), userBenefits.getGoodsId(), null, Constant.noOrderStatus, lastYear, null, null);
+				if (comboCount > 0) {
+					throw BusinessRuntimeException.getInstance("你今年已享受该套餐优惠");
+				}
+			}
+		} else if (type == UserBenefits.Type.RENEW) {
+			//是否购买过该规格套餐
+			Long skuId = userBenefits.getSkuId();
+			fhCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class).in(OrderDon::getUserId, userIdList).eq(OrderDon::getSkuId, skuId).notIn(OrderDon::getStatus, Constant.noOrderAllStatus));
+		} else {
+			throw BusinessRuntimeException.getInstance("权益下单异常");
+		}
+		if (fhCount == 0) {
+			throw BusinessRuntimeException.getInstance("你未完成该权益目标");
+		}
+	}
+
+
 	/**
 	 * 试用车票跳转购买正式车票订单记录
 	 */

+ 54 - 0
netflix-service/src/main/java/com/cyksj/service/order/impl/UserRealOrderBenefitsImpl.java

@@ -9,6 +9,7 @@ import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.mapper.GoodsDonSkuMapper;
 import com.cyksj.mapper.GroupsRelationMapper;
 import com.cyksj.mapper.OrderDonMapper;
+import com.cyksj.mapper.UserBenefitsComboRelationRecordMapper;
 import com.cyksj.mapper.channel.UserPayEquipmentAvailableBenefitsMapper;
 import com.cyksj.mapper.channel.UserPayEquipmentReceiveBenefitsRecordMapper;
 import com.cyksj.model.entity.*;
@@ -57,6 +58,8 @@ public class UserRealOrderBenefitsImpl implements UserRealOrderBenefitsService {
 
 	private final OrderDonMapper orderDonMapper;
 
+	private final UserBenefitsComboRelationRecordMapper userBenefitsComboRelationRecordMapper;
+
 	@Override
 	public Boolean getPayRealGoodsBenefits(Long userId, Long orderId, Long yhsId, List<GoodsDonSku> skuIds) {
 		Boolean isReceivedAll = true;
@@ -210,6 +213,57 @@ public class UserRealOrderBenefitsImpl implements UserRealOrderBenefitsService {
 		return ticketViews;
 	}
 
+	@Override
+	public void sendUserBenefitsTicket(OrderDon orderDon, List<GoodsDonSku> goodsDonSkus) {
+		Long orderId = orderDon.getId();
+		Long userId = orderDon.getUserId();
+		Long yhsId = orderDon.getYhsId();
+		List<Long> relationIds = new ArrayList<>();
+		goodsDonSkus.forEach(sku -> {
+			Long skuId = sku.getId();
+			sku = goodsDonSkuMapper.selectById(skuId);
+			Long goodsId = sku.getGoodsId();
+			log.info("开始发放用户套餐优惠权益订单:{} 车票规格:{}", orderId, skuId);
+			List<GroupsRelationView> relationViewList = groupsRelationMapper.selectRelationByGoodsId(goodsId, userId, yhsId);
+			//是否有该规格车票,若无直接发放车票
+			if (CollUtil.isEmpty(relationViewList)) {
+				log.info("用户userId:{}未有skuId:{}车票,获取对应车票", userId, sku.getId());
+				//领取权益时,没有车票的用户自动上车,获得对应车票
+				GroupsRelation relation = exchangeCodeService.getGroupTripsTicket(yhsId, sku, userId, null, OrderDon.Type.PAY_EQUIPMENT, null);
+				relationIds.add(relation.getId());
+			} else if (relationViewList.size() == 1) {
+				//若有且只有一张增加时长
+				//TODO
+				GroupsRelationView groupsRelationView = relationViewList.get(0);
+				log.info("存在对应规格:{}的车票,增加车票:relationId:{}时长", sku.getId(), groupsRelationView.getId());
+				Date expiryTime = groupsRelationView.getExpiryTime();
+				if (expiryTime == null) {
+					throw BusinessRuntimeException.getInstance("用户存在未发车的车票,请发车后再进行发货");
+				}
+				DateTime updateExpiryTime = DateUtil.offset(expiryTime, DateField.MONTH, sku.getMonths());
+				updateExpiryTime = DateUtil.offsetDay(updateExpiryTime, 3);
+				groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+						.eq(GroupsRelation::getId, groupsRelationView.getId())
+						.eq(GroupsRelation::getUserId, userId)
+						.set(GroupsRelation::getExpiryTime, updateExpiryTime));
+				groupsRelationView.setUpdateExpiryTime(updateExpiryTime);
+				relationIds.add(groupsRelationView.getId());
+			} else if (relationViewList.size() > 1) {
+				UserPayEquipmentAvailableBenefits userPayEquipmentAvailableBenefits = new UserPayEquipmentAvailableBenefits()
+						.setUserId(userId)
+						.setSkuId(skuId)
+						.setType(1)
+						.setRealOrderId(orderDon.getId());
+				availableBenefitsMapper.insert(userPayEquipmentAvailableBenefits);
+			}
+		});
+		UserBenefitsComboRelationRecord userBenefitsComboRelationRecord = new UserBenefitsComboRelationRecord();
+		userBenefitsComboRelationRecord.setOrderId(orderId);
+		userBenefitsComboRelationRecord.setRelationIds(relationIds.toString());
+		userBenefitsComboRelationRecord.setUserId(orderDon.getUserId());
+		userBenefitsComboRelationRecordMapper.insert(userBenefitsComboRelationRecord);
+	}
+
 	/**
 	 * 保存获取权益记录
 	 */

+ 22 - 5
netflix-service/src/main/java/com/cyksj/service/pay/impl/AliPayCommonServiceImpl.java

@@ -71,6 +71,8 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 
 	private final GoodsWholesaleUserDepositMapper depositMapper;
 
+	private final UserBenefitsOrderRecordMapper userBenefitsOrderRecordMapper;
+
 	private static final Sequence SEQUENCE = new Sequence(0);
 
 	@Override
@@ -130,11 +132,18 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 			orderDon.setIsWebPay(false);
 		}
 		orderDonMapper.updateById(orderDon);
-		GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId());
-		String subject = goodsDon.getTitle().contains("ChatGPT") ? "AI订阅" : goodsDon.getTitle();
+		String notifyPath = BaseZfbConfig.notifyUrl;
+		Boolean isUserBenefits = re.getIsUserBenefits();
+		String subject;
+		if (isUserBenefits) {
+			subject = orderDon.getGoodsId() == 15 ? "套餐权益" : "续费减免";
+		} else {
+			GoodsDon goodsDon = goodsDonMapper.selectById(orderDon.getGoodsId());
+			subject = goodsDon.getTitle().contains("ChatGPT") ? "AI订阅" : goodsDon.getTitle();
+		}
 		//回调地址
 		String host = envCommonService.getNotifyHost();
-		String notifyUrl = String.format("%s%s", host, BaseZfbConfig.notifyUrl);
+		String notifyUrl = String.format("%s%s", host, notifyPath);
 		//手机h5 跳转
 		OrderDonPost orderDonPost = orderDonPostMapper.selectOne(Wrappers.lambdaQuery(OrderDonPost.class)
 				.eq(OrderDonPost::getOrderId, orderId).last("limit 1"));
@@ -143,6 +152,9 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 		if (orderDonPost != null && OrderDonPost.Type.BaiDu == orderDonPost.getType()) {
 			is_bdPost = true;
 		}
+		if (StrUtil.isNotEmpty(returnUrl)) {
+			log.info("支付宝支付成功返回地址:{}", returnUrl);
+		}
 		//是否在微信内支付 微信内支付宝支付后 支付宝回调页面不跳转
 		Boolean isWx = Optional.ofNullable(re.getIsWx()).orElse(false);
 		if (ZfbProductCode.MOBILE_WEB.getProductCode().equals(productCode) && !isWx) {
@@ -150,10 +162,15 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 				//页面 拼接订单号,前端从url获取订单id.
 				returnUrl = String.format("%s%s", envCommonService.getDomain(), BaseZfbConfig.SUCCESS_URL) + orderDon.getId();
 			} else {
-				//默认跳转页
-				returnUrl = String.format("%s%s", envCommonService.getDomain(), BaseZfbConfig.DEFAULT_URL);
+				if (StrUtil.isEmpty(returnUrl)) {
+					//默认跳转页
+					returnUrl = String.format("%s%s", envCommonService.getDomain(), BaseZfbConfig.DEFAULT_URL);
+				}
 			}
 		}
+		if (StrUtil.isNotEmpty(returnUrl)) {
+			log.info("支付宝支付成功最终返回地址:{}", returnUrl);
+		}
 		AliPayParams aliPayParams = builderPayParams(orderDon.getId(),
 				orderDon.getOrderNo(),
 				orderDon.getShopId(),

+ 18 - 0
netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationFrontServiceImpl.java

@@ -155,6 +155,8 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 
 	private final AccountMapper accountMapper;
 
+	private final UserBenefitsMapper userBenefitsMapper;
+
 	@Override
 	public SearchResult<RenewalView> getMyTicket(long userId, Boolean isLoginPopularize, String customId, String account) {
 		User u = userMapper.selectById(userId);
@@ -180,6 +182,9 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 		//过滤邀请制、已失效的车位
 		Integer size = list.getTotalCount().intValue();
 
+		List<Long> renewReduceSkuIds = userBenefitsMapper.selectList(Wrappers.lambdaQuery(UserBenefits.class)
+				.eq(UserBenefits::getType, UserBenefits.Type.RENEW).eq(UserBenefits::getStatus, 1).select(UserBenefits::getSkuId)).stream().map(UserBenefits::getSkuId).collect(Collectors.toList());
+
 		list.getDataList().forEach(ticket -> {
 			if (ticket.getSkuId() == null || ticket.getGoodsId() == null) {
 				return;
@@ -233,6 +238,7 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 					remainDays = days + 1 + "天";
 				}
 				ticket.setRemainDays(remainDays);
+				ticket.setTotal(DateUtil.betweenDay(ticket.getStartTime(), ticket.getExpiryTime(), false));
 			}
 			OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
 					.eq(OrderDon::getRelationId, ticket.getRelationId())
@@ -299,6 +305,10 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 
 			//续费升级折扣
 			setRenewDiscount(ticket);
+
+			if (CollUtil.isNotEmpty(renewReduceSkuIds) && renewReduceSkuIds.contains(ticket.getSkuId())) {
+				ticket.setIsRenewReduce(true);
+			}
 		});
 		userLoginRecordService.saveUserLoginDayRecord(userId);
 		return list;
@@ -639,6 +649,9 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 
 	@Override
 	public void setGroupsTripsParkingTimeEmpty(GoodsDonSku sku, Long relationId) {
+		if (sku == null) {
+			return;
+		}
 		Long skuId = sku.getId();
 		if (skuId == 161l || skuId == 178l) {
 			GroupsRelation relation = groupsRelationMapper.selectById(relationId);
@@ -735,6 +748,8 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 			redisService.setNx(RedisService.key.TICKET_EXPIRY_NOTIFY_DAY.getNameFormat(thisDate, userId), userId, RedisService.key.TICKET_EXPIRY_NOTIFY_DAY.getTimeout());
 			return null;
 		}
+		List<Long> renewReduceSkuIds = userBenefitsMapper.selectList(Wrappers.lambdaQuery(UserBenefits.class)
+				.eq(UserBenefits::getType, UserBenefits.Type.RENEW).eq(UserBenefits::getStatus, 1).select(UserBenefits::getSkuId)).stream().map(UserBenefits::getSkuId).collect(Collectors.toList());
 		//提前7天
 		AtomicInteger day = new AtomicInteger(7);
 		List<RenewalView> expiryTickets = list.stream().filter(ticket -> {
@@ -758,6 +773,9 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 				//GPT镜像信息
 				setGptMirrorInfo(ticket);
 				ticket.setExpiryTime(null);
+				if (CollUtil.isNotEmpty(renewReduceSkuIds) && renewReduceSkuIds.contains(ticket.getSkuId())) {
+					ticket.setIsRenewReduce(true);
+				}
 				return true;
 			}
 			return false;

+ 14 - 0
netflix-service/src/main/java/com/cyksj/service/user/UserBenefitsBackService.java

@@ -0,0 +1,14 @@
+package com.cyksj.service.user;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.cyksj.model.entity.UserBenefits;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: userBenefitsBackService
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 15:21
+ */
+public interface UserBenefitsBackService extends IService<UserBenefits> {
+	void saveOrUpdatePojo(UserBenefits userBenefits) throws Exception;
+}

+ 30 - 0
netflix-service/src/main/java/com/cyksj/service/user/impl/UserBenefitsBackServiceImpl.java

@@ -0,0 +1,30 @@
+package com.cyksj.service.user.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.mapper.UserBenefitsMapper;
+import com.cyksj.model.entity.UserBenefits;
+import com.cyksj.service.user.UserBenefitsBackService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsBackServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 15:22
+ */
+@Service
+public class UserBenefitsBackServiceImpl extends ServiceImpl<UserBenefitsMapper, UserBenefits> implements UserBenefitsBackService {
+
+	@Override
+	public void saveOrUpdatePojo(UserBenefits userBenefits) throws Exception {
+		List<UserBenefits.RenewConfig> renewConfigList = userBenefits.getRenewConfigList();
+		if (CollUtil.isNotEmpty(renewConfigList)) {
+			userBenefits.setRenewConfig(Jsons.toJson(renewConfigList));
+		}
+		this.saveOrUpdate(userBenefits);
+	}
+}

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

@@ -133,6 +133,8 @@ public class GroupRelationController {
 
     private final UpgradeOrderDonMapper upgradeOrderDonMapper;
 
+    private final UserBenefitsOrderRecordMapper userBenefitsOrderRecordMapper;
+
     private final static Sequence SEQUENCE = new Sequence(0);
 
     @RequestMapping("/get/renewal")
@@ -211,6 +213,7 @@ public class GroupRelationController {
         List<UserTicketView> ticketViews = beanSearcher.searchAll(UserTicketView.class, MapUtils.flatBuilder(request.getParameterMap())
                 .field(UserTicketView::getUserId, userIds).op(Operator.InList)
                 .field(RenewalView::getStatus, List.of("validity", "outside")).op(Operator.InList)
+                .field(RenewalView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
                 .orderBy(UserTicketView::getRelationId).asc()
                 .build());
         return GatewayResponse.SUCCESS.newBuilder().toResult(ticketViews);
@@ -1031,4 +1034,30 @@ public class GroupRelationController {
         }
         return GatewayResponse.SUCCESS.newBuilder().toResult(code);
     }
+
+    /**
+     * 独立规格续费减免
+     */
+    @GetMapping("/get/renew/reduce")
+    public Result<UserBenefitsFrontView> getRenewReduce(Long skuId, Long relationId) {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+        UserBenefitsFrontView data = beanSearcher.searchFirst(UserBenefitsFrontView.class, MapUtils.builder().field(UserBenefitsFrontView::getSkuId, skuId).field(UserBenefitsFrontView::getType, UserBenefits.Type.RENEW.name()).build());
+        if (data == null) {
+            return GatewayResponse.SUCCESS.newBuilder().toResult();
+        }
+        Date startTime = null;
+        if (relationId != null) {
+            GroupsRelation relation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+                    .eq(GroupsRelation::getId, relationId)
+                    .in(GroupsRelation::getUserId, userIdList));
+            if (relation == null) {
+                throw BusinessRuntimeException.getInstance("车票不存在");
+            }
+            startTime = relation.getStartTime();
+        }
+        Integer renewCount = userBenefitsOrderRecordMapper.selectUseComboRecordByUid(userIdList, data.getType(), data.getGoodsId(), data.getSkuId(), Constant.noOrderAllStatus, null, startTime, relationId);
+        data.setRenewCount(renewCount);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(data);
+    }
 }

+ 22 - 2
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java

@@ -128,7 +128,7 @@ public class CmsOrderController {
 
 	@GetMapping("/get")
 	@SaCheckPermission("order:view")
-	public Result<SearchResult<OrderDonBackView>> get(Long channelId, String account, String showId, Long acId, String nickname, String register, Boolean isGoogle, Boolean isSubsidy, Boolean isRenewPackage) {
+	public Result<SearchResult<OrderDonBackView>> get(Long channelId, String account, String showId, Long acId, String nickname, String register, Boolean isGoogle, Boolean isSubsidy, Boolean isRenewPackage, Integer otherOrderType) {
 		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
 		if (channelId != null) {
 			List<Long> popularizeIds = channelPopularizeMapper.getPopularizeIdByChannelId(channelId);
@@ -184,6 +184,16 @@ public class CmsOrderController {
 			}
 			conditionSql += " exists (select 1 from order_don_renew_record odrr1 where odrr1.order_id = o.id limit 1)";
 		}
+		if (otherOrderType != null) {
+			//3.续费升级订单
+			if (otherOrderType == 3) {
+				builder.put("tb", String.format(" inner join order_don_renew_record odcr on odcr.order_id = o.id and odcr.order_type = 2"));
+			}
+			if (otherOrderType == 4) {
+				//4.用户权益订单
+				builder.put("tb", String.format(" inner join user_benefits_order_record ubor on ubor.order_id = o.id and ubor.type = 'RENEW'"));
+			}
+		}
 		if (StrUtil.isNotBlank(conditionSql)) {
 			builder.put("condition", conditionSql);
 		}
@@ -304,7 +314,7 @@ public class CmsOrderController {
 	@GetMapping("/export")
 	@SaCheckPermission("order:export")
 	@Log(module = "订单管理/导出订单", businessType = BusinessType.EXPORT, isSaveRequestData = true)
-	public void exportOrders(HttpServletResponse response, Long channelId, String account, String showId, Long acId, String nickname, String register, Boolean isGoogle, Boolean isSubsidy, Boolean isRenewPackage) throws InterruptedException {
+	public void exportOrders(HttpServletResponse response, Long channelId, String account, String showId, Long acId, String nickname, String register, Boolean isGoogle, Boolean isSubsidy, Boolean isRenewPackage, Integer otherOrderType) throws InterruptedException {
 		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
 		if (channelId != null) {
 			List<Long> popularizeIds = channelPopularizeMapper.getPopularizeIdByChannelId(channelId);
@@ -322,6 +332,16 @@ public class CmsOrderController {
 		if (StrUtil.isNotBlank(showId)) {
 			builder.field(ExcelOrderDonData::getUserId, userService.getUserIdByShowId(showId));
 		}
+		if (otherOrderType != null) {
+			//3.续费升级订单
+			if (otherOrderType == 3) {
+				builder.put("tb", String.format(" inner join order_don_renew_record odcr on odcr.order_id = o.id and odcr.order_type = 2"));
+			}
+			//4.用户权益订单
+			if (otherOrderType == 4) {
+				builder.put("tb", String.format(" inner join user_benefits_order_record ubor on ubor.order_id = o.id and ubor.type = 'RENEW'"));
+			}
+		}
 		String conditionSql = StrUtil.EMPTY;
 		if (acId != null) {
 			conditionSql += String.format(" o.user_id in (select user_id from corp_user where ac_id = %s)", acId);

+ 110 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/benefits/UserBenefitsController.java

@@ -0,0 +1,110 @@
+package com.cyksj.web.controller.manage.benefits;
+
+import cn.hutool.core.util.StrUtil;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.entity.UserBenefits;
+import com.cyksj.model.views.UserBenefitsView;
+import com.cyksj.service.mange.RefreshCacheService;
+import com.cyksj.service.user.UserBenefitsBackService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.SearchResult;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsController
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 14:27
+ */
+@RestController
+@RequestMapping("/manage/user/benefits")
+@RequiredArgsConstructor
+public class UserBenefitsController {
+	private final BeanSearcher beanSearcher;
+
+	private final HttpServletRequest request;
+
+	private final UserBenefitsBackService userBenefitsBackService;
+
+	private final RefreshCacheService refreshCacheService;
+
+	/**
+	 * 用户权益列表
+	 */
+	@GetMapping("/get")
+	public Result<SearchResult<UserBenefitsView>> get() {
+		SearchResult<UserBenefitsView> search = beanSearcher.search(UserBenefitsView.class, MapUtils.flatBuilder(request.getParameterMap())
+				.orderBy(UserBenefitsView::getSorted).asc()
+				.build());
+		search.getDataList().forEach(data->{
+			if (StrUtil.isNotBlank(data.getRenewConfig())) {
+				try {
+					data.setRenewConfigList(Jsons.parseList(data.getRenewConfig(), UserBenefits.RenewConfig.class));
+				} catch (Exception e) {
+				}
+			}
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 增加用户权益
+	 */
+	@PostMapping("/post")
+	public Result<String> saveUserBenefits(@RequestBody UserBenefits userBenefits) throws Exception {
+		userBenefitsBackService.saveOrUpdatePojo(userBenefits);
+		refreshCacheService.refreshUserBenefitsConfigCache();
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 编辑用户权益
+	 */
+	@PutMapping("/update")
+	public Result<String> updateUserBenefits(@RequestBody UserBenefits userBenefits) throws Exception {
+		userBenefitsBackService.saveOrUpdatePojo(userBenefits);
+		refreshCacheService.refreshUserBenefitsConfigCache();
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 删除用户权益
+	 */
+	@DeleteMapping("/delete/{id}")
+	public Result<String> deleteById(@PathVariable Long id) {
+		userBenefitsBackService.removeById(id);
+		refreshCacheService.refreshUserBenefitsConfigCache();
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 排序
+	 */
+	@PutMapping("/sorted")
+	public Result<String> sorted(@RequestBody UserBenefits userBenefits) {
+		Long id = userBenefits.getId();
+		UserBenefits byId = userBenefitsBackService.getById(id);
+		byId.setSorted(userBenefits.getSorted());
+		userBenefitsBackService.updateById(byId);
+		refreshCacheService.refreshUserBenefitsConfigCache();
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 上下架
+	 */
+	@PutMapping("/update/status/{id}")
+	public Result<String> updateStatusById(@PathVariable Long id) {
+		UserBenefits byId = userBenefitsBackService.getById(id);
+		byId.setStatus(!byId.getStatus());
+		userBenefitsBackService.updateById(byId);
+		refreshCacheService.refreshUserBenefitsConfigCache();
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+}

+ 44 - 5
netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java

@@ -131,6 +131,9 @@ public class OrderController {
     @Autowired
     private LumaService lumaService;
 
+    @Autowired
+    private UserBindRelationService userBindRelationService;
+
     /**
      * 支付
      * @author chan
@@ -173,11 +176,6 @@ public class OrderController {
             cookie.setPath("/");
             response.addCookie(userInfo);
         }
-        if (orderDon.getGoodsId() != null && orderDon.getGoodsId() == 26l) {
-            GoodsDonSku sku = skuMapper.selectById(orderDon.getSkuId());
-            orderDon.setIsMirror(sku.getIsMirror());
-        }
-
         return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
     }
 
@@ -205,6 +203,7 @@ public class OrderController {
      * 0元订单确认
      */
     @PostMapping("/post/order/status/{orderId}")
+    @Deprecated
     public Result<String> zeroOrderConfirm(@PathVariable Long orderId) {
         orderDonService.zeroOrderConfirm(orderId);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
@@ -214,6 +213,7 @@ public class OrderController {
      * 套餐0元订单确认
      */
     @PostMapping("/post/upgrade/order/status/{orderId}")
+    @Deprecated
     public Result<String> zeroUpgradeOrderConfirm(@PathVariable Long orderId) {
         orderDonService.zeroUpgradeOrderConfirm(orderId);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
@@ -897,4 +897,43 @@ public class OrderController {
                 .build());
         return GatewayResponse.SUCCESS.newBuilder().toResult(list);
     }
+
+    /**
+     * 用户权益预支付
+     */
+    @PostMapping("/user/benefits/submit")
+    public Result<OrderDon> userBenefitsSubmit(@RequestBody OrderPayRequest orderPayRequest) throws Exception {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        orderPayRequest.setUserId(userId);
+        String ip = ServletUtil.getClientIP(request);
+        orderPayRequest.setIp(ip);
+        OrderDon orderDon = orderDonService.userBenefitsSubmit(orderPayRequest);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
+    }
+
+    /**
+     * 是否是实物可选多张车票权益记录
+     */
+    @GetMapping("/get/combo/optional/ticket/record/{orderId}")
+    public Result<UserPayEquipmentAvailableBenefits> getComboOptionalTicketRecord(@PathVariable Long orderId) {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        UserPayEquipmentAvailableBenefits userPayEquipmentAvailableBenefits = orderDonService.getComboOptionalTicketRecord(userId, orderId);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(userPayEquipmentAvailableBenefits);
+    }
+
+    /**
+     * 是否购买过实物设备
+     */
+    @GetMapping("/has/paid/equipment")
+    public Result<Boolean> hasPaidEquipment() {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+        //是否买过设备
+        Integer selectCount = orderDonService.count(Wrappers.lambdaQuery(OrderDon.class)
+                .eq(OrderDon::getGoodsId, 15)
+                .in(OrderDon::getUserId, userIdList)
+                .notIn(OrderDon::getStatus, Constant.noOrderAllStatus));
+        return GatewayResponse.SUCCESS.newBuilder().toResult(selectCount > 0);
+    }
+
 }

+ 241 - 0
netflix-web/src/main/java/com/cyksj/web/controller/user/UserBenefitsFrontController.java

@@ -0,0 +1,241 @@
+package com.cyksj.web.controller.user;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.constant.Constant;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.*;
+import com.cyksj.mapper.manage.coupon.CouponSkuMapper;
+import com.cyksj.mapper.manage.coupon.CouponUserMapper;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.manage.request.ReqGoodsSkuSpec;
+import com.cyksj.model.views.*;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.user.UserBindRelationService;
+import com.cyksj.web.util.StpUserUtil;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapBuilder;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserBenefitsFrontController
+ * 创建者: JavaZou
+ * 创建时间:2024/9/23 15:32
+ */
+@RestController
+@RequestMapping("/applets/user/benefits")
+@RequiredArgsConstructor
+@Slf4j
+public class UserBenefitsFrontController {
+	private final HttpServletRequest request;
+
+	private final BeanSearcher beanSearcher;
+
+	private final OrderDonMapper orderDonMapper;
+
+	private final UserBindRelationService userBindRelationService;
+
+	private final UserStudentCertRecordMapper studentCertRecordMapper;
+
+	private final UserBenefitsOrderRecordMapper userBenefitsOrderRecordMapper;
+
+	private final CouponUserMapper couponUserMapper;
+
+	private final CouponSkuMapper couponSkuMapper;
+
+	private final RedisService redisService;
+
+	private final UserActivityMapper userActivityMapper;
+
+	private final GroupsRelationMapper relationMapper;
+
+
+	/**
+	 * 权益分类
+	 */
+	@GetMapping("/get/classification")
+	public Result<Map<UserBenefits.Type, List<UserBenefitsFrontView>>> getUserBenefitsClassification() {
+		String key = RedisService.key.USER_BENEFITS_KEY.getName() + ":cfc";
+		long userId = StpUserUtil.getLoginIdAsLong();
+		Object o = redisService.get(key);
+		Map<UserBenefits.Type, List<UserBenefitsFrontView>> classifications = null;
+		List<UserBenefitsFrontView> userBenefitsFrontViews;
+		if (o == null) {
+			userBenefitsFrontViews = beanSearcher.searchAll(UserBenefitsFrontView.class, MapUtils.builder().onlySelect(UserBenefitsFrontView::getId, UserBenefitsFrontView::getType, UserBenefitsFrontView::getDescription).build());
+			classifications = userBenefitsFrontViews.stream().collect(Collectors.groupingBy(UserBenefitsFrontView::getType, LinkedHashMap::new, Collectors.toList()));
+			redisService.set(key, classifications, RedisService.key.USER_BENEFITS_KEY.getTimeout() + RandomUtil.randomInt(1800));
+		} else {
+			classifications = (Map<UserBenefits.Type, List<UserBenefitsFrontView>>) o;
+		}
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		classifications.values().forEach(list -> setDataInfo(list, null, userIdList, true, userId));
+		return GatewayResponse.SUCCESS.newBuilder().toResult(classifications);
+	}
+
+	/**
+	 * 用户权益
+	 */
+	@GetMapping("/get")
+	public Result<List<UserBenefitsFrontView>> getUserBenefits(UserBenefits.Type type) {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		String key = RedisService.key.USER_BENEFITS_KEY.getName();
+		if (type != null) {
+			key += ":type:" + type.name();
+		}
+		Object o = redisService.get(key);
+		List<UserBenefitsFrontView> userBenefitsFrontViews;
+		if (o == null) {
+			MapBuilder builder = MapUtils.builder();
+			if (type != null) {
+				builder.field(UserBenefitsFrontView::getType, type.name());
+			}
+			userBenefitsFrontViews = beanSearcher.searchAll(UserBenefitsFrontView.class, builder.build());
+			if (CollUtil.isEmpty(userBenefitsFrontViews)) {
+				return GatewayResponse.SUCCESS.newBuilder().toResult();
+			}
+			redisService.setNx(key, userBenefitsFrontViews, RedisService.key.USER_BENEFITS_KEY.getTimeout());
+		} else {
+			userBenefitsFrontViews = (List<UserBenefitsFrontView>) o;
+		}
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		setDataInfo(userBenefitsFrontViews, type, userIdList, false, userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(userBenefitsFrontViews);
+	}
+
+
+	public void setDataInfo(List<UserBenefitsFrontView> userBenefitsFrontViews, UserBenefits.Type type, List<Long> userIdList, Boolean isAll, Long userId) {
+		for (UserBenefitsFrontView data : userBenefitsFrontViews) {
+			if (isAll || type == null) {
+				type = data.getType();
+			}
+			Integer fhCount = 0;
+			if (type == UserBenefits.Type.COMBO) {
+				//是否已经购买实物
+				fhCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class).in(OrderDon::getUserId, userIdList).eq(OrderDon::getGoodsId, Constant.NF_EP_GID).notIn(OrderDon::getStatus, Constant.noOrderAllStatus));
+				//近一年是否已经使用过该权益
+				DateTime lastYear = DateUtil.offsetMonth(DateTime.now(), -12);
+				//是否已经使用过该权益
+				Integer comboCount = userBenefitsOrderRecordMapper.selectUseComboRecordByUid(userIdList, data.getType(), data.getGoodsId(), null, Constant.noOrderStatus, lastYear, null, null);
+				data.setIsComboUsed(comboCount > 0);
+
+				if (StrUtil.isNotBlank(data.getComboSkuIds())) {
+					List<GoodsDonSkuFrontView> comboSkuInfo = null;
+					try {
+						comboSkuInfo = beanSearcher.searchAll(GoodsDonSkuFrontView.class, MapUtils.builder().field(GoodsDonSkuFrontView::getId, Jsons.parseList(data.getComboSkuIds(), Long.class)).op(Operator.InList)
+								.selectExclude(GoodsDonSkuFrontView::getSpecJson, GoodsDonSkuFrontView::getImg)
+								.build());
+						GoodsDonSpec goodsDonSpec = beanSearcher.searchFirst(GoodsDonSpec.class, MapUtils.builder().field(GoodsDonSpec::getGoodsId, 15).build());
+						List<ReqGoodsSkuSpec> specs = Jsons.parseList(goodsDonSpec.getSpecsJson(), ReqGoodsSkuSpec.class);
+						Map<String, List<ReqGoodsSkuSpec>> collect = specs.stream().collect(Collectors.groupingBy(ReqGoodsSkuSpec::getKey));
+						ReqGoodsSkuSpec reqGoodsSkuSpec = collect.get("套餐").get(0);
+						data.setGoodsSpecJson(Jsons.toJson(reqGoodsSkuSpec));
+					} catch (Exception e) {
+						log.error("解析实物套餐json错误,:{}", StringUtil.getErrorText(e));
+					}
+					data.setComboSkuInfo(comboSkuInfo);
+				}
+			}
+			if (type == UserBenefits.Type.RENEW) {
+				//是否购买过该规格套餐
+				Long skuId = data.getSkuId();
+				Number tickets = beanSearcher.searchCount(RenewalView.class, MapUtils.builder()
+						.field(RenewalView::getSkuId, skuId)
+						.field(RenewalView::getUserId, userIdList).op(Operator.InList)
+						.field(RenewalView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
+						.build());
+				fhCount = tickets.intValue();
+			}
+			if (type == UserBenefits.Type.CERT) {
+				//是否进行大学生认证过
+				fhCount = studentCertRecordMapper.selectCount(Wrappers.lambdaQuery(UserStudentCertRecord.class)
+						.in(UserStudentCertRecord::getUserId, userIdList));
+				try {
+					//获取大学生认证优惠券
+					UserActivity userActivity = userActivityMapper.selectOne(Wrappers.lambdaQuery(UserActivity.class)
+							.eq(UserActivity::getType, 0)
+							.eq(UserActivity::getDeleted, 1)
+							.last("limit 1"));
+					String couponIdsStr = userActivity.getCouponIds();
+					List<Long> couponIds = Jsons.parseList(couponIdsStr, Long.class);
+					//优惠券使用详情
+					if (fhCount > 0) {
+						List<CouponUserView> couponUserViews = couponUserMapper.couponPersonalByCouponIds(List.of(userId), couponIds);
+						couponUserViews.forEach(couponUserView -> {
+							String str = couponSkuMapper.selectGoodsSkuStr(couponUserView.getCouponId());
+							couponUserView.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+							List<CouponSkuView> couponSkus = couponSkuMapper.getGoodsSkuByCouponId(couponUserView.getCouponId());
+							try {
+								couponUserView.setGoodsSkuIds(Jsons.toJson(couponSkus));
+								if (StrUtil.isEmpty(couponUserView.getGoodsImg())) {
+									Long goodsId = couponSkus.get(0).getGoodsId();
+									GoodsDon goodsDon = beanSearcher.searchFirst(GoodsDon.class, MapUtils.builder().field(GoodsDon::getId, goodsId).build());
+									couponUserView.setGoodsImg(goodsDon.getImg());
+								}
+							} catch (Exception e) {
+
+							}
+						});
+						data.setCouponUserViews(couponUserViews);
+					} else {
+						List<CouponView> couponViews = beanSearcher.searchAll(CouponView.class, MapUtils.builder().field(CouponView::getId, couponIds).op(Operator.InList).build());
+						couponViews.forEach(couponView -> {
+							String str = couponSkuMapper.selectGoodsSkuStr(couponView.getId());
+							couponView.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+							List<CouponSkuView> couponSkus = couponSkuMapper.getGoodsSkuByCouponId(couponView.getId());
+							try {
+								couponView.setGoodsSkuIds(Jsons.toJson(couponSkus));
+								if (StrUtil.isEmpty(couponView.getGoodsImg())) {
+									Long goodsId = couponSkus.get(0).getGoodsId();
+									GoodsDon goodsDon = beanSearcher.searchFirst(GoodsDon.class, MapUtils.builder().field(GoodsDon::getId, goodsId).build());
+									couponView.setGoodsImg(goodsDon.getImg());
+								}
+							} catch (Exception e) {
+
+							}
+						});
+						data.setCouponViews(couponViews);
+					}
+				} catch (Exception e) {
+					log.error("解析大学生认证json错误,:{}", StringUtil.getErrorText(e));
+				}
+			}
+			data.setIsFh(fhCount > 0);
+		}
+	}
+
+	/**
+	 * 续费权益 车票续费次数
+	 */
+	@GetMapping("/get/renew/num")
+	public Result<Integer> getRenewNum(Long relationId) {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		RenewalView renewalViews = beanSearcher.searchFirst(RenewalView.class, MapUtils.builder()
+				.field(RenewalView::getRelationId, relationId)
+				.field(RenewalView::getUserId, userIdList).op(Operator.InList)
+				.field(RenewalView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
+				.build());
+		Integer renewCount = userBenefitsOrderRecordMapper.selectUseComboRecordByUid(userIdList, UserBenefits.Type.RENEW, renewalViews.getGoodsId(), renewalViews.getSkuId(), Constant.noOrderAllStatus, null, renewalViews.getStartTime(), renewalViews.getRelationId());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(renewCount);
+	}
+}