zoujiajian 1 vuosi sitten
vanhempi
sitoutus
23103d53b9
22 muutettua tiedostoa jossa 984 lisäystä ja 1 poistoa
  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/UserBenefitsMapper.java
  3. 21 0
      netflix-dao/src/main/java/com/cyksj/mapper/UserBenefitsOrderRecordMapper.java
  4. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponUserMapper.java
  5. 4 0
      netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java
  6. 104 0
      netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefits.java
  7. 32 0
      netflix-dao/src/main/java/com/cyksj/model/entity/UserBenefitsOrderRecord.java
  8. 5 0
      netflix-dao/src/main/java/com/cyksj/model/request/OrderPayRequest.java
  9. 125 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserBenefitsFrontView.java
  10. 94 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserBenefitsView.java
  11. 44 0
      netflix-dao/src/main/resources/mapper/CouponUserMapper.xml
  12. 33 0
      netflix-dao/src/main/resources/mapper/UserBenefitsOrderRecordMapper.xml
  13. 1 0
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  14. 5 0
      netflix-service/src/main/java/com/cyksj/service/mange/RefreshCacheService.java
  15. 6 0
      netflix-service/src/main/java/com/cyksj/service/mange/impl/RefreshCacheServiceImpl.java
  16. 2 0
      netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java
  17. 149 1
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  18. 14 0
      netflix-service/src/main/java/com/cyksj/service/user/UserBenefitsBackService.java
  19. 30 0
      netflix-service/src/main/java/com/cyksj/service/user/impl/UserBenefitsBackServiceImpl.java
  20. 110 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/benefits/UserBenefitsController.java
  21. 13 0
      netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java
  22. 172 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

@@ -393,4 +393,9 @@ public interface Constant {
 	String WHOLESALE_DEPOSIT_KEY = "wholesale_deposit_key";
 
 	String SHOP = "店铺";
+
+	/**
+	 * 奈飞设备平台id
+	 */
+	Long NF_EP_GID = 15l;
 }

+ 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> {
+}

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

@@ -0,0 +1,21 @@
+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.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);
+
+	Integer selectNoPaymentRecord(@Param("userIdList") 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("couponIds") List<Long> couponIds);
 }

+ 4 - 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;
@@ -345,6 +346,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);
         }
     }

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

@@ -0,0 +1,104 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+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 couponIds;
+
+	/**
+	 * 实物套餐权益 生效套餐ids
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String comboSkuIds;
+
+	/**
+	 * 续费减免生效规格(购买那个规格可享受权益)
+	 */
+	private Long skuId;
+
+	/**
+	 * 续费减免配置str
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String renewConfig;
+
+	/**
+	 * 续费减免配置 传入参数
+	 */
+	private List<RenewConfig> renewConfigList;
+
+	/**
+	 * 图片撇脂
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String imgConfig;
+
+	/**
+	 * 描述
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String description;
+
+	/**
+	 * 续费减免前几次减免
+	 */
+	private Integer start;
+
+	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;
+	}
+}

+ 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/request/OrderPayRequest.java

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

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

@@ -0,0 +1,125 @@
+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 com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+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 = "ud.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;
+
+	/**
+	 * 优惠券ids
+	 */
+	@DbField("ub.coupon_ids")
+	private String couponIds;
+
+	/**
+	 * 实物套餐权益 生效套餐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("ub.renew_config")
+	@JsonIgnore
+	private String renewConfig;
+
+	/**
+	 * 续费减免配置
+	 */
+	@DbField("ub.renew_config")
+	private List<UserBenefits.RenewConfig> renewConfigList;
+
+	/**
+	 * 图片撇脂
+	 */
+	@DbField("ub.img_config")
+	private String imgConfig;
+
+	/**
+	 * 续费减免前几次减免
+	 */
+	@DbField("ub.start")
+	private Integer start;
+
+	/**
+	 * 是否符合条件
+	 */
+	@DbIgnore
+	private Boolean isFh;
+
+	/**
+	 * 使用过套餐权益
+	 */
+	@DbIgnore
+	private Boolean isComboUsed;
+
+	/**
+	 * 续费次数
+	 */
+	@DbIgnore
+	private Integer renewCount;
+
+	/**
+	 * 用户认证优惠券详情
+	 */
+	@DbIgnore
+	private List<CouponUserView> couponUserViews;
+
+	/**
+	 * 认证优惠券展示
+	 */
+	@DbIgnore
+	private List<CouponView> couponViews;
+}

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

@@ -0,0 +1,94 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.UserBenefits;
+import com.ejlchina.searcher.bean.DbField;
+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.coupon_ids")
+	private String couponIds;
+
+	/**
+	 * 实物套餐权益 生效套餐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;
+
+	/**
+	 * 续费减免配置
+	 */
+	@DbField("ub.renew_config")
+	private List<UserBenefits.RenewConfig> renewConfigList;
+
+	/**
+	 * 图片撇脂
+	 */
+	@DbField("ub.img_config")
+	private String imgConfig;
+
+	/**
+	 * 续费减免前几次减免
+	 */
+	@DbField("ub.start")
+	private Integer start;
+
+	@DbField("ub.sorted")
+	private Integer sorted;
+
+	@DbField("ud.status")
+	private Boolean status;
+
+	@DbField("ub.created_time")
+	private Date createdTime;
+
+	@DbField("ub.update_time")
+	private Date updateTime;
+}

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

@@ -240,4 +240,48 @@
         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 = #{userId}
+        and cu.coupon_id in
+        <foreach collection="couponIds" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+        and cu.channel != 'exCode'
+        order by `orderby`
+    </select>
 </mapper>

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

@@ -0,0 +1,33 @@
+<?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_use_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>) 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>
+    </select>
+
+    <select id="selectNoPaymentRecord" resultType="java.lang.Integer">
+        select count(1) from (select order_id from user_benefits_use_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

@@ -847,6 +847,7 @@ public class RedisService {
         LUMA_NUM_LIMIT("luma_num_limit:", "luma次数", 60 * 60 * 48L),
         NF_SET_TIME_LIMIT("nf_set_time_limit:", "设置pin码", 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;

+ 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();
 }

+ 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/OrderDonService.java

@@ -106,4 +106,6 @@ public interface OrderDonService extends IService<OrderDon> {
     UpgradeOrderDetailView getUpgradeOrderDetail(Long orderId, Long userId);
 
 	OrderDon submitRenewUpgrade(OrderPayRequest payRequest);
+
+	OrderDon userBenefitsSubmit(OrderPayRequest orderPayRequest) throws Exception;
 }

+ 149 - 1
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -329,6 +329,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final OrderDonUserDiscountPopularizeRecordMapper orderDonUserDiscountPopularizeRecordMapper;
 
+	private final UserBenefitsMapper userBenefitsMapper;
+
+	private final UserBenefitsOrderRecordMapper userBenefitsOrderRecordMapper;
+
 	private final List<String> noChekGoodsTemp = List.of("Apple One", "Youtube", "Spotify");
 
 	@Transactional(rollbackFor = Throwable.class)
@@ -678,7 +682,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) {
@@ -3291,7 +3308,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 -> {
@@ -3897,6 +3914,137 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		return orderDon;
 	}
 
+	@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());
+		GoodsDonSku sku = goodsDonSkuMapper.selectById(userBenefits.getSkuId());
+		orderDon.setMoney(sku.getPrice());
+		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);
+			String renewConfig = userBenefits.getRenewConfig();
+			List<UserBenefits.RenewConfig> renewConfigs = Jsons.parseList(renewConfig, UserBenefits.RenewConfig.class);
+			Integer start = userBenefits.getStart();
+			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));
+		}
+		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());
+
+		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);
+		return orderDon;
+	}
+
+	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);
+				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("你未完成该权益目标");
+		}
+	}
+
+
 	/**
 	 * 试用车票跳转购买正式车票订单记录
 	 */

+ 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);
+	}
+}

+ 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("/delele/{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();
+	}
+}

+ 13 - 0
netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java

@@ -881,4 +881,17 @@ public class OrderController {
         }
         return GatewayResponse.SUCCESS.newBuilder().toResult(search);
     }
+
+    /**
+     * 用户权益预支付
+     */
+    @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);
+        orderDonService.userBenefitsSubmit(orderPayRequest);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
 }

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

@@ -0,0 +1,172 @@
+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.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.OrderDonMapper;
+import com.cyksj.mapper.UserBenefitsOrderRecordMapper;
+import com.cyksj.mapper.UserCertRecordMapper;
+import com.cyksj.mapper.manage.coupon.CouponSkuMapper;
+import com.cyksj.mapper.manage.coupon.CouponUserMapper;
+import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.entity.UserBenefits;
+import com.cyksj.model.entity.UserCertRecord;
+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.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.List;
+
+/**
+ * 项目名: 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 UserCertRecordMapper userCertRecordMapper;
+
+	private final UserBenefitsOrderRecordMapper userBenefitsOrderRecordMapper;
+
+	private final CouponUserMapper couponUserMapper;
+
+	private final CouponSkuMapper couponSkuMapper;
+
+	private final RedisService redisService;
+
+	@GetMapping("/get")
+	public Result<List<UserBenefitsFrontView>> getUserBenefits(UserBenefits.Type type) {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		Object o = redisService.get(RedisService.key.USER_BENEFITS_KEY.getName());
+		List<UserBenefitsFrontView> userBenefitsFrontViews;
+		if (o == null) {
+			userBenefitsFrontViews = beanSearcher.searchAll(UserBenefitsFrontView.class, MapUtils.builder().build());
+			if (CollUtil.isEmpty(userBenefitsFrontViews)) {
+				return GatewayResponse.SUCCESS.newBuilder().toResult();
+			}
+			redisService.setNx(RedisService.key.USER_BENEFITS_KEY.getName(), userBenefitsFrontViews, RedisService.key.USER_BENEFITS_KEY.getTimeout());
+		} else {
+			userBenefitsFrontViews = (List<UserBenefitsFrontView>) o;
+		}
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		setDataInfo(userBenefitsFrontViews, type, userIdList);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(userBenefitsFrontViews);
+	}
+
+
+	public void setDataInfo(List<UserBenefitsFrontView> userBenefitsFrontViews, UserBenefits.Type type, List<Long> userIdList) {
+		for (UserBenefitsFrontView data : userBenefitsFrontViews) {
+			if (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);
+				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))
+								.selectExclude(GoodsDonSkuFrontView::getSpecJson, GoodsDonSkuFrontView::getSpecIds)
+								.build());
+					} catch (Exception e) {
+						log.error("解析实物套餐json错误,:{}", StringUtil.getErrorText(e));
+					}
+					data.setComboSkuInfo(comboSkuInfo);
+				}
+			}
+			if (type == UserBenefits.Type.RENEW) {
+				//是否购买过该规格套餐
+				Long skuId = data.getSkuId();
+				fhCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class).in(OrderDon::getUserId, userIdList).eq(OrderDon::getSkuId, skuId).notIn(OrderDon::getStatus, Constant.noOrderAllStatus));
+				Integer renewCount = userBenefitsOrderRecordMapper.selectUseComboRecordByUid(userIdList, data.getType(), data.getGoodsId(), data.getSkuId(), Constant.noOrderAllStatus, null);
+				data.setRenewCount(renewCount);
+
+				String renewConfig = data.getRenewConfig();
+				if (StrUtil.isNotBlank(renewConfig)) {
+					List<UserBenefits.RenewConfig> renewConfigs = null;
+					try {
+						renewConfigs = Jsons.parseList(renewConfig, UserBenefits.RenewConfig.class);
+						data.setRenewConfigList(renewConfigs);
+					} catch (Exception e) {
+						log.error("解析gpt续费json错误,:{}", StringUtil.getErrorText(e));
+					}
+				}
+			}
+			if (type == UserBenefits.Type.CERT) {
+				//是否进行大学生认证过
+				fhCount = userCertRecordMapper.selectCount(Wrappers.lambdaQuery(UserCertRecord.class)
+						.in(UserCertRecord::getUserId, userIdList));
+				try {
+					List<Long> couponIds = Jsons.parseList(data.getCouponIds(), Long.class);
+					//优惠券使用详情
+					if (fhCount > 0) {
+						List<CouponUserView> couponUserViews = couponUserMapper.couponPersonalByCouponIds(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));
+							} 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));
+							} catch (Exception e) {
+
+							}
+						});
+						data.setCouponViews(couponViews);
+					}
+				} catch (Exception e) {
+					log.error("解析大学生认证json错误,:{}", StringUtil.getErrorText(e));
+				}
+			}
+			data.setIsFh(fhCount > 0);
+		}
+	}
+}