Ver Fonte

银河用户会员

zoujiajian há 1 ano atrás
pai
commit
7128cf5813
75 ficheiros alterados com 2991 adições e 61 exclusões
  1. 15 0
      netflix-common/src/main/java/com/cyksj/common/constant/Constant.java
  2. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/OrderDonRenewRecordMapper.java
  3. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/vip/RefundVipOrderDonMapper.java
  4. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/vip/VipGoodsSkuMapper.java
  5. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/vip/VipOrderDonDeductRelationMapper.java
  6. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/vip/VipOrderDonMapper.java
  7. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/vip/VipUserMapper.java
  8. 33 0
      netflix-dao/src/main/java/com/cyksj/model/dto/VipDeductDto.java
  9. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/ChatgptUser.java
  10. 5 2
      netflix-dao/src/main/java/com/cyksj/model/entity/ClaudeUser.java
  11. 4 1
      netflix-dao/src/main/java/com/cyksj/model/entity/GrokUser.java
  12. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/GroupsRelation.java
  13. 2 0
      netflix-dao/src/main/java/com/cyksj/model/entity/HomeManagementConfig.java
  14. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/LumaUser.java
  15. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/MidjourneyUser.java
  16. 54 0
      netflix-dao/src/main/java/com/cyksj/model/entity/RefundVipOrderDon.java
  17. 2 0
      netflix-dao/src/main/java/com/cyksj/model/entity/UserBalanceSourceRecord.java
  18. 7 1
      netflix-dao/src/main/java/com/cyksj/model/entity/UserTicketClearedRecord.java
  19. 18 0
      netflix-dao/src/main/java/com/cyksj/model/entity/VipGoodsSku.java
  20. 163 0
      netflix-dao/src/main/java/com/cyksj/model/entity/VipOrderDon.java
  21. 23 0
      netflix-dao/src/main/java/com/cyksj/model/entity/VipOrderDonDeductRelation.java
  22. 22 0
      netflix-dao/src/main/java/com/cyksj/model/entity/VipUser.java
  23. 6 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/GroupsRelationBackView.java
  24. 3 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/GroupsRelationView.java
  25. 6 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/UserView.java
  26. 3 0
      netflix-dao/src/main/java/com/cyksj/model/request/OrderRefundReq.java
  27. 68 0
      netflix-dao/src/main/java/com/cyksj/model/request/VipOrderDonPayReq.java
  28. 39 0
      netflix-dao/src/main/java/com/cyksj/model/response/VipUserPageResp.java
  29. 93 0
      netflix-dao/src/main/java/com/cyksj/model/views/CmsVipOrderDonView.java
  30. 9 0
      netflix-dao/src/main/java/com/cyksj/model/views/GoodsFrontListCategoryView.java
  31. 12 0
      netflix-dao/src/main/java/com/cyksj/model/views/RenewalView.java
  32. 18 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserNumJhView.java
  33. 32 0
      netflix-dao/src/main/java/com/cyksj/model/views/UserSyncInfoView.java
  34. 94 0
      netflix-dao/src/main/java/com/cyksj/model/views/VipGoodsSkuFrontView.java
  35. 33 0
      netflix-dao/src/main/java/com/cyksj/model/views/VipGoodsSkuView.java
  36. 2 1
      netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml
  37. 13 0
      netflix-dao/src/main/resources/mapper/OrderDonRenewRecordMapper.xml
  38. 4 0
      netflix-service/src/main/java/com/cyksj/config/zfb/BaseZfbConfig.java
  39. 1 1
      netflix-service/src/main/java/com/cyksj/service/chatgpt/impl/ChatGptAccountServiceImpl.java
  40. 2 0
      netflix-service/src/main/java/com/cyksj/service/claude/ClaudeService.java
  41. 15 0
      netflix-service/src/main/java/com/cyksj/service/claude/impl/ClaudeServiceImpl.java
  42. 1 1
      netflix-service/src/main/java/com/cyksj/service/mange/MirrorUserCleatService.java
  43. 0 4
      netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java
  44. 21 5
      netflix-service/src/main/java/com/cyksj/service/mange/impl/MirrorUserCleatServiceImpl.java
  45. 13 0
      netflix-service/src/main/java/com/cyksj/service/mange/vip/CmsVipOrderService.java
  46. 15 0
      netflix-service/src/main/java/com/cyksj/service/mange/vip/VipCommonService.java
  47. 171 0
      netflix-service/src/main/java/com/cyksj/service/mange/vip/impl/CmsVipOrderServiceImpl.java
  48. 44 0
      netflix-service/src/main/java/com/cyksj/service/mange/vip/impl/VipCommonServiceImpl.java
  49. 3 0
      netflix-service/src/main/java/com/cyksj/service/midjourney/MidjourneyAccountService.java
  50. 8 4
      netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidJourneyAccountServiceImpl.java
  51. 2 0
      netflix-service/src/main/java/com/cyksj/service/order/OrderRefundService.java
  52. 29 0
      netflix-service/src/main/java/com/cyksj/service/order/VipOrderDonService.java
  53. 85 15
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderRefundServiceImpl.java
  54. 581 0
      netflix-service/src/main/java/com/cyksj/service/order/impl/VipOrderDonServiceImpl.java
  55. 2 0
      netflix-service/src/main/java/com/cyksj/service/pay/AliPayCommonService.java
  56. 36 0
      netflix-service/src/main/java/com/cyksj/service/pay/impl/AliPayCommonServiceImpl.java
  57. 119 0
      netflix-service/src/main/java/com/cyksj/service/paypal/PayPalService.java
  58. 131 5
      netflix-service/src/main/java/com/cyksj/service/paypal/PaypalV2Service.java
  59. 7 0
      netflix-service/src/main/java/com/cyksj/service/relation/GroupRelationFrontService.java
  60. 1 3
      netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationClearServiceImpl.java
  61. 121 2
      netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationFrontServiceImpl.java
  62. 3 0
      netflix-service/src/main/java/com/cyksj/service/user/UserService.java
  63. 22 5
      netflix-service/src/main/java/com/cyksj/service/user/impl/UserServiceImpl.java
  64. 16 0
      netflix-service/src/main/java/com/cyksj/service/vip/VipFrontService.java
  65. 240 0
      netflix-service/src/main/java/com/cyksj/service/vip/impl/VipFrontServiceImpl.java
  66. 1 1
      netflix-web/src/main/java/com/cyksj/web/controller/external/ExternalController.java
  67. 1 1
      netflix-web/src/main/java/com/cyksj/web/controller/goods/GoodsDonController.java
  68. 26 4
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsUserController.java
  69. 10 4
      netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/CmsGoodsDonController.java
  70. 1 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupRelationController.java
  71. 92 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/vip/CmsVipController.java
  72. 1 1
      netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java
  73. 13 0
      netflix-web/src/main/java/com/cyksj/web/controller/user/UserController.java
  74. 52 0
      netflix-web/src/main/java/com/cyksj/web/controller/vip/VipController.java
  75. 235 0
      netflix-web/src/main/java/com/cyksj/web/controller/vip/VipUserOrderController.java

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

@@ -468,6 +468,11 @@ public interface Constant {
 	 */
 	String USER_MIRROR_SHOP_USER_DEFAULT_IMG = "https://cdn2.yicanggongyi.com/picture/08944798d95e42f62f3bb63d8109dcf1-1732169951288.png";
 
+	/**
+	 * MJ
+	 */
+	Long MJ_GID = 26l;
+
 	/**
 	 * Claude pro
 	 */
@@ -584,4 +589,14 @@ public interface Constant {
 	 * openai file 域名
 	 */
 	String GPT_FILE_DOMAIN = "filesystem.site";
+
+	/**
+	 * VIP用户费用
+	 */
+	String VIP_FEE_KEY = "vip_fee_key";
+
+	/**
+	 * VIP title
+	 */
+	String VIP_TITLE = "银河超级AI会员";
 }

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

@@ -16,4 +16,6 @@ import java.util.List;
 public interface OrderDonRenewRecordMapper extends BaseMapper<OrderDonRenewRecord> {
 
 	OrderDon getOtherRenewSku(@Param("orderId") Long orderId, @Param("relationId") Long relationId, @Param("userIds") List<Long> userIds);
+
+	List<OrderDon> getOtherRenewSkus(@Param("orderId") Long orderId, @Param("relationId") Long relationId, @Param("userIds") List<Long> userIdList);
 }

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.vip;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.RefundVipOrderDon;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: RefundVipOrderDonMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/5/14 15:34
+ */
+public interface RefundVipOrderDonMapper extends BaseMapper<RefundVipOrderDon> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.vip;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.VipGoodsSku;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipGoodsSkuMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 11:06
+ */
+public interface VipGoodsSkuMapper extends BaseMapper<VipGoodsSku> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.vip;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.VipOrderDonDeductRelation;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipOrderDonDeductRelationMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/5/16 15:54
+ */
+public interface VipOrderDonDeductRelationMapper extends BaseMapper<VipOrderDonDeductRelation> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.vip;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.VipOrderDon;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipOrderDonMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 11:05
+ */
+public interface VipOrderDonMapper extends BaseMapper<VipOrderDon> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.vip;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.VipUser;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipUserMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 11:04
+ */
+public interface VipUserMapper extends BaseMapper<VipUser> {
+}

+ 33 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/VipDeductDto.java

@@ -0,0 +1,33 @@
+package com.cyksj.model.dto;
+
+import com.cyksj.model.manage.views.GroupsRelationView;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipDeductDto
+ * 创建者: JavaZou
+ * 创建时间:2025/5/16 15:40
+ */
+@Getter
+@Setter
+public class VipDeductDto {
+	/**
+	 * 抵扣车票ids
+	 */
+	private List<Long> deductRelationIds;
+
+	/**
+	 * 可抵扣金额
+	 */
+	private BigDecimal deductMoney;
+
+	/**
+	 * 抵扣车票
+	 */
+	private List<GroupsRelationView> relationUserViews;
+}

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

@@ -136,4 +136,9 @@ public class ChatgptUser extends BaseEntity {
      * 用户目前模式 0旧版 1新版
      */
     private Integer model;
+
+    /**
+     * 是否是VIP
+     */
+    private Boolean isVip;
 }

+ 5 - 2
netflix-dao/src/main/java/com/cyksj/model/entity/ClaudeUser.java

@@ -13,8 +13,6 @@ import java.util.Date;
  */
 @Data
 public class ClaudeUser extends BaseEntity {
-
-
     private String name;
 
     @DbField("userToken")
@@ -73,4 +71,9 @@ public class ClaudeUser extends BaseEntity {
      */
     @TableField(updateStrategy = FieldStrategy.IGNORED)
     private Date renewExpiryTime;
+
+    /**
+     * 是否是VIP
+     */
+    private Boolean isVip;
 }

+ 4 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/GrokUser.java

@@ -1,6 +1,5 @@
 package com.cyksj.model.entity;
 
-import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.ejlchina.searcher.bean.DbField;
 import lombok.Data;
@@ -50,4 +49,8 @@ public class GrokUser extends BaseEntity {
      */
     private Boolean isBlock;
 
+    /**
+     * 是否是VIP
+     */
+    private Boolean isVip;
 }

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

@@ -129,6 +129,11 @@ public class GroupsRelation extends BaseEntity {
      */
     private String pinCode;
 
+    /**
+     * 是否是VIP
+     */
+    private Boolean isVip;
+
 
     @AllArgsConstructor
     @Getter

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/HomeManagementConfig.java

@@ -115,6 +115,8 @@ public class HomeManagementConfig extends BaseEntity{
 		pcSec("pc首页副"),
 		pcDis("pc发现首页"),
 		h5Sec("h5副广告"),
+		h5Banner("H5横幅广告"),
+		pcBanner("pc横幅广告")
 		;
 		String desc;
 

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

@@ -50,4 +50,9 @@ public class LumaUser extends BaseEntity{
     @DbIgnore
     @TableField(exist = false)
     private Integer aqType;
+
+    /**
+     * 是否是VIP
+     */
+    private Boolean isVip;
 }

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

@@ -64,4 +64,9 @@ public class MidjourneyUser extends BaseEntity{
     @DbIgnore
     @TableField(exist = false)
     private Integer aqType;
+
+    /**
+     * 是否是VIP
+     */
+    private Boolean isVip;
 }

+ 54 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/RefundVipOrderDon.java

@@ -0,0 +1,54 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/*
+ *项目名: netflix
+ *文件名: RefundOrderDon
+ *创建者: JavaZou
+ *创建时间:2022/11/8 18:16
+ */
+@Getter
+@Setter
+public class RefundVipOrderDon extends BaseEntity{
+	private Long userId;
+
+	private Long orderId;
+
+	/**
+	 * 退款订单号
+	 */
+	private String outRefundNo;
+
+	private String goodsTitle;
+
+	/**
+	 * 订单总金额
+	 */
+	private BigDecimal totalFee;
+
+	private BigDecimal refundFee;
+
+	private String refundDesc;
+
+	/**
+	 * 退款方式
+	 */
+	private String refundMethod;
+
+	/**
+	 * 是否清空余额退款
+	 * 默认false
+	 */
+	private Boolean isClearBalance;
+
+	private String operator;
+
+	/**
+	 * 聊天记录
+	 */
+	private String chatImg;
+}

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/UserBalanceSourceRecord.java

@@ -44,6 +44,8 @@ public class UserBalanceSourceRecord extends BaseEntity {
 		distribute_rank("分销排行"),
 		wholesale_differ("返批发差价"),
 		wholesale_order("批发订单"),
+		vip_order("会员下单"),
+		vip_refund("会员退款"),
 		;
 		String desc;
 

+ 7 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/UserTicketClearedRecord.java

@@ -31,6 +31,11 @@ public class UserTicketClearedRecord extends BaseEntity{
 
 	private Long relationId;
 
+	/**
+	 * 镜像用户token
+	 */
+	private String userToken;
+
 	private Long yhsId;
 
 	private Integer num;
@@ -58,7 +63,8 @@ public class UserTicketClearedRecord extends BaseEntity{
 		abnormal("异常订单"),
 		dp("优惠加购订单退款"),
 		manual("手动清除"),
-		refund("退款")
+		refund("退款"),
+		vip_refund("退款"),
 		;
 		String desc;
 

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/VipGoodsSku.java

@@ -0,0 +1,18 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipGoodsSku
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 9:52
+ */
+@Getter
+@Setter
+public class VipGoodsSku extends BaseEntity{
+	private Long goodsId;
+
+	private Long skuId;
+}

+ 163 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/VipOrderDon.java

@@ -0,0 +1,163 @@
+package com.cyksj.model.entity;
+
+import cn.hutool.core.util.StrUtil;
+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.Arrays;
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipOrderDon
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 10:38
+ */
+@Getter
+@Setter
+public class VipOrderDon extends BaseEntity{
+
+	/** 订单号 */
+	private String orderNo ;
+
+	/**
+	 * 买家支付id
+	 */
+	private String buyerId;
+
+	/**
+	 * 微信订单号
+	 */
+	private String transactionId;
+
+
+	/** 用户id */
+	private Long userId ;
+
+	/**
+	 * openId
+	 */
+	private String openId;
+
+	/** 金额 */
+	private BigDecimal money;
+
+	/**
+	 * 海外支付金额
+	 */
+	private BigDecimal abroadMoney;
+
+	/**
+	 * 手续费
+	 */
+	private BigDecimal abroadTransactionFee;
+
+	/**
+	 * 退款金额
+	 */
+	private BigDecimal refundMoney;
+
+	/**
+	 * 退款余额
+	 */
+	private BigDecimal refundBalance;
+
+	/**
+	 * 退款原因
+	 */
+	private String refundDesc;
+
+	/** 支付状态 */
+	private Status status ;
+
+	/**
+	 * 支付类型
+	 */
+	private Type type;
+
+	/**
+	 * 订单类型 1普通订单、2续费订单
+	 * 默认值为1
+	 */
+	private Integer orderType;
+
+	/**
+	 * 支付时间
+	 */
+	private Date payTime;
+
+	/**
+	 * 商户id
+	 */
+	private String shopId;
+
+	/**
+	 * 渠道链接id
+	 */
+	private Long popularizeId;
+
+	/**
+	 * 是否是网页端支付 默认0移动端支付
+	 */
+	private Boolean isWebPay;
+
+	/**
+	 * 余额支付
+	 */
+	private BigDecimal balance;
+
+	private String payTitle;
+
+	/**
+	 * 付款说明
+	 */
+	@TableField(exist = false)
+	@DbIgnore
+	private String payDesc;
+
+	public enum Status {
+		/**
+		 * 订单状态
+		 */
+		close("已关闭"),
+		noPayment("待付款"),
+		hasPayment("已付款"),
+		complete("已完成"),
+		refund("已退款"),
+		;
+
+		private String desc;
+
+		Status(String desc) {
+			this.desc = desc;
+		}
+
+		public String getDesc() {
+			return desc;
+		}
+	}
+
+	@Getter
+	@AllArgsConstructor
+	public enum Type{
+		WeChat("wechat","微信"),
+		ALI_PAY("zfb","支付宝"),
+		BALANCE("balance", "余额支付"),
+		PAYPAL("paypal","paypal支付"),
+		STRIPE("stripe","stripe支付")
+		;
+		String type;
+		String desc;
+
+		public static VipOrderDon.Type getType(String type) {
+			if (StrUtil.isEmpty(type)) {
+				return ALI_PAY;
+			}
+			return Arrays.stream(VipOrderDon.Type.values()).filter(tp -> tp.getType().equals(type)).findFirst().orElse(ALI_PAY);
+		}
+	}
+}

+ 23 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/VipOrderDonDeductRelation.java

@@ -0,0 +1,23 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipOrderDonDeductRelation
+ * 创建者: JavaZou
+ * 创建时间:2025/5/16 15:53
+ */
+@Getter
+@Setter
+public class VipOrderDonDeductRelation extends BaseEntity{
+
+	private Long orderId;
+
+	private Long userId;
+
+	private String relationIds;
+
+	private Boolean status;
+}

+ 22 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/VipUser.java

@@ -0,0 +1,22 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipUser
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 9:51
+ */
+@Getter
+@Setter
+public class VipUser extends BaseEntity{
+	private Long userId;
+
+	private Date startTime;
+
+	private Date expiryTime;
+}

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

@@ -119,6 +119,12 @@ public class GroupsRelationBackView {
 	@DbField("gr.account")
 	private String account;
 
+	/**
+	 * 是否是VIP
+	 */
+	@DbField("gr.is_vip")
+	private Boolean isVip;
+
 	@DbIgnore
 	private Date updateExpiryTime;
 

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

@@ -123,6 +123,9 @@ public class GroupsRelationView {
     @JsonIgnore
     private Date update_time;
 
+    @DbField("gr.is_vip")
+    private Boolean isVip;
+
     /**
      * 账号
      */

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

@@ -193,4 +193,10 @@ public class UserView {
      */
     @DbIgnore
     private Boolean isCert;
+
+    @DbIgnore
+    private Boolean isVip = false;
+
+    @DbIgnore
+    private Date vipExpiryTime;
 }

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/model/request/OrderRefundReq.java

@@ -41,6 +41,9 @@ public class OrderRefundReq {
 	 */
 	private BigDecimal refundBalance;
 
+	/**
+	 * 仅仅退余额
+	 */
 	private Boolean isOnly;
 
 	private String operator;

+ 68 - 0
netflix-dao/src/main/java/com/cyksj/model/request/VipOrderDonPayReq.java

@@ -0,0 +1,68 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipOrderDonPayReq
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 11:15
+ */
+@Getter
+@Setter
+public class VipOrderDonPayReq {
+	private Long userId;
+
+	/**
+	 * 金额
+	 */
+	@NotNull(message = "请输入金额")
+	private BigDecimal donMoney;
+
+	/**
+	 * 渠道链接id
+	 */
+	private Long popularizeId;
+
+	/**
+	 * 是否是网页端支付 默认0移动端支付
+	 */
+	private Boolean isWebPay;
+
+	/**
+	 * 余额支付
+	 */
+	private BigDecimal balance;
+
+	private String callback;
+
+	private String callbackType;
+
+	private Long goodsId;
+
+	/**
+	 * 礼品卡现金支付
+	 */
+	private BigDecimal gcCash;
+
+	/**
+	 * 礼品卡现金支付所拥有id
+	 */
+	private List<Long> gcpIds;
+
+	private String ip;
+
+	private String location;
+
+	private String type;
+
+	/**
+	 * 是否抵扣
+	 */
+	private Boolean isDeduct = false;
+}

+ 39 - 0
netflix-dao/src/main/java/com/cyksj/model/response/VipUserPageResp.java

@@ -0,0 +1,39 @@
+package com.cyksj.model.response;
+
+import com.cyksj.model.manage.views.GroupsRelationView;
+import com.cyksj.model.views.VipGoodsSkuFrontView;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipUserPageResp
+ * 创建者: JavaZou
+ * 创建时间:2025/5/14 18:06
+ */
+@Getter
+@Setter
+public class VipUserPageResp {
+	/**
+	 * 可抵扣金额
+	 */
+	private BigDecimal deductMoney;
+
+	/**
+	 * vip 费用
+	 */
+	private BigDecimal vipFee;
+
+	/**
+	 * 关联skus
+	 */
+	private List<VipGoodsSkuFrontView> skus;
+
+	/**
+	 * 抵扣车票
+	 */
+	private List<GroupsRelationView> relationUserViews;
+}

+ 93 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CmsVipOrderDonView.java

@@ -0,0 +1,93 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.VipOrderDon;
+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.Date;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CmsVipOrderDonView
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 15:49
+ */
+@Getter
+@Setter
+@SearchBean(tables = "vip_order_don o left join user u on u.id = o.user_id",
+		where = ":condition:"
+)
+public class CmsVipOrderDonView {
+	@DbField("o.id")
+	private Long id;
+
+	@DbIgnore
+	private String channel;
+
+	@DbField("o.order_no")
+	private String orderNo;
+
+	@DbField("o.order_type")
+	private Integer orderType;
+
+	@DbField("o.transaction_id")
+	private String transactionId;
+
+	@DbField("o.user_id")
+	private Long userId;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("case when u.login_phone != '' then 'phone' when u.email != '' then 'email' when u.open_id != '' then 'wechat' else 'wechat' end")
+	private String register;
+
+	/**
+	 * 登录注册手机号
+	 */
+	@DbField("u.login_phone")
+	private String loginPhone;
+
+	/**
+	 * 登录注册邮箱
+	 */
+	@DbField("u.email")
+	private String email;
+
+	@DbField("o.money")
+	private BigDecimal money;
+
+	@DbField("o.balance")
+	private BigDecimal balance;
+
+	@DbField("o.refund_money")
+	private BigDecimal refundMoney;
+
+	@DbField("o.refund_balance")
+	private BigDecimal refundBalance;
+
+	@DbField("o.refund_desc")
+	private String refundDesc;
+
+	@DbField("o.status")
+	private VipOrderDon.Status status;
+
+	@DbField("o.type")
+	private VipOrderDon.Type type;
+
+	@DbField("o.popularize_id")
+	private Long popularizeId;
+
+	@DbField("o.pay_time")
+	private Date payTime;
+
+	@DbField("o.pay_title")
+	private String payTitle;
+
+	@DbField("o.created_time")
+	private Date createdTime;
+}

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

@@ -160,6 +160,15 @@ public class GoodsFrontListCategoryView {
 	@DbField("g.video")
 	private String video;
 
+	@DbField("g.match_img")
+	private String matchImg;
+
+	/**
+	 * 背景图
+	 */
+	@DbField("g.img")
+	private String goodsImg;
+
 	/**
 	 * 购买通知信息
 	 */

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

@@ -210,6 +210,12 @@ public class RenewalView {
     @DbField("gr.aq_type")
     private Integer aqType;
 
+    /**
+     * 是否是VIP
+     */
+    @DbField("gr.is_vip")
+    private Boolean isVip;
+
     @DbIgnore
     private Date payTime;
 
@@ -441,4 +447,10 @@ public class RenewalView {
      */
     @DbIgnore
     private Boolean receivedGptGift;
+
+    /**
+     * 是否是会员抵扣车票
+     */
+    @DbIgnore
+    private Boolean isVipDeduct = false;
 }

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/model/views/UserNumJhView.java

@@ -0,0 +1,18 @@
+package com.cyksj.model.views;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserNumJhView
+ * 创建者: JavaZou
+ * 创建时间:2025/5/15 17:32
+ */
+@Getter
+@Setter
+public class UserNumJhView {
+	private Integer realUserNum;
+
+	private Integer vipNum;
+}

+ 32 - 0
netflix-dao/src/main/java/com/cyksj/model/views/UserSyncInfoView.java

@@ -0,0 +1,32 @@
+package com.cyksj.model.views;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: UserSyncInfoView
+ * 创建者: JavaZou
+ * 创建时间:2025/5/22 11:39
+ */
+@Getter
+@Setter
+public class UserSyncInfoView {
+
+	/**
+	 * 是否是vip
+	 */
+	private Boolean isVip = false;
+
+	/**
+	 * 会员开始时间
+	 */
+	private Date vipStartTime;
+
+	/**
+	 * 会员过期时间
+	 */
+	private Date vipExpiryTime;
+}

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

@@ -0,0 +1,94 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipGoodsSkuFrontView
+ * 创建者: JavaZou
+ * 创建时间:2025/5/14 17:54
+ */
+@Getter
+@Setter
+@SearchBean(tables = "vip_goods_sku vgs left join goods_don g on g.id = vgs.goods_id" +
+		" left join goods_don_sku sku on sku.id = vgs.sku_id")
+public class VipGoodsSkuFrontView {
+
+	@DbField("vgs.goods_id")
+	private Long goodsId;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("vgs.sku_id")
+	private Long skuId;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+
+	/**
+	 * 价格
+	 */
+	@DbField("sku.price")
+	private BigDecimal price;
+
+
+	@DbField("sku.num")
+	private Integer skuNum;
+
+	/**
+	 * 限制次数
+	 */
+	@DbField("sku.gpt_limit_num")
+	private Integer gptLimitNum;
+
+	/**
+	 * 限制时间
+	 */
+	@DbField("sku.gpt_limit_time")
+	private Long gptLimitTime;
+
+	/**
+	 * mj fast次数
+	 */
+	@DbField("sku.mj_fast_num")
+	private Integer mjFastNum;
+
+	/**
+	 * mj relax次数
+	 */
+	@DbField("sku.mj_relax_num")
+	private Integer mjRelaxNum;
+
+	@DbField("sku.luma_num")
+	private Integer lumaNum;
+
+	/**
+	 * deepseek限制次数
+	 */
+	@DbField("sku.ds_limit_num")
+	private Integer dsLimitNum;
+
+	/**
+	 * deepseek限制时间
+	 */
+	@DbField("sku.ds_limit_time")
+	private Long dsLimitTime;
+
+	/**
+	 * 其他模型限制次数
+	 */
+	@DbField("sku.other_limit_num")
+	private Integer otherLimitNum;
+
+	/**
+	 * 其他模型限制时间
+	 */
+	@DbField("sku.other_limit_time")
+	private Long otherLimitTime;
+}

+ 33 - 0
netflix-dao/src/main/java/com/cyksj/model/views/VipGoodsSkuView.java

@@ -0,0 +1,33 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipGoodsSkuView
+ * 创建者: JavaZou
+ * 创建时间:2025/5/14 16:57
+ */
+@Getter
+@Setter
+@SearchBean(tables = "vip_goods_sku vgs left join goods_don g on g.id = vgs.goods_id" +
+		" left join goods_don_sku sku on sku.id = vgs.sku_id")
+public class VipGoodsSkuView {
+	@DbField("vgs.id")
+	private Long id;
+
+	@DbField("vgs.goods_id")
+	private Long goodsId;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("vgs.sku_id")
+	private Long skuId;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+}

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

@@ -21,7 +21,8 @@
         cmt = 0,
         aq_type = 1,
         invite_link = null,
-        contact = null
+        contact = null,
+        is_vip = 0
         where id = #{entity.id}
         <if test="expireTime != null">
             and expiry_time = #{expireTime}

+ 13 - 0
netflix-dao/src/main/resources/mapper/OrderDonRenewRecordMapper.xml

@@ -16,4 +16,17 @@
         order by sku.months desc,sku.gpt_limit_num desc
         limit 1
     </select>
+
+    <select id="getOtherRenewSkus" resultType="com.cyksj.model.entity.OrderDon">
+        select o.* from (select order_id from order_don_renew_record where order_id != #{orderId}
+        and is_valid = 0
+        and user_id in
+        <foreach collection="userIds" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>) orr inner join order_don o on o.id = orr.order_id
+        and o.status not in ('close','noPayment','refund')
+        and o.order_type = 2 and o.relation_id = #{relationId}
+        inner join goods_don_sku sku on sku.id = o.sku_id
+        order by sku.months desc,sku.gpt_limit_num desc
+    </select>
 </mapper>

+ 4 - 0
netflix-service/src/main/java/com/cyksj/config/zfb/BaseZfbConfig.java

@@ -42,12 +42,16 @@ public class BaseZfbConfig {
 
 	public static String zfb_register_login_return_url = "yinhe/web/registerTicket";
 
+	public static String zfb_vip_login_return_url = "AIMirror";
+
 	public static String notifyUrl = "applets/order/prepay/zfb/notify";
 
 	public static String upgradeNotifyUrl = "applets/order/prepay/zfb/upgrade/notify";
 
 	public static String zfb_register_notify_url = "applets/register/prepay/zfb/notify";
 
+	public static String zfb_vip_notify_url = "/applet/vip/orders/zfb/notify";
+
 	public static String wholesaleNotifyUrl = "applet/wholesale/zfb/notify";
 
 	public static String wholesaleDepositNotifyUrl = "applet/wholesale/deposit/zfb/notify";

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/chatgpt/impl/ChatGptAccountServiceImpl.java

@@ -131,7 +131,7 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
     @Override
     public void clearGptNum(Long relationId) throws Exception {
         HttpRequest request = new HttpRequest("http://154.198.213.52:9611/cleanUserTokenLimit?relationId=" + relationId);
-        HttpResponse execute = request.method(Method.GET).execute();
+        HttpResponse execute = request.method(Method.GET).setConnectionTimeout(3000).timeout(3000).execute();
         String body = execute.body();
         JSONObject re = Jsons.parseObject(body, JSONObject.class);
         if (!body.contains("成功")) {

+ 2 - 0
netflix-service/src/main/java/com/cyksj/service/claude/ClaudeService.java

@@ -31,4 +31,6 @@ public interface ClaudeService {
     Long getConversationCount(String userToken, Long limitTime);
 
     Long carLimit(String carId);
+
+	void clearNum(String userToken) throws Exception;
 }

+ 15 - 0
netflix-service/src/main/java/com/cyksj/service/claude/impl/ClaudeServiceImpl.java

@@ -4,11 +4,15 @@ import cn.hutool.core.date.DateTime;
 import cn.hutool.core.lang.UUID;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
 import cn.hutool.http.HttpUtil;
+import cn.hutool.http.Method;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Jsons;
 import com.cyksj.common.util.StringUtil;
 import com.cyksj.mapper.*;
 import com.cyksj.model.entity.*;
@@ -234,6 +238,17 @@ public class ClaudeServiceImpl implements ClaudeService {
         return 0L;
     }
 
+    @Override
+    public void clearNum(String userToken) throws Exception {
+        HttpRequest request = new HttpRequest("http://147.79.20.183:9611/claude/cleanUserTokenLimit?userToken=" + userToken);
+        HttpResponse execute = request.method(Method.GET).setConnectionTimeout(3000).timeout(3000).execute();
+        String body = execute.body();
+        JSONObject re = Jsons.parseObject(body, JSONObject.class);
+        if (!body.contains("清除成功")) {
+            throw BusinessRuntimeException.getInstance(re.getStr("message"));
+        }
+    }
+
 
     /**
      * 获取车位信息

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/mange/MirrorUserCleatService.java

@@ -10,7 +10,7 @@ import com.cyksj.model.entity.OrderDon;
  * 创建时间:2024/6/27 11:15
  */
 public interface MirrorUserCleatService {
-	void delMirrorUser(Long goodsId, Long relationId);
+	void delMirrorUser(Long goodsId, Long relationId, Long clearId);
 
 	/**
 	 * 处理续费升级订单,将镜像车票还原

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

@@ -1146,10 +1146,6 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
                     .set(GiftCardUserRecord::getIsPay, 0)
                     .eq(GiftCardUserRecord::getOrderId, orderDon.getId()));
         }
-        //是否是GPT镜像规格
-        if (orderDon.getOrderType() == 1) {
-            mirrorUserCleatService.delMirrorUser(orderDon.getGoodsId(), orderDon.getRelationId());
-        }
         //是否是分销订单
         //处理下级渠道分销提成
         handleSubRewards(orderDon);

+ 21 - 5
netflix-service/src/main/java/com/cyksj/service/mange/impl/MirrorUserCleatServiceImpl.java

@@ -1,6 +1,7 @@
 package com.cyksj.service.mange.impl;
 
 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.mapper.*;
@@ -52,13 +53,19 @@ public class MirrorUserCleatServiceImpl implements MirrorUserCleatService {
 
 	private final OrderDonUpgradeRenewRecordMapper orderDonUpgradeRenewRecordMapper;
 
-	@Override
-	public void delMirrorUser(Long goodsId, Long relationId) {
+	private final UserTicketClearedRecordMapper userTicketClearedRecordMapper;
+
+	/**
+	 * 删除镜像User
+	 */
+	public void delMirrorUser(Long goodsId, Long relationId, Long clearId) {
+		String userToken = null;
 		//gpt镜像
 		if (goodsId != null && goodsId == 18) {
-			Integer selectCount = chatgptUserMapper.selectCount(Wrappers.lambdaQuery(ChatgptUser.class)
-					.eq(ChatgptUser::getRelationId, relationId));
-			if (selectCount > 0) {
+			ChatgptUser user = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class)
+					.eq(ChatgptUser::getRelationId, relationId).last("limit 1"));
+			if (user != null) {
+				userToken = user.getUserToken();
 				chatgptUserMapper.delete(Wrappers.lambdaQuery(ChatgptUser.class)
 						.eq(ChatgptUser::getRelationId, relationId));
 			}
@@ -68,6 +75,7 @@ public class MirrorUserCleatServiceImpl implements MirrorUserCleatService {
 			MidjourneyUser user = midjourneyUserMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUser.class)
 					.eq(MidjourneyUser::getRelationId, relationId).last("limit 1"));
 			if (user != null) {
+				userToken = user.getUserToken();
 				midjourneyUserMapper.delete(Wrappers.lambdaQuery(MidjourneyUser.class)
 						.eq(MidjourneyUser::getRelationId, relationId));
 				redisService.del(RedisService.key.MIDJOURNEY_USER.getName() + user.getUserToken());
@@ -78,6 +86,7 @@ public class MirrorUserCleatServiceImpl implements MirrorUserCleatService {
 			ClaudeUser user = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class)
 					.eq(ClaudeUser::getRelationId, relationId).last("limit 1"));
 			if (user != null) {
+				userToken = user.getUserToken();
 				claudeUserMapper.delete(Wrappers.lambdaQuery(ClaudeUser.class)
 						.eq(ClaudeUser::getRelationId, relationId));
 			}
@@ -88,6 +97,7 @@ public class MirrorUserCleatServiceImpl implements MirrorUserCleatService {
 			LumaUser user = lumaUserMapper.selectOne(Wrappers.lambdaQuery(LumaUser.class)
 					.eq(LumaUser::getRelationId, relationId).last("limit 1"));
 			if (user != null) {
+				userToken = user.getUserToken();
 				lumaUserMapper.delete(Wrappers.lambdaQuery(LumaUser.class)
 						.eq(LumaUser::getRelationId, relationId));
 				redisService.del(RedisService.key.LUMA_NUM_LIMIT.getName() + user.getId());
@@ -99,10 +109,16 @@ public class MirrorUserCleatServiceImpl implements MirrorUserCleatService {
 			GrokUser user = grokUserMapper.selectOne(Wrappers.lambdaQuery(GrokUser.class)
 					.eq(GrokUser::getRelationId, relationId).last("limit 1"));
 			if (user != null) {
+				userToken = user.getUserToken();
 				grokUserMapper.delete(Wrappers.lambdaQuery(GrokUser.class)
 						.eq(GrokUser::getRelationId, relationId));
 			}
 		}
+		if (clearId != null && StrUtil.isNotBlank(userToken)) {
+			userTicketClearedRecordMapper.update(null, Wrappers.lambdaUpdate(UserTicketClearedRecord.class)
+					.set(UserTicketClearedRecord::getUserToken, userToken)
+					.eq(UserTicketClearedRecord::getId, clearId));
+		}
 	}
 
 	@Override

+ 13 - 0
netflix-service/src/main/java/com/cyksj/service/mange/vip/CmsVipOrderService.java

@@ -0,0 +1,13 @@
+package com.cyksj.service.mange.vip;
+
+import com.cyksj.model.request.OrderRefundReq;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CmsVipOrderService
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 16:40
+ */
+public interface CmsVipOrderService {
+	void refundOrders(OrderRefundReq refundReq) throws Exception;
+}

+ 15 - 0
netflix-service/src/main/java/com/cyksj/service/mange/vip/VipCommonService.java

@@ -0,0 +1,15 @@
+package com.cyksj.service.mange.vip;
+
+import com.cyksj.model.entity.VipGoodsSku;
+
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipCommonService
+ * 创建者: JavaZou
+ * 创建时间:2025/5/14 17:01
+ */
+public interface VipCommonService {
+	void setVipGoodsSku(List<VipGoodsSku> vipGoodsSkus);
+}

+ 171 - 0
netflix-service/src/main/java/com/cyksj/service/mange/vip/impl/CmsVipOrderServiceImpl.java

@@ -0,0 +1,171 @@
+package com.cyksj.service.mange.vip.impl;
+
+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.exception.BusinessRuntimeException;
+import com.cyksj.mapper.GoodsDonSkuMapper;
+import com.cyksj.mapper.GroupsMapper;
+import com.cyksj.mapper.GroupsRelationMapper;
+import com.cyksj.mapper.vip.RefundVipOrderDonMapper;
+import com.cyksj.mapper.vip.VipOrderDonMapper;
+import com.cyksj.mapper.vip.VipUserMapper;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.request.OrderRefundReq;
+import com.cyksj.service.mange.MirrorUserCleatService;
+import com.cyksj.service.mange.vip.CmsVipOrderService;
+import com.cyksj.service.order.OrderRefundService;
+import com.cyksj.service.relation.GroupRelationClearService;
+import com.cyksj.service.user.UserBenefitsService;
+import com.cyksj.service.user.UserBindRelationService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CmsVipOrderServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 16:40
+ */
+@Service
+@RequiredArgsConstructor
+public class CmsVipOrderServiceImpl implements CmsVipOrderService {
+	private final RefundVipOrderDonMapper refundVipOrderDonMapper;
+
+	private final OrderRefundService orderRefundService;
+
+	private final VipOrderDonMapper vipOrderDonMapper;
+
+	private final VipUserMapper vipUserMapper;
+
+	private final GroupsRelationMapper relationMapper;
+
+	private final GoodsDonSkuMapper skuMapper;
+
+	private final GroupsMapper groupsMapper;
+
+	private final MirrorUserCleatService mirrorUserCleatService;
+
+	private final GroupRelationClearService groupRelationClearService;
+
+	private final UserBenefitsService userBenefitsService;
+
+	private final UserBindRelationService userBindRelationService;
+
+	@Override
+	public void refundOrders(OrderRefundReq refundReq) throws Exception {
+		VipOrderDon orderDon = vipOrderDonMapper.selectById(refundReq.getOrderId());
+		if (StrUtil.isEmpty(refundReq.getRefundType())) {
+			refundReq.setRefundType("money");
+		}
+		String refundType = refundReq.getRefundType();
+		//校验订单是否符合退款
+		checkOrderRefund(orderDon, refundReq);
+		//调用第三方接口退款
+		String outNo = StrUtil.EMPTY;
+		if (refundReq.getRefundMoney() != null && refundReq.getRefundMoney().compareTo(BigDecimal.ZERO) > 0) {
+			outNo = orderRefundService.vipCenterRefund(refundReq, orderDon);
+		}
+		orderDon.setRefundDesc(refundReq.getRefundDesc());
+		//记录退款信息
+		commonRefund(orderDon, refundReq.getRefundMoney(), refundReq.getOperator(), outNo, refundType, refundReq.getIsOnly(), refundReq.getRefundBalance(), refundReq.getChatImg(), refundReq.getDeductBalance());
+		//修改订单状态
+		orderDon.setStatus(VipOrderDon.Status.refund);
+		vipOrderDonMapper.updateById(orderDon);
+	}
+
+	private void commonRefund(VipOrderDon orderDon, BigDecimal refundMoney, String operator, String outNo, String refundType, Boolean isOnly, BigDecimal refundBalance, String chatImg, BigDecimal deductBalance) {
+		//记录退款
+		recordRefundOrder(orderDon, outNo, refundMoney, operator, chatImg, refundType);
+		//退等比例
+		if (isOnly != null && isOnly) {
+			userBenefitsService.addUserBalance(orderDon.getUserId(), refundBalance, UserBalanceSourceRecord.Source.vip_refund, null, orderDon.getId(), orderDon.getPayTitle() + UserBalanceSourceRecord.Source.vip_refund.getDesc(), true);
+			BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO);
+			orderDon.setRefundBalance(orderDonRefundBalance.add(refundBalance));
+		}
+		//若存在余额支付,退还余额
+		if (isOnly == null && orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+			BigDecimal addBalance = orderDon.getBalance();
+			//需额外扣除余额
+			if (deductBalance != null && deductBalance.compareTo(BigDecimal.ZERO) > 0) {
+				addBalance = addBalance.subtract(deductBalance);
+			}
+			userBenefitsService.addUserBalance(orderDon.getUserId(), addBalance, UserBalanceSourceRecord.Source.vip_refund, null, orderDon.getId(), orderDon.getPayTitle() + UserBalanceSourceRecord.Source.vip_refund.getDesc(), true);
+		}
+		//扣除会员时长
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(orderDon.getUserId(), null);
+		VipUser vipUser = vipUserMapper.selectOne(Wrappers.lambdaQuery(VipUser.class)
+				.in(VipUser::getUserId, userIdList)
+				.last("limit 1"));
+		if (vipUser != null) {
+			Date expiryTime = vipUser.getExpiryTime();
+			DateTime subExpiryTime = DateUtil.offsetMonth(expiryTime, -1);
+			vipUser.setExpiryTime(subExpiryTime);
+			//若会员过期清除镜像user
+			clearVipMirrorUser(vipUser.getUserId(), subExpiryTime);
+			vipUserMapper.updateById(vipUser);
+		}
+	}
+
+	private void recordRefundOrder(VipOrderDon orderDon, String outRefundNo, BigDecimal refundMoney, String operator, String chatImg, String refundType) {
+		RefundVipOrderDon refund = new RefundVipOrderDon();
+		refund.setOrderId(orderDon.getId());
+		refund.setOutRefundNo(outRefundNo);
+		refund.setTotalFee(orderDon.getMoney());
+		refund.setRefundDesc(orderDon.getRefundDesc());
+		//退款金额
+		refund.setRefundFee(refundMoney);
+		refund.setUserId(orderDon.getUserId());
+		refund.setGoodsTitle(orderDon.getPayTitle());
+		refund.setRefundMethod(orderDon.getType().name());
+		refund.setOperator(operator);
+		refund.setChatImg(chatImg);
+		//退款方式
+		if (StrUtil.isNotBlank(refundType) && refundType.equals(VipOrderDon.Type.BALANCE.getType())) {
+			refund.setRefundMethod(refundType);
+			refund.setRefundFee(orderDon.getRefundBalance());
+		}
+		refundVipOrderDonMapper.insert(refund);
+	}
+
+	private void checkOrderRefund(VipOrderDon orderDon, OrderRefundReq refundReq) {
+		if (orderDon.getStatus()!= VipOrderDon.Status.complete) {
+			throw BusinessRuntimeException.getInstance("订单状态异常");
+		}
+		Long userId = orderDon.getUserId();
+		//会员是否已过期
+		VipUser vipUser = vipUserMapper.selectOne(Wrappers.lambdaQuery(VipUser.class)
+				.eq(VipUser::getUserId, userId)
+				.gt(VipUser::getExpiryTime, DateTime.now())
+				.last("limit 1"));
+		if (vipUser == null) {
+			throw BusinessRuntimeException.getInstance("用户会员已过期,无法退款");
+		}
+	}
+
+	/**
+	 * 整理会员车票时长
+	 * 过期清除镜像user
+	 */
+	private void clearVipMirrorUser(Long userId, Date expiryTimeAfterSub) {
+		Boolean overdue = expiryTimeAfterSub.before(DateTime.now());
+		List<GroupsRelation> groupsRelations = relationMapper.selectList(Wrappers.lambdaQuery(GroupsRelation.class)
+				.eq(GroupsRelation::getUserId, userId)
+				.eq(GroupsRelation::getIsVip, Boolean.TRUE));
+		groupsRelations.forEach(ticket -> {
+			if (overdue) {
+				//清除车票
+				groupRelationClearService.clearTicket(ticket, UserTicketClearedRecord.Source.vip_refund);
+				return;
+			}
+			ticket.setExpiryTime(expiryTimeAfterSub);
+			relationMapper.updateById(ticket);
+		});
+	}
+}

+ 44 - 0
netflix-service/src/main/java/com/cyksj/service/mange/vip/impl/VipCommonServiceImpl.java

@@ -0,0 +1,44 @@
+package com.cyksj.service.mange.vip.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.mapper.vip.VipGoodsSkuMapper;
+import com.cyksj.model.entity.VipGoodsSku;
+import com.cyksj.service.mange.vip.VipCommonService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipCommonServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2025/5/14 17:01
+ */
+@Service
+@RequiredArgsConstructor
+public class VipCommonServiceImpl implements VipCommonService {
+	private final VipGoodsSkuMapper vipGoodsSkuMapper;
+
+	@Override
+	public void setVipGoodsSku(List<VipGoodsSku> vipGoodsSkus) {
+		List<Long> collect = vipGoodsSkus.stream().map(VipGoodsSku::getSkuId).collect(Collectors.toList());
+		//清除
+		vipGoodsSkuMapper.delete(Wrappers.lambdaUpdate(VipGoodsSku.class)
+				.notIn(VipGoodsSku::getSkuId, collect));
+
+		vipGoodsSkus.forEach(ygs->{
+			Long id = ygs.getId();
+			if (id == null) {
+				try {
+					vipGoodsSkuMapper.insert(ygs);
+				} catch (DuplicateKeyException e) {
+				}
+				return;
+			}
+			vipGoodsSkuMapper.updateById(ygs);
+		});
+	}
+}

+ 3 - 0
netflix-service/src/main/java/com/cyksj/service/midjourney/MidjourneyAccountService.java

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.cyksj.model.entity.MidjourneyAccount;
 import com.cyksj.model.entity.MidjourneyUser;
 
+import java.util.Date;
+
 /**
  * @author zwhui
  * @date 2024/4/12 15:54
@@ -21,4 +23,5 @@ public interface MidjourneyAccountService extends IService<MidjourneyAccount> {
 
     MidjourneyUser getMidjourneyUserToken(Long userId, Long relationId);
 
+    void updateUserExpiryTime(Long relationId, Date expiryTime);
 }

+ 8 - 4
netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidJourneyAccountServiceImpl.java

@@ -27,10 +27,7 @@ import javax.imageio.stream.FileImageOutputStream;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
+import java.util.*;
 
 /**
  * @author zwhui
@@ -226,6 +223,13 @@ public class MidJourneyAccountServiceImpl extends ServiceImpl<MidjourneyAccountM
         }
     }
 
+    @Override
+    public void updateUserExpiryTime(Long relationId, Date expiryTime) {
+        midjourneyUserMapper.update(null, Wrappers.lambdaUpdate(MidjourneyUser.class)
+                .set(MidjourneyUser::getExpireTime, expiryTime)
+                .eq(MidjourneyUser::getRelationId, relationId));
+    }
+
     /**
      * 获取车位信息
      *

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

@@ -32,4 +32,6 @@ public interface OrderRefundService {
 	String refundUserMirrorShopFee(UserMirrorShopFeeOrderDon orderDon) throws Exception;
 
 	void refundSpotifyUpgrade(Long orderId) throws Exception;
+
+	String vipCenterRefund(OrderRefundReq refundReq, VipOrderDon orderDon) throws Exception;
 }

+ 29 - 0
netflix-service/src/main/java/com/cyksj/service/order/VipOrderDonService.java

@@ -0,0 +1,29 @@
+package com.cyksj.service.order;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.cyksj.model.dto.AliPayParams;
+import com.cyksj.model.entity.VipOrderDon;
+import com.cyksj.model.request.AlipayOrderDonReq;
+import com.cyksj.model.request.VipOrderDonPayReq;
+
+import java.util.Map;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipOrderDonService
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 11:14
+ */
+public interface VipOrderDonService extends IService<VipOrderDon> {
+	VipOrderDon submit(VipOrderDonPayReq payRequest);
+
+	AliPayParams aliPay(AlipayOrderDonReq re) throws Exception;
+
+	void aliPayNotify(Map<String, String> params) throws Exception;
+
+	void orderNotify(VipOrderDon orderDon);
+
+	VipOrderDon renew(VipOrderDonPayReq payRequest);
+
+	void closeOrderDon(long userId, Long orderId);
+}

+ 85 - 15
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderRefundServiceImpl.java

@@ -12,6 +12,7 @@ import com.cyksj.config.WeChatConfig;
 import com.cyksj.config.zfb.AliPayClientFactory;
 import com.cyksj.mapper.*;
 import com.cyksj.mapper.channel.ShopConfigMapper;
+import com.cyksj.mapper.vip.VipOrderDonMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.request.OrderRefundReq;
 import com.cyksj.service.order.OrderRefundService;
@@ -65,15 +66,17 @@ public class OrderRefundServiceImpl implements OrderRefundService {
 
 	private final SpotifyUserUpgradeOrderMapper spotifyUserUpgradeOrderMapper;
 
+	private final VipOrderDonMapper vipOrderDonMapper;
+
 	@Override
 	public String centerRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception {
 		OrderDon.Type type = orderDon.getType();
 		String refundType = refundReq.getRefundType();
 		if (type == OrderDon.Type.WeChat && !refundType.equals(OrderDon.Type.BALANCE.getType())) {
-			return wxRefund(refundReq, orderDon);
+			return wxRefund(refundReq, orderDon.getId(), orderDon.getShopId(), orderDon.getTransactionId(), orderDon.getMoney());
 		}
 		if (type == OrderDon.Type.ALI_PAY && !refundType.equals(OrderDon.Type.BALANCE.getType())) {
-			return zfbRefund(refundReq, orderDon);
+			return zfbRefund(refundReq, orderDon.getId(), orderDon.getShopId(), orderDon.getTransactionId(), orderDon.getMoney());
 		}
 		if (type == OrderDon.Type.PAYPAL && !refundType.equals(OrderDon.Type.BALANCE.getType())) {
 			if (!paypalRefund(refundReq, orderDon)) {
@@ -239,51 +242,50 @@ public class OrderRefundServiceImpl implements OrderRefundService {
 		return outRefundNo;
 	}
 
-	public String wxRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception {
-		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) <=0){
+	public String wxRefund(OrderRefundReq refundReq, Long orderId, String shopId, String transactionId, BigDecimal orderMoney) throws Exception {
+		if (orderMoney.compareTo(BigDecimal.ZERO) <= 0) {
 			return StrUtil.EMPTY;
 		}
 		long time = System.currentTimeMillis();
 		//退款订单号
 		String outRefundNo = Codec.DoDigest.custom()
 				.setAlgorithm(Codec.DoDigest.Algorithm.MD5)
-				.setStringData(time + String.valueOf(orderDon.getId()))
+				.setStringData(time + String.valueOf(orderId))
 				.toHexString();
 		weChatService.refundWxOrder(weChatConfig.getAppid(),
-				Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID),
-				orderDon.getTransactionId(),
+				Optional.ofNullable(shopId).orElse(ShopConfig.WX_DEFAULT_APP_ID),
+				transactionId,
 				outRefundNo,
-				orderDon.getMoney(),
+				orderMoney,
 				refundReq.getRefundMoney(),
 				weChatConfig.getNotifyDomain() + "manage/order/wx/refund/notify");
 		//记录退款信息
 		return outRefundNo;
 	}
 
-	public String zfbRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception {
-		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) <=0){
+	public String zfbRefund(OrderRefundReq refundReq,  Long orderId, String shopId, String transactionId, BigDecimal orderMoney) throws Exception {
+		if (orderMoney.compareTo(BigDecimal.ZERO) <=0){
 			return StrUtil.EMPTY;
 		}
 		AlipayTradeRefundResponse response;
 		//支付宝退款
-		AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.ZFB_DEFAULT_APP_ID));
+		AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(Optional.ofNullable(shopId).orElse(ShopConfig.ZFB_DEFAULT_APP_ID));
 		AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
 		AlipayTradeRefundModel model = new AlipayTradeRefundModel();
 		//支付宝交易号
-		model.setTradeNo(orderDon.getTransactionId());
+		model.setTradeNo(transactionId);
 		model.setRefundAmount(refundReq.getRefundMoney().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_EVEN).toString());
 		model.setQueryOptions(List.of("gmt_refund_pay"));
 		long time = System.currentTimeMillis();
 		//退款订单号
 		String outRefundNo = Codec.DoDigest.custom()
 				.setAlgorithm(Codec.DoDigest.Algorithm.MD5)
-				.setStringData(time + String.valueOf(orderDon.getId()))
+				.setStringData(time + String.valueOf(orderId))
 				.toHexString();
 		//退款请求号
 		model.setOutRequestNo(outRefundNo);
 		request.setBizModel(model);
-		String shopId = orderDon.getShopId();
-		ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, orderDon.getShopId()));
+		ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, shopId));
 		if (shopId != null && shopConfig != null && shopConfig.getIsCert()) {
 			response = alipayClient.certificateExecute(request);
 		} else {
@@ -478,6 +480,29 @@ public class OrderRefundServiceImpl implements OrderRefundService {
 		spotifyUserUpgradeOrderMapper.updateById(order);
 	}
 
+	@Override
+	public String vipCenterRefund(OrderRefundReq refundReq, VipOrderDon orderDon) throws Exception {
+		VipOrderDon.Type type = orderDon.getType();
+		String refundType = refundReq.getRefundType();
+		if (type == VipOrderDon.Type.WeChat && !refundType.equals(VipOrderDon.Type.BALANCE.getType())) {
+			return wxRefund(refundReq, orderDon.getId(), orderDon.getShopId(), orderDon.getTransactionId(), orderDon.getMoney());
+		}
+		if (type == VipOrderDon.Type.ALI_PAY && !refundType.equals(VipOrderDon.Type.BALANCE.getType())) {
+			return zfbRefund(refundReq, orderDon.getId(), orderDon.getShopId(), orderDon.getTransactionId(), orderDon.getMoney());
+		}
+		if (type == VipOrderDon.Type.PAYPAL && !refundType.equals(VipOrderDon.Type.BALANCE.getType())) {
+			if (!paypalRefundVipOrder(refundReq, orderDon)) {
+				throw BusinessRuntimeException.getInstance("paypal退款失败");
+			}
+		}
+		if (type == VipOrderDon.Type.STRIPE && !refundType.equals(VipOrderDon.Type.BALANCE.getType())) {
+			if (!stripeRefundVipOrder(refundReq, orderDon)) {
+				throw BusinessRuntimeException.getInstance("stripe退款失败");
+			}
+		}
+		return StrUtil.EMPTY;
+	}
+
 	private String zfbRefundWholesaleOrder(GoodsWholesaleOrderDon orderDon) throws Exception {
 		BigDecimal money = orderDon.getRefundMoney();
 		if (money.compareTo(BigDecimal.ZERO) <=0){
@@ -511,4 +536,49 @@ public class OrderRefundServiceImpl implements OrderRefundService {
 		if (!response.isSuccess()) throw BusinessRuntimeException.getInstance("退款失败,e:{0}", response.getSubMsg());
 		return outRefundNo;
 	}
+
+	private boolean stripeRefundVipOrder(OrderRefundReq refundReq, VipOrderDon orderDon) throws StripeException {
+		BigDecimal net = refundReq.getRefundMoney().divide(new BigDecimal(7), 2, RoundingMode.HALF_DOWN);
+		if (!refundReq.getIsDiffer()) {
+			net = net.subtract(orderDon.getAbroadTransactionFee());
+		}
+		log.info("stripe return order:{},money:{}",orderDon.getId(),net);
+		StripePayConfig stripePayConfig = stripePayConfigMapper.selectById(1);
+		Stripe.apiKey = stripePayConfig.getApiKey();
+		RefundCreateParams params =
+				RefundCreateParams.builder()
+						.setCharge(orderDon.getTransactionId())
+						.setAmount(net.longValue())
+						.build();
+		com.stripe.model.Refund refund = com.stripe.model.Refund.create(params);
+		log.info("stripe return refund:{}",refund);
+		if ("succeeded".equals(refund.getStatus())){
+			orderDon.setStatus(VipOrderDon.Status.refund);
+			vipOrderDonMapper.updateById(orderDon);
+			return true;
+		}
+		return false;
+	}
+
+	private boolean paypalRefundVipOrder(OrderRefundReq refundReq, VipOrderDon orderDon) throws PayPalRESTException {
+		BigDecimal net = refundReq.getRefundMoney().divide(new BigDecimal(7), 2, RoundingMode.HALF_DOWN);
+		//非补差价 需要扣除手续费
+		if (!refundReq.getIsDiffer()) {
+			net = net.subtract(orderDon.getAbroadTransactionFee());
+		}
+		log.info("paypal return order:{},money:{}",orderDon.getId(),net);
+		PaypalPayConfig paypalPayConfig = paypalPayConfigMapper.selectById(1);
+		APIContext apiContext = getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(),paypalPayConfig.getMode());
+		Sale sale = Sale.get(apiContext, orderDon.getTransactionId());
+		Refund refund = new Refund();
+		refund.setAmount(new Amount("USD", net.divide(new BigDecimal(100),2,RoundingMode.HALF_DOWN).toString()));
+		refund = sale.refund(apiContext, refund);
+		log.info("paypal return refund:{}",refund);
+		if ("completed".equals(refund.getState())){
+			orderDon.setStatus(VipOrderDon.Status.refund);
+			vipOrderDonMapper.updateById(orderDon);
+			return true;
+		}
+		return false;
+	}
 }

+ 581 - 0
netflix-service/src/main/java/com/cyksj/service/order/impl/VipOrderDonServiceImpl.java

@@ -0,0 +1,581 @@
+package com.cyksj.service.order.impl;
+
+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.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.domain.AlipayTradeWapPayModel;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.alipay.api.request.AlipayTradeQueryRequest;
+import com.alipay.api.response.AlipayTradeQueryResponse;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.constant.Constant;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.snowflake.Sequence;
+import com.cyksj.common.util.Codec;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.config.zfb.AliPayClientFactory;
+import com.cyksj.config.zfb.BaseZfbConfig;
+import com.cyksj.config.zfb.ZfbProductCode;
+import com.cyksj.enums.GatewayApiCode;
+import com.cyksj.mapper.*;
+import com.cyksj.mapper.channel.ShopConfigMapper;
+import com.cyksj.mapper.sys.SysConfigMapper;
+import com.cyksj.mapper.vip.VipGoodsSkuMapper;
+import com.cyksj.mapper.vip.VipOrderDonDeductRelationMapper;
+import com.cyksj.mapper.vip.VipOrderDonMapper;
+import com.cyksj.mapper.vip.VipUserMapper;
+import com.cyksj.model.dto.AliPayParams;
+import com.cyksj.model.dto.VipDeductDto;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.manage.views.GroupsRelationView;
+import com.cyksj.model.request.AlipayOrderDonReq;
+import com.cyksj.model.request.OrderAttach;
+import com.cyksj.model.request.VipOrderDonPayReq;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.chatgpt.ChatGptAccountService;
+import com.cyksj.service.claude.ClaudeService;
+import com.cyksj.service.order.VipOrderDonService;
+import com.cyksj.service.pay.AliPayCommonService;
+import com.cyksj.service.relation.GroupRelationFrontService;
+import com.cyksj.service.user.UserBenefitsService;
+import com.cyksj.service.user.UserBindRelationService;
+import com.cyksj.service.vip.VipFrontService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipOrderDonServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 11:14
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class VipOrderDonServiceImpl extends ServiceImpl<VipOrderDonMapper, VipOrderDon> implements VipOrderDonService {
+	private static final Sequence SEQUENCE = new Sequence(0);
+
+	private final UserMapper userMapper;
+
+	private final UserBenefitsService userBenefitsService;
+
+	private final VipUserMapper vipUserMapper;
+
+	private final VipGoodsSkuMapper vipGoodsSkuMapper;
+
+	private final UserBindRelationService userBindRelationService;
+
+	private final SysConfigMapper sysConfigMapper;
+
+	private final AliPayCommonService aliPayCommonService;
+
+	private final ShopConfigMapper shopConfigMapper;
+
+	private final GroupRelationFrontService groupRelationFrontService;
+
+	private final GroupsRelationMapper relationMapper;
+
+	private final VipFrontService vipFrontService;
+
+	private final VipOrderDonDeductRelationMapper vipOrderDonDeductRelationMapper;
+
+	private final BeanSearcher beanSearcher;
+
+	private final MidjourneyUserMapper midjourneyUserMapper;
+
+	private final ChatgptUserMapper chatgptUserMapper;
+
+	private final ClaudeUserMapper claudeUserMapper;
+
+	private final LumaUserMapper lumaUserMapper;
+
+	private final GrokUserMapper grokUserMapper;
+
+	private final RedisService redisService;
+
+	private final GoodsDonSkuMapper skuMapper;
+
+	private final ChatGptAccountService chatGptAccountService;
+
+	private final ClaudeService claudeService;
+
+	@Override
+	public VipOrderDon submit(VipOrderDonPayReq payRequest) {
+		Long userId = payRequest.getUserId();
+		User user = userMapper.selectById(userId);
+		if (user == null) throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_TOKEN_VALID);
+		if (user.getIsBlack()) throw BusinessRuntimeException.getInstance("系统检测操作异常,请稍后再试");
+		/* 生成订单 */
+		String donNo = SEQUENCE.nextId().toString();
+		VipOrderDon orderDon = new VipOrderDon();
+		orderDon.setOrderNo(donNo);
+		orderDon.setOpenId(user.getOpenId());
+		//获取会员价格
+		VipDeductDto vipMoneyIfDeduct = getVipMoneyIfDeduct(user, payRequest.getIsDeduct());
+		BigDecimal vipMoney = getVipMoney();
+		payRequest.setDonMoney(vipMoney);
+		if (vipMoneyIfDeduct != null) {
+			payRequest.setDonMoney(vipMoney.subtract(vipMoneyIfDeduct.getDeductMoney()));
+		}
+		orderDon.setMoney(payRequest.getDonMoney());
+		//渠道链接id
+		if (payRequest.getPopularizeId() != null) {
+			orderDon.setPopularizeId(payRequest.getPopularizeId());
+		}
+		orderDon.setStatus(VipOrderDon.Status.noPayment);
+		orderDon.setUserId(user.getId());
+		orderDon.setType(VipOrderDon.Type.getType(payRequest.getType()));
+		orderDon.setIsWebPay(payRequest.getIsWebPay());
+		//扣除使用优惠券、余额的订单金额
+		deductOrderMoney(orderDon, user, payRequest);
+		//设置订单支付商品信息
+		orderDon.setPayTitle(Constant.VIP_TITLE);
+		this.saveOrUpdate(orderDon);
+
+		Long orderId = orderDon.getId();
+		//存在抵扣 记录抵扣成功后 需清除车票的ids
+		if (vipMoneyIfDeduct != null) {
+			List<Long> deductRelationIds = vipMoneyIfDeduct.getDeductRelationIds();
+			recordVipDeductRelationIds(orderDon.getId(), orderDon.getUserId(), deductRelationIds);
+		}
+		//余额支付订单
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+			orderDon = getById(orderId);
+			unifiedHandlerOrderNotify(orderDon);
+		}
+		return orderDon;
+	}
+
+	@Override
+	public AliPayParams aliPay(AlipayOrderDonReq re) throws Exception {
+		String productCode = StrUtil.EMPTY;
+		if (re.getType() == 1) {
+			productCode = ZfbProductCode.MOBILE_WEB.getProductCode();
+		}
+		if (re.getType() == 2) {
+			productCode = ZfbProductCode.PC_WEB.getProductCode();
+		}
+		return aliPayCommonService.unifiedPayVip(re.getOrderId(), productCode, re.getPayMode(), re.getReturnUrl());
+	}
+
+	@Override
+	public void aliPayNotify(Map<String, String> params) throws Exception {
+		//调用SDK验证签名
+		String body = Codec.DoBase64.custom().setData(params.get("body")).decodeAsText();
+		OrderAttach orderAttach = Jsons.parseObject(body, OrderAttach.class);
+		Long oderId = orderAttach.getO();
+		VipOrderDon orderDon = this.getById(oderId);
+		if (null == orderDon) {
+			throw BusinessRuntimeException.getInstance("订单不存在.");
+		}
+		if (orderDon.getPopularizeId() == null) {
+			orderDon.setShopId(null);
+		}
+		String appId = params.get("app_id");
+		boolean signVerified = AlipaySignature.rsaCheckV1(params, shopConfigMapper.getByAppId(appId).getPublicKey(), BaseZfbConfig.charset, BaseZfbConfig.signType);
+		if (!signVerified) {
+			log.error("支付宝回调校验签名错误,自定义入参:{}", params.get("body"));
+			throw BusinessRuntimeException.getInstance("支付宝回调校验签名错误");
+		}
+		String transactionId = params.get("trade_no");
+		String payTimeStr = params.get("gmt_payment");
+		//买家支付宝账号
+		String buyerId = Optional.ofNullable(params.get("buyer_id")).orElse(params.get("buyer_open_id"));
+		log.info("支付宝回调信息:{}", Jsons.toJson(params));
+		if (!orderDon.getStatus().equals(VipOrderDon.Status.noPayment) && !orderDon.getStatus().equals(VipOrderDon.Status.close)) {
+			log.info("支付宝已回调,重复修改状态 id:{}", orderDon.getId());
+			return;
+		}
+		Date payTime = DateTime.now();
+		if (StringUtils.isNotBlank(payTimeStr)) {
+			payTime = DateUtil.parse(payTimeStr, "yyyy-MM-dd HH:mm:ss");
+		}
+		// 更改订单状态
+		orderDon.setTransactionId(transactionId);
+		orderDon.setPayTime(payTime);
+		orderDon.setShopId(appId);
+		orderDon.setType(VipOrderDon.Type.ALI_PAY);
+		orderDon.setBuyerId(buyerId);
+		//回调
+		unifiedHandlerOrderNotify(orderDon);
+	}
+
+	@Override
+	public void orderNotify(VipOrderDon orderDon) {
+		unifiedHandlerOrderNotify(orderDon);
+	}
+
+	@Override
+	public VipOrderDon renew(VipOrderDonPayReq payRequest) {
+		Long userId = payRequest.getUserId();
+		User user = userMapper.selectById(userId);
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, user);
+		Integer selectCount = vipUserMapper.selectCount(Wrappers.lambdaQuery(VipUser.class)
+				.in(VipUser::getUserId, userIdList)
+				.gt(VipUser::getExpiryTime, DateTime.now()));
+		if (selectCount == 0) {
+			throw BusinessRuntimeException.getInstance("您会员已过期,无法续费");
+		}
+		/* 生成订单 */
+		String donNo = SEQUENCE.nextId().toString();
+		VipOrderDon orderDon = new VipOrderDon();
+		orderDon.setOrderNo(donNo);
+		orderDon.setOpenId(user.getOpenId());
+		//获取续费会员价格
+		payRequest.setDonMoney(getVipMoney());
+		//续费订单
+		orderDon.setOrderType(2);
+		orderDon.setMoney(payRequest.getDonMoney());
+		//渠道链接id
+		if (payRequest.getPopularizeId() != null) {
+			orderDon.setPopularizeId(payRequest.getPopularizeId());
+		}
+		orderDon.setStatus(VipOrderDon.Status.noPayment);
+		orderDon.setUserId(user.getId());
+		orderDon.setType(VipOrderDon.Type.getType(payRequest.getType()));
+		orderDon.setIsWebPay(payRequest.getIsWebPay());
+		//扣除使用优惠券、余额的订单金额
+		deductOrderMoney(orderDon, user, payRequest);
+		//设置订单支付商品信息
+		orderDon.setPayTitle(Constant.VIP_TITLE);
+
+		this.saveOrUpdate(orderDon);
+
+		Long orderId = orderDon.getId();
+		//余额支付订单
+		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+			orderDon = getById(orderId);
+			unifiedHandlerOrderNotify(orderDon);
+		}
+		return orderDon;
+	}
+
+	@Override
+	public void closeOrderDon(long userId, Long orderId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		VipOrderDon orderDon = getById(orderId);
+		if (!userIdList.contains(orderDon.getUserId())) {
+			throw BusinessRuntimeException.getInstance("订单错误");
+		}
+		if (orderDon.getStatus() != VipOrderDon.Status.noPayment) {
+			throw BusinessRuntimeException.getInstance("订单状态错误");
+		}
+		VipOrderDon.Status status = isHasPaid(orderDon);
+		if (status == VipOrderDon.Status.noPayment) {
+			log.info("关闭订单 orderId:{}", orderId);
+			orderDon.setStatus(VipOrderDon.Status.close);
+			updateById(orderDon);
+		}
+	}
+
+	private VipOrderDon.Status isHasPaid(VipOrderDon orderDon) {
+		if (orderDon.getType() == VipOrderDon.Type.ALI_PAY) {
+			try {
+				AlipayTradeQueryResponse zfbOrderDetail = getZfbOrderDetail(orderDon.getShopId(), orderDon.getOrderNo());
+				if (zfbOrderDetail != null) {
+					String tradeStatus = zfbOrderDetail.getTradeStatus();
+					if ("TRADE_SUCCESS".equals(tradeStatus)) {
+						orderDon.setStatus(VipOrderDon.Status.complete);
+						orderDon.setTransactionId(zfbOrderDetail.getTradeNo());
+						Date sendPayDate = zfbOrderDetail.getSendPayDate();
+						orderDon.setPayTime(sendPayDate);
+						//买家支付宝账号
+						String buyerId = Optional.ofNullable(zfbOrderDetail.getBuyerUserId()).orElse(zfbOrderDetail.getBuyerOpenId());
+						orderDon.setBuyerId(buyerId);
+						unifiedHandlerOrderNotify(orderDon);
+					}
+				}
+			} catch (Exception e) {
+
+			}
+		}
+		return orderDon.getStatus();
+	}
+
+	/**
+	 * 查询支付宝订单详情
+	 */
+	public AlipayTradeQueryResponse getZfbOrderDetail(String shopId, String orderNo) throws AlipayApiException {
+		AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(shopId);
+		AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+		AlipayTradeWapPayModel bizModel = new AlipayTradeWapPayModel();
+		bizModel.setOutTradeNo(orderNo);
+		request.setBizModel(bizModel);
+		AlipayTradeQueryResponse response;
+		ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, shopId));
+		if (shopConfig != null && shopConfig.getIsCert()) {
+			response = alipayClient.certificateExecute(request);
+		} else {
+			response = alipayClient.execute(request);
+		}
+		if (response.isSuccess()) {
+			return response;
+		}
+		return null;
+	}
+
+	private void unifiedHandlerOrderNotify(VipOrderDon orderDon) {
+		orderDon.setStatus(VipOrderDon.Status.complete);
+		this.updateById(orderDon);
+		//余额使用记录
+		if (orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+			userBenefitsService.recordBalanceBySource(orderDon.getUserId(), orderDon.getBalance().negate(), UserBalanceSourceRecord.Source.vip_order, orderDon.getId(), UserBalanceSourceRecord.Source.vip_order.getDesc(), null, null);
+		}
+		Long userId = orderDon.getUserId();
+		//只选择一个号
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		VipUser vipUser = Optional.ofNullable(vipUserMapper.selectOne(Wrappers.lambdaQuery(VipUser.class)
+				.in(VipUser::getUserId, userIdList)
+				.last("limit 1"))).orElse(new VipUser());
+		vipUser.setUserId(userId);
+		//发放会员车票 普通订单
+		if (orderDon.getOrderType() == 1) {
+			vipUser.setStartTime(DateTime.now());
+			vipUser.setExpiryTime(DateUtil.offsetMonth(vipUser.getStartTime(), 1));
+			if (vipUser.getId() == null) {
+				try {
+					vipUserMapper.insert(vipUser);
+				} catch (DuplicateKeyException e) {
+				}
+			} else vipUserMapper.updateById(vipUser);
+			//发送车票
+			sendVipTicketToUser(vipUser.getUserId(), vipUser.getExpiryTime(), userIdList, orderDon.getId());
+		} else {
+			//续费订单
+			vipUser.setExpiryTime(DateUtil.offsetMonth(vipUser.getExpiryTime(), 1));
+			vipUserMapper.updateById(vipUser);
+			//续费订单
+			setTicketToVipExpiryTime(userIdList, vipUser.getUserId(), vipUser.getExpiryTime());
+		}
+	}
+
+	private void sendVipTicketToUser(Long userId, Date vipExpiryTime, List<Long> userIds, Long orderId) {
+		//是否存在抵扣 抵扣成功 清除车票
+		VipOrderDonDeductRelation vipOrderDonDeductRelation = vipOrderDonDeductRelationMapper.selectOne(Wrappers.lambdaQuery(VipOrderDonDeductRelation.class)
+				.eq(VipOrderDonDeductRelation::getOrderId, orderId)
+				.last("limit 1"));
+		List<Long> deductRelationIds = null;
+		if (vipOrderDonDeductRelation != null) {
+			try {
+				deductRelationIds = Jsons.parseList(vipOrderDonDeductRelation.getRelationIds(), Long.class);
+				vipOrderDonDeductRelation.setStatus(true);
+				vipOrderDonDeductRelationMapper.updateById(vipOrderDonDeductRelation);
+			} catch (Exception e) {
+			}
+		}
+		List<VipGoodsSku> skus = vipGoodsSkuMapper.selectList(null);
+		List<Long> deductSkuIds = null;
+		if (CollUtil.isNotEmpty(deductRelationIds)) {
+			List<GroupsRelationView> groupsRelationViews = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
+					.field(GroupsRelationView::getUserId, userIds).op(Operator.InList)
+					.field(GroupsRelationView::getId, deductRelationIds).op(Operator.InList).build());
+			deductSkuIds = groupsRelationViews.stream().map(GroupsRelationView::getSkuId).collect(Collectors.toList());
+			//将车票弄成会员类型
+			relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+					.set(GroupsRelation::getIsVip, Boolean.TRUE)
+					.in(GroupsRelation::getUserId, userIds)
+					.in(GroupsRelation::getId, deductRelationIds));
+			//重置镜像user
+			resetMirrorUser(groupsRelationViews, vipExpiryTime);
+		}
+		for (VipGoodsSku sku : skus) {
+			//未有抵扣 直接发送会员车票
+			if (deductSkuIds == null || !deductSkuIds.contains(sku.getSkuId())) {
+				groupRelationFrontService.getTicket(userId, sku.getSkuId(), Boolean.TRUE);
+			}
+		}
+	}
+
+	private void deductOrderMoney(VipOrderDon orderDon, User user, VipOrderDonPayReq payRequest) {
+		BigDecimal balance = payRequest.getBalance();
+		//存在余额
+		deductBalancePayOrderMoney(orderDon, balance, user);
+	}
+
+	private VipDeductDto getVipMoneyIfDeduct(User user, Boolean isDeduct) {
+		//若用户抵扣
+		if (isDeduct) {
+			return vipFrontService.checkAndGetOrderDeductMoney(user.getId());
+		}
+		return null;
+	}
+
+	public void deductBalancePayOrderMoney(VipOrderDon orderDon, BigDecimal balance, User user) {
+		//存在余额
+		if (balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
+			BigDecimal money = orderDon.getMoney();
+			//去掉余额 多出的部分
+			BigDecimal subBalance = balance;
+			orderDon.setMoney(money.subtract(subBalance));
+			if (balance.compareTo(money) >= 0) {
+				orderDon.setMoney(BigDecimal.ZERO);
+				orderDon.setBalance(money);
+				subBalance = money;
+				orderDon.setType(VipOrderDon.Type.BALANCE);
+			}
+			BigDecimal userBalance = user.getBalance();
+			//扣除余额 是否成功
+			Integer success = userMapper.deductBalance(user.getId(), subBalance, userBalance);
+			if (success != 1) throw BusinessRuntimeException.getInstance("您的余额不足!");
+			orderDon.setBalance(subBalance);
+		}
+	}
+
+	/**
+	 * 设置会员车票同一过期时间
+	 */
+	private void setTicketToVipExpiryTime(List<Long> userIds, Long vipUserId, Date expiryTime) {
+		List<Long> vipSkuIds = vipGoodsSkuMapper.selectList(null).stream().map(VipGoodsSku::getSkuId).collect(Collectors.toList());
+		List<Long> selfSkuIds = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
+				.field(GroupsRelationView::getUserId, userIds).op(Operator.InList)
+				.field(GroupsRelationView::getSkuId, vipSkuIds).op(Operator.InList)
+				.field(GroupsRelationView::getIsVip, true)
+				.onlySelect(GroupsRelationView::getSkuId)
+				.build()).stream().map(GroupsRelationView::getSkuId).collect(Collectors.toList());
+		if (selfSkuIds.size() != vipSkuIds.size()) {
+			vipSkuIds.forEach(vipSkuId->{
+				if (!selfSkuIds.contains(vipSkuId)) {
+					//发送 缺少的规格
+					groupRelationFrontService.getTicket(vipUserId, vipSkuId, Boolean.TRUE);
+				}
+			});
+		}
+		relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+				.set(GroupsRelation::getExpiryTime, expiryTime)
+				.set(GroupsRelation::getUpdateTime, DateTime.now())
+				.in(GroupsRelation::getUserId, userIds)
+				.eq(GroupsRelation::getStatus, GroupsRelation.Status.validity)
+				.eq(GroupsRelation::getIsVip, Boolean.TRUE));
+	}
+
+	private BigDecimal getVipMoney() {
+		SysConfig sysConfig = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class)
+				.eq(SysConfig::getSysKey, Constant.VIP_FEE_KEY)
+				.last("limit 1"));
+		if (sysConfig == null || StrUtil.isEmpty(sysConfig.getSysValue())) {
+			throw BusinessRuntimeException.getInstance("系统异常");
+		}
+		BigDecimal vipFee = BigDecimal.valueOf(Long.valueOf(sysConfig.getSysValue()));
+		return vipFee;
+	}
+
+	private void recordVipDeductRelationIds(Long orderId, Long userId, List<Long> deductRelationIds) {
+		VipOrderDonDeductRelation vipOrderDonDeductRelation = new VipOrderDonDeductRelation();
+		vipOrderDonDeductRelation.setOrderId(orderId);
+		vipOrderDonDeductRelation.setUserId(userId);
+		vipOrderDonDeductRelation.setRelationIds(deductRelationIds.toString());
+		vipOrderDonDeductRelationMapper.insert(vipOrderDonDeductRelation);
+	}
+
+	/**
+	 * 重置镜像user
+	 */
+	private void resetMirrorUser(List<GroupsRelationView> groupsRelationViews, Date vipExpiryTime) {
+		for (GroupsRelationView groupsRelationView : groupsRelationViews) {
+			Long skuId = groupsRelationView.getSkuId();
+			Long goodsId = groupsRelationView.getGoodsId();
+			Long relationId = groupsRelationView.getId();
+			GoodsDonSku sku = skuMapper.selectById(skuId);
+			if (goodsId == Constant.MJ_GID) {
+				MidjourneyUser user = midjourneyUserMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUser.class)
+						.eq(MidjourneyUser::getRelationId, relationId)
+						.last("limit 1"));
+				if (user != null) {
+					user.setMjFastNum(sku.getMjFastNum());
+					user.setMjRelaxNum(sku.getMjRelaxNum());
+					user.setIsVip(Boolean.TRUE);
+					user.setExpireTime(vipExpiryTime);
+					midjourneyUserMapper.updateById(user);
+					redisService.del(RedisService.key.MIDJOURNEY_USER.getName() + user.getUserToken());
+				}
+			}
+			if (goodsId == Constant.GPT_PLUS_GID) {
+				ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class)
+						.eq(ChatgptUser::getRelationId, relationId)
+						.last("limit 1"));
+				if (chatgptUser != null) {
+					chatgptUser.setLimitNum(sku.getGptLimitNum());
+					chatgptUser.setLimitTime(sku.getGptLimitTime());
+					chatgptUser.setDsLimitNum(sku.getDsLimitNum());
+					chatgptUser.setDsLimitTime(sku.getDsLimitTime());
+					chatgptUser.setOtherLimitNum(sku.getOtherLimitNum());
+					chatgptUser.setOtherLimitTime(sku.getOtherLimitTime());
+					chatgptUser.setPackageId(0l);
+					chatgptUser.setClearPackageTime(null);
+					chatgptUser.setRenewOrderId(0l);
+					chatgptUser.setRenewSkuId(null);
+					chatgptUser.setRenewExpiryTime(null);
+					chatgptUser.setIsVip(Boolean.TRUE);
+					chatgptUser.setExpireTime(vipExpiryTime);
+					chatgptUserMapper.updateById(chatgptUser);
+					try {
+						//清除次数
+						chatGptAccountService.clearGptNum(relationId);
+					} catch (Exception e) {
+					}
+				}
+			}
+			if (goodsId == Constant.CALUDE_GOODS_ID) {
+				ClaudeUser claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class)
+						.eq(ClaudeUser::getRelationId, relationId)
+						.last("limit 1"));
+				if (claudeUser != null) {
+					claudeUser.setLimitNum(sku.getGptLimitNum());
+					claudeUser.setLimitTime(sku.getGptLimitTime());
+					claudeUser.setRenewOrderId(null);
+					claudeUser.setRenewExpiryTime(null);
+					claudeUser.setRenewSkuId(null);
+					claudeUser.setIsVip(Boolean.TRUE);
+					claudeUser.setExpireTime(vipExpiryTime);
+					claudeUserMapper.updateById(claudeUser);
+					try {
+						//清除次数
+						claudeService.clearNum(claudeUser.getUserToken());
+					} catch (Exception e) {
+					}
+				}
+			}
+			if (goodsId == Constant.GROK_GOODS_ID) {
+				GrokUser grokUser = grokUserMapper.selectOne(Wrappers.lambdaQuery(GrokUser.class)
+						.eq(GrokUser::getRelationId, relationId)
+						.last("limit 1"));
+				if (grokUser != null) {
+					grokUser.setLimitNum(sku.getGptLimitNum());
+					grokUser.setLimitTime(sku.getGptLimitTime());
+					grokUser.setIsVip(Boolean.TRUE);
+					grokUser.setExpireTime(vipExpiryTime);
+					grokUserMapper.updateById(grokUser);
+				}
+			}
+			if (goodsId == Constant.LUMA_GOODS_ID) {
+				LumaUser lumaUser = lumaUserMapper.selectOne(Wrappers.lambdaQuery(LumaUser.class)
+						.eq(LumaUser::getRelationId, relationId)
+						.last("limit 1"));
+				if (lumaUser != null) {
+					lumaUser.setNum(sku.getLumaNum());
+					lumaUser.setIsVip(Boolean.TRUE);
+					lumaUser.setExpireTime(vipExpiryTime);
+					lumaUserMapper.updateById(lumaUser);
+					redisService.del(RedisService.key.LUMA_NUM_LIMIT.getName() + lumaUser.getId());
+				}
+			}
+		}
+	}
+}

+ 2 - 0
netflix-service/src/main/java/com/cyksj/service/pay/AliPayCommonService.java

@@ -30,4 +30,6 @@ public interface AliPayCommonService {
 	AliPayParams openShopAliPay(AlipayOrderDonReq re, String productCode, String qrPayMode) throws Exception;
 
 	AliPayParams aliPaySpotifyUpgrade(AlipayOrderDonReq re, String productCode, String qrPayMode) throws Exception;
+
+	AliPayParams unifiedPayVip(Long orderId, String productCode, String payMode, String returnUrl) throws Exception;
 }

+ 36 - 0
netflix-service/src/main/java/com/cyksj/service/pay/impl/AliPayCommonServiceImpl.java

@@ -28,6 +28,7 @@ import com.cyksj.mapper.*;
 import com.cyksj.mapper.channel.ShopConfigMapper;
 import com.cyksj.mapper.mirrorshop.UserMirrorShopFeeOrderDonMapper;
 import com.cyksj.mapper.order.OrderDonPostMapper;
+import com.cyksj.mapper.vip.VipOrderDonMapper;
 import com.cyksj.model.dto.AliPayParams;
 import com.cyksj.model.dto.OrderPayBuilderDto;
 import com.cyksj.model.entity.*;
@@ -88,6 +89,8 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 
 	private final SpotifyUserUpgradeOrderMapper spotifyUserUpgradeOrderMapper;
 
+	private final VipOrderDonMapper vipOrderDonMapper;
+
 	private static final Sequence SEQUENCE = new Sequence(0);
 
 	@Override
@@ -547,6 +550,39 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 				returnUrl);
 	}
 
+	@Override
+	public AliPayParams unifiedPayVip(Long orderId, String productCode, String payMode, String returnUrl) throws Exception {
+		VipOrderDon orderDon = vipOrderDonMapper.selectById(orderId);
+		Assert.notNull(orderDon, "订单不存在");
+		if (StrUtil.isEmpty(productCode)) throw BusinessRuntimeException.getInstance("支付异常");
+		//获取支付宝默认商户
+		String shopId = sysConfigService.getDefaultShopConfigByType("zfb");
+		orderDon.setShopId(shopId);
+		orderDon.setType(VipOrderDon.Type.ALI_PAY);
+		if (orderDon.getType() != VipOrderDon.Type.ALI_PAY) {
+			orderDon.setType(VipOrderDon.Type.ALI_PAY);
+		}
+		vipOrderDonMapper.updateById(orderDon);
+		//回调地址
+		String host = envCommonService.getNotifyHost();
+		String notifyUrl = String.format("%s%s", host, BaseZfbConfig.zfb_vip_notify_url);
+		if (ZfbProductCode.MOBILE_WEB.getProductCode().equals(productCode) && BaseZfbConfig.qrPayMode_redirect.equals(payMode)) {
+			returnUrl = String.format("%s%s", envCommonService.getDomain(), BaseZfbConfig.zfb_vip_login_return_url);
+		}
+		String subject = orderDon.getPayTitle();
+		AliPayParams aliPayParams = builderPayParams(orderDon.getId(),
+				orderDon.getOrderNo(),
+				orderDon.getShopId(),
+				orderDon.getMoney(),
+				orderDon.getCreatedTime(),
+				subject,
+				productCode,
+				payMode,
+				notifyUrl,
+				returnUrl);
+		return aliPayParams;
+	}
+
 	public String getZfbDayLimitShopId(OrderDon orderDon) {
 		String appId = orderDon.getShopId();
 		if (orderDon.getPopularizeId() != null) {

+ 119 - 0
netflix-service/src/main/java/com/cyksj/service/paypal/PayPalService.java

@@ -8,9 +8,11 @@ import com.cyksj.mapper.GoodsDonSkuMapper;
 import com.cyksj.model.entity.GoodsDonSku;
 import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.entity.PaypalPayConfig;
+import com.cyksj.model.entity.VipOrderDon;
 import com.cyksj.model.request.OrderRefundReq;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.order.OrderDonService;
+import com.cyksj.service.order.VipOrderDonService;
 import com.paypal.api.payments.Currency;
 import com.paypal.api.payments.*;
 import com.paypal.base.rest.APIContext;
@@ -48,6 +50,7 @@ public class PayPalService{
 
     private final GoodsDonSkuMapper skuMapper;
 
+    private final VipOrderDonService vipOrderDonService;
 
     private static final String SANDBOX_URL = "https://api-m.sandbox.paypal.com";
     private static final String LIVE_URL = "https://api-m.paypal.com";
@@ -220,4 +223,120 @@ public class PayPalService{
         return false;
     }
 
+    public Payment createVipPayment(Long orderId, String successUrl) throws PayPalRESTException {
+        VipOrderDon orderDon = vipOrderDonService.getById(orderId);
+        redisService.set(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId,successUrl,RedisService.key.ABROAD_SUCCESS_URL.getTimeout());
+        BigDecimal money = orderDon.getMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN);
+        orderDon.setAbroadMoney(money);
+        orderDon.setType(VipOrderDon.Type.PAYPAL);
+        vipOrderDonService.updateById(orderDon);
+        if (VipOrderDon.Status.hasPayment.equals(orderDon.getStatus())) {
+            throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"该订单已支付");
+        }
+        PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
+        // Set payer details
+        Payer payer = new Payer();
+        payer.setPaymentMethod("paypal");
+
+        // Set redirect URLs
+        RedirectUrls redirectUrls = new RedirectUrls();
+
+        redirectUrls.setCancelUrl(paypalPayConfig.getCancelUrl());
+        String returnUrl = "https://" + paypalPayConfig.getHost() + "/8081/api/applet/vip/orders/paypal/notify";
+        //成功回调地址(自己写的/pay/success)
+        redirectUrls.setReturnUrl(returnUrl);
+
+        Transaction transaction = new Transaction();
+        transaction.setDescription("");
+        transaction.setCustom(orderDon.getId().toString());
+
+        //订单价格
+        Amount amount = new Amount();
+        amount.setCurrency("USD");
+        // 支付的总价,paypal会校验 total = subTotal + tax + ...
+        BigDecimal totalMoney = money.divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN);
+        amount.setTotal(totalMoney.toString());
+        // 设置各种费用
+        Details details = new Details();
+        //商品总价
+        details.setSubtotal(totalMoney.toString());
+
+        amount.setDetails(details);
+        transaction.setAmount(amount);
+
+
+        // Add transaction to a list
+        List<Transaction> transactions = new ArrayList<Transaction>();
+        transactions.add(transaction);
+
+        // Add payment details
+        Payment payment = new Payment();
+
+        payment.setIntent("sale");
+        payment.setPayer(payer);
+        payment.setRedirectUrls(redirectUrls);
+        payment.setTransactions(transactions);
+        return payment.create(this.getAPIContext(paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()));
+    }
+
+    public String successVipPayment(String paymentId, String payerId, PaypalPayConfig paypalPayConfig) throws Exception {
+        log.info("paypal回调 paymentId:{},payerId:{}", paymentId, payerId);
+        Payment payment = null;
+        try {
+            payment = this.executePayment(paymentId, payerId, paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode());
+        } catch (PayPalRESTException e) {
+            log.error("支付回调失败", e);
+            payment = Payment.get(getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode()), paymentId);
+        }
+
+        PayerInfo payerInfo = payment.getPayer().getPayerInfo();
+        log.info("paypal 支付回调 用户信息:{}", payerInfo);
+        //支付成功
+        if ("approved".equals(payment.getState())) {
+            //paypal 交易id
+            Sale sale = payment.getTransactions().get(0).getRelatedResources().get(0).getSale();
+            log.info("paypal 支付回调 订单信息:{}", sale);
+            Currency transactionFee = sale.getTransactionFee();
+            String transationId = sale.getId();
+            String orderId = payment.getTransactions().get(0).getCustom();
+            //更新订单支付状态
+            VipOrderDon orderDon = vipOrderDonService.getById(orderId);
+            orderDon.setTransactionId(transationId);
+            orderDon.setPayTime(new Date());
+            orderDon.setAbroadTransactionFee(new BigDecimal(transactionFee.getValue()).multiply(BigDecimal.valueOf(100)));
+            vipOrderDonService.orderNotify(orderDon);
+            log.info("支付成功订单:" + orderId + ",支付交易号:" + transationId);
+            log.info("支付成功订单:" + orderId + ",返回参数:" + payment);
+
+            THREAD_POOL.execute(() -> {
+                try {
+                    APIContext apiContext = this.getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode());
+                    String baseUrl = "live".equals(paypalPayConfig.getMode()) ? LIVE_URL : SANDBOX_URL;
+                    URL url = new URL(baseUrl + "/v1/shipping/trackers-batch");
+                    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
+                    httpConn.setRequestMethod("POST");
+                    httpConn.setRequestProperty("Content-Type", "application/json");
+                    httpConn.setRequestProperty("Authorization", apiContext.getAccessToken());
+                    httpConn.setDoOutput(true);
+                    OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
+                    writer.write("{\"trackers\": [{ \"transaction_id\": \"" + transationId + "\", \"status\": \"SHIPPED\"}]}");
+                    writer.flush();
+                    writer.close();
+                    httpConn.getOutputStream().close();
+
+                    InputStream responseStream = httpConn.getResponseCode() / 100 == 2 ? httpConn.getInputStream() : httpConn.getErrorStream();
+                    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
+                    String response = s.hasNext() ? s.next() : "";
+                    log.info("添加物流信息 response:{}", response);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+            });
+
+            return redisService.getStr(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId);
+        }
+        log.info("支付失败返回参数:" + Jsons.toJson(payment));
+        return paypalPayConfig.getCancelUrl();
+    }
 }

+ 131 - 5
netflix-service/src/main/java/com/cyksj/service/paypal/PaypalV2Service.java

@@ -1,25 +1,22 @@
 package com.cyksj.service.paypal;
 
-import cn.hutool.json.JSONObject;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.enums.GatewayApiCode;
-import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GoodsDonSkuMapper;
 import com.cyksj.model.entity.GoodsDonSku;
 import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.entity.PaypalPayConfig;
+import com.cyksj.model.entity.VipOrderDon;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.order.OrderDonService;
-import com.paypal.api.payments.Amount;
-import com.paypal.api.payments.Sale;
+import com.cyksj.service.order.VipOrderDonService;
 import com.paypal.base.rest.APIContext;
 import com.paypal.base.rest.OAuthTokenCredential;
 import com.paypal.base.rest.PayPalRESTException;
 import com.paypal.core.PayPalHttpClient;
 import com.paypal.http.HttpResponse;
-import com.paypal.http.serializer.Json;
 import com.paypal.orders.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -52,6 +49,9 @@ public class PaypalV2Service {
     private final RedisService redisService;
 
     private final GoodsDonSkuMapper skuMapper;
+
+    private final VipOrderDonService vipOrderDonService;
+
     private static final String SANDBOX_URL = "https://api-m.sandbox.paypal.com";
 
     private static final String LIVE_URL = "https://api-m.paypal.com";
@@ -269,4 +269,130 @@ public class PaypalV2Service {
         apiContext.setConfigurationMap(sdkConfig);
         return apiContext;
     }
+
+    public String createVipPayment(Long orderId, String successUrl) {
+        VipOrderDon orderDon = vipOrderDonService.getById(orderId);
+        redisService.set(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId,successUrl,RedisService.key.ABROAD_SUCCESS_URL.getTimeout());
+        BigDecimal money = orderDon.getMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN);
+        orderDon.setAbroadMoney(money);
+        orderDon.setType(VipOrderDon.Type.PAYPAL);
+        vipOrderDonService.updateById(orderDon);
+        if (VipOrderDon.Status.hasPayment.equals(orderDon.getStatus())) {
+            throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"该订单已支付");
+        }
+        PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
+        PayPalClient payPalClient = new PayPalClient();
+        // 设置环境沙盒或生产
+        PayPalHttpClient client = payPalClient.client(paypalPayConfig.getMode(), paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret());
+
+        //回调参数(支付成功success路径所携带的参数)
+        Map<String, String> sParaTemp = new HashMap<String, String>();
+
+        sParaTemp.put("orderId", orderId.toString());
+
+        String returnUrl = "https://" + paypalPayConfig.getHost() + "/8081/api/applet/vip/orders/paypalV2/notify";
+
+        // 配置请求参数
+        OrderRequest orderRequest = new OrderRequest();
+        orderRequest.checkoutPaymentIntent("CAPTURE");
+        List<PurchaseUnitRequest> purchaseUnits = new ArrayList<>();
+        BigDecimal totalMoney = money.divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN);
+        purchaseUnits.add(new PurchaseUnitRequest().referenceId(orderId.toString()).amountWithBreakdown(new AmountWithBreakdown().currencyCode("USD").value(totalMoney.toString())));
+        orderRequest.purchaseUnits(purchaseUnits);
+        orderRequest.applicationContext(new ApplicationContext()
+                .landingPage(LANDINGPAGE)
+                .userAction(USERACTION)
+                .returnUrl(returnUrl)
+                .cancelUrl(paypalPayConfig.getCancelUrl()));
+        OrdersCreateRequest request = new OrdersCreateRequest().requestBody(orderRequest);
+
+        HttpResponse<Order> response;
+        try {
+            response = client.execute(request);
+            Order order = response.result();
+            String payHref = null;
+            String status = order.status();
+            if (status.equals("CREATED")) {
+                List<LinkDescription> links = order.links();
+                for (LinkDescription linkDescription : links) {
+                    if (linkDescription.rel().equals("approve")) {
+                        payHref = linkDescription.href();
+                    }
+                }
+            }
+            return payHref;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public String successVipPayment(String token, String payerId, PaypalPayConfig paypalPayConfig) throws Exception {
+        log.info("paypalV2回调 token:{},payerId:{}", token, payerId);
+        //捕获订单 进行支付
+        HttpResponse<Order> response = null;
+        OrdersCaptureRequest ordersCaptureRequest = new OrdersCaptureRequest(token);
+        ordersCaptureRequest.requestBody(new OrderRequest());
+
+        PayPalClient payPalClient = new PayPalClient();
+        try {
+            response = payPalClient.client(paypalPayConfig.getMode(), paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret()).execute(ordersCaptureRequest);
+            Order result = response.result();
+            log.info("captureOrder response: {}", result);
+            for (PurchaseUnit purchaseUnit : result.purchaseUnits()) {
+                for (Capture capture : purchaseUnit.payments().captures()) {
+                    if ("COMPLETED".equals(capture.status())) {
+                        //支付成功
+                        // 订单号
+                        String saleId = capture.id();
+                        String fee = capture.sellerReceivableBreakdown().paypalFee().value();
+                        String orderId = purchaseUnit.referenceId();
+                        String json = Jsons.toJson(result);
+                        log.info("captureOrder response body: {}", json);
+                        //更新订单支付状态
+                        VipOrderDon orderDon = vipOrderDonService.getById(orderId);
+                        orderDon.setTransactionId(saleId);
+                        orderDon.setPayTime(new Date());
+                        orderDon.setAbroadTransactionFee(new BigDecimal(fee).multiply(BigDecimal.valueOf(100)));
+                        vipOrderDonService.orderNotify(orderDon);
+                        log.info("支付成功订单:"+orderId+",支付交易号:"+saleId);
+
+                        THREAD_POOL.execute(()->{
+                            try {
+                                APIContext apiContext = this.getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode());
+                                String baseUrl = "live".equals(paypalPayConfig.getMode()) ? LIVE_URL : SANDBOX_URL;
+                                URL url = new URL(baseUrl + "/v1/shipping/trackers-batch");
+                                HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
+                                httpConn.setRequestMethod("POST");
+                                httpConn.setRequestProperty("Content-Type", "application/json");
+                                httpConn.setRequestProperty("Authorization", apiContext.getAccessToken());
+                                httpConn.setDoOutput(true);
+                                OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
+                                writer.write("{\"trackers\": [{ \"transaction_id\": \"" + saleId + "\", \"status\": \"SHIPPED\"}]}");
+                                writer.flush();
+                                writer.close();
+                                httpConn.getOutputStream().close();
+
+                                InputStream responseStream = httpConn.getResponseCode() / 100 == 2 ? httpConn.getInputStream() : httpConn.getErrorStream();
+                                Scanner s = new Scanner(responseStream).useDelimiter("\\A");
+                                String res = s.hasNext() ? s.next() : "";
+                                log.info("添加物流信息 response:{}",res);
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+
+                        });
+
+                        return redisService.getStr(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } catch (Exception e) {
+	        throw new RuntimeException(e);
+        }
+	    log.info("支付失败返回参数:"+ Jsons.toJson(response.result()));
+        return paypalPayConfig.getCancelUrl();
+    }
 }

+ 7 - 0
netflix-service/src/main/java/com/cyksj/service/relation/GroupRelationFrontService.java

@@ -91,4 +91,11 @@ public interface GroupRelationFrontService {
 	Long getTrialTicketByUserIdAndGoodsId(Long userId, Long trialSkuId, Integer ticketType);
 
 	Boolean getMirrorPopups(long userId);
+
+	/**
+	 * 用户获取车票
+	 */
+	Long getTicket(Long userId, Long skuId, Boolean isVip);
+
+	Boolean getVipMirrorTicketIfEmpty(long userId, Long skuId);
 }

+ 1 - 3
netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationClearServiceImpl.java

@@ -15,7 +15,6 @@ import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.util.Date;
 
@@ -42,7 +41,6 @@ public class GroupRelationClearServiceImpl implements GroupRelationClearService
 	private final MirrorUserCleatService mirrorUserCleatService;
 
 	@Override
-	@Transactional(rollbackFor = Throwable.class)
 	public void clearTicket(GroupsRelation relation, UserTicketClearedRecord.Source source) {
 		Long relationId = relation.getId();
 		Long newRelationId = relation.getNewRelationId();
@@ -128,7 +126,7 @@ public class GroupRelationClearServiceImpl implements GroupRelationClearService
 //						log.info("ChatGPT 独立会员账号车票过期,删除原先车队id:{}并生成一个10人月付规格的车队id:{}", groupsRelation.getId(), gt.getId());
 //					}
 //				}
-				mirrorUserCleatService.delMirrorUser(groupsRelation.getGoodsId(), groupsRelation.getId());
+				mirrorUserCleatService.delMirrorUser(groupsRelation.getGoodsId(), groupsRelation.getId(), userTicketClearedRecord.getId());
 			}
 		}
 	}

+ 121 - 2
netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationFrontServiceImpl.java

@@ -26,8 +26,11 @@ import com.cyksj.mapper.mirrorshop.UserMirrorShopMapper;
 import com.cyksj.mapper.renew.RenewUpgradePackageMapper;
 import com.cyksj.mapper.sys.SysConfigMapper;
 import com.cyksj.mapper.sys.SysEmailMapper;
+import com.cyksj.mapper.vip.VipGoodsSkuMapper;
+import com.cyksj.mapper.vip.VipUserMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.AccountView;
+import com.cyksj.model.manage.views.GroupsRelationView;
 import com.cyksj.model.request.SpecialGoodsTicketReq;
 import com.cyksj.model.request.TicketLoginReq;
 import com.cyksj.model.views.*;
@@ -171,6 +174,10 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 
 	private final MirrorTicketPopupsRecordMapper mirrorTicketPopupsRecordMapper;
 
+	private final VipUserMapper vipUserMapper;
+
+	private final VipGoodsSkuMapper vipGoodsSkuMapper;
+
 	@Override
 	public SearchResult<RenewalView> getMyTicket(long userId, Boolean isLoginPopularize, String customId, String account, String orderNo, Integer cmt) {
 		User u = userMapper.selectById(userId);
@@ -233,6 +240,10 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 				} catch (Exception e) {
 				}
 			}
+			List<Long> vipSkuIds = vipGoodsSkuMapper.selectList(null).stream().map(VipGoodsSku::getSkuId).collect(Collectors.toList());
+			Integer vipUserCount = vipUserMapper.selectCount(Wrappers.lambdaQuery(VipUser.class)
+					.in(VipUser::getUserId, userIds)
+					.gt(VipUser::getExpiryTime, now));
 			list.getDataList().forEach(ticket -> {
 				if (ticket.getSkuId() == null || ticket.getGoodsId() == null) {
 					return;
@@ -291,7 +302,7 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 						remainDays = days + 1 + "天";
 					}
 					ticket.setRemainDays(remainDays);
-					ticket.setTotal(DateUtil.betweenDay(ticket.getStartTime(), ticket.getExpiryTime(), false));
+					ticket.setTotal(DateUtil.betweenDay(ticket.getStartTime(), ticket.getExpiryTime(), false) + 1);
 				}
 				OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
 						.eq(OrderDon::getRelationId, ticket.getRelationId())
@@ -373,13 +384,14 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 				}
 
 				if (CollUtil.isNotEmpty(gptBuyGids) && gptBuyGids.contains(ticket.getGoodsId())) {
-					if (gptMirrorDayCardGiveawayActivity != null && orderDon!=null && orderDon.getCreatedTime().compareTo(gptMirrorDayCardGiveawayActivity.getCreatedTime()) > 0) {
+					if (gptMirrorDayCardGiveawayActivity != null && orderDon != null && orderDon.getCreatedTime().compareTo(gptMirrorDayCardGiveawayActivity.getCreatedTime()) > 0) {
 						//是否领取过GPT日卡
 						Long orderId = orderDon.getId();
 						Number count = beanSearcher.searchCount(GptMirrorDayCardActivityRecord.class, MapUtils.builder().field(GptMirrorDayCardActivityRecord::getOrderId, orderId).build());
 						ticket.setReceivedGptGift(count.intValue() > 0);
 					}
 				}
+				markVipDeductTicket(userIds, ticket, vipSkuIds, vipUserCount > 0);
 			});
 		}
 		userLoginRecordService.saveUserLoginDayRecord(userId);
@@ -812,6 +824,7 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 				.field(RenewalView::getExpiryTime, now).op(Operator.GreaterThan)
 				.field(RenewalView::getStatus, List.of("validity", "overdue", "outside")).op(Operator.InList)
 				.field(RenewalView::getAqType, 1)
+				.field(RenewalView::getIsVip, Boolean.TRUE)
 				.orderBy(RenewalView::getExpiryTime).asc()
 				.build());
 		if (list.size() > 5) {
@@ -1308,6 +1321,91 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 		return flag;
 	}
 
+	@Override
+	public Long getTicket(Long userId, Long skuId, Boolean isVip) {
+		Retryer<Long> build = RetryerBuilder.<Long>newBuilder()
+				.retryIfException()
+				//运行时异常重试
+				.retryIfRuntimeException()
+				//false重试
+				.retryIfResult(res -> res == null)
+				//1s 间隔
+				.withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
+				//停止策略 : 尝试请求1次
+				.withStopStrategy(StopStrategies.stopAfterAttempt(1)).build();
+		try {
+			return build.call(() -> {
+				GroupsTrips groups = groupsMapper.selectCollegeStudentUserGptTicket(skuId);
+				GroupsRelation relation = null;
+				if (groups != null) {
+					relation = groupsRelationMapper.selectRandomOneRelationByGroupsId(groups.getId());
+				}
+				GoodsDonSku sku = goodsDonSkuMapper.selectById(skuId);
+				//生成空车队
+				if (relation == null) {
+					relation = groupsFuncService.createNewGroupsTrips(sku);
+				}
+				DateTime startTime = DateTime.now();
+				DateTime expiryTime;
+				if (sku.getDays() != null) {
+					expiryTime = DateUtil.offsetDay(startTime, sku.getDays());
+				} else {
+					expiryTime = DateUtil.offsetMonth(startTime, sku.getMonths());
+				}
+				int update = groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+						.set(GroupsRelation::getUserId, userId)
+						.set(GroupsRelation::getStartTime, startTime)
+						.set(GroupsRelation::getExpiryTime, expiryTime)
+						.set(GroupsRelation::getStatus, GroupsRelation.Status.validity)
+						.set(GroupsRelation::getIsVip, isVip)
+						.eq(GroupsRelation::getId, relation.getId())
+						.eq(GroupsRelation::getUserId, 0));
+				if (update > 0) {
+					groupsMapper.decrAvailableNum(groups.getId());
+					return relation.getId();
+				}
+				log.error("发送用户:{} GPT镜像车票失败,进行重试", userId);
+				return null;
+			});
+		} catch (ExecutionException | RetryException e) {
+			log.error("发送用户GPT镜像失败:{}", StringUtil.getErrorText(e));
+			return null;
+		}
+	}
+
+	@Override
+	public Boolean getVipMirrorTicketIfEmpty(long userId, Long skuId) {
+		Integer selectCount = vipGoodsSkuMapper.selectCount(Wrappers.lambdaQuery(VipGoodsSku.class)
+				.eq(VipGoodsSku::getSkuId, skuId));
+		if (selectCount == 0) {
+			return false;
+		}
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		VipUser vipUser = vipUserMapper.selectOne(Wrappers.lambdaQuery(VipUser.class)
+				.in(VipUser::getUserId, userIdList)
+				.gt(VipUser::getExpiryTime, DateTime.now())
+				.last("limit 1"));
+		//不是会员 或已到期
+		if (vipUser == null) {
+			return false;
+		}
+		Number number = beanSearcher.searchCount(GroupsRelationView.class, MapUtils.builder()
+				.field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
+				.field(GroupsRelationView::getSkuId, skuId)
+				.field(GroupsRelationView::getIsVip, Boolean.TRUE)
+				.build());
+		if (number.intValue() == 0) {
+			Long relationId = getTicket(vipUser.getUserId(), skuId, Boolean.TRUE);
+			//同步时间
+			groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+					.set(GroupsRelation::getExpiryTime, vipUser.getExpiryTime())
+					.eq(GroupsRelation::getUserId, vipUser.getUserId())
+					.eq(GroupsRelation::getId, relationId));
+			return true;
+		}
+		return false;
+	}
+
 	private DistributePosterGoodsDetailView setGoodsPoster(RenewalView ticket, User user, List<Long> userIds) {
 		DistributePosterGoodsDetailView distributePosterGoodsDetailView = beanSearcher.searchFirst(DistributePosterGoodsDetailView.class, MapUtils.flatBuilder(request.getParameterMap())
 				.field(DistributePosterGoodsFrontView::getGoodsId, ticket.getGoodsId()).build());
@@ -1404,6 +1502,7 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 					ticket.setMjUserFastNum(search.getMjUserFastNum());
 					ticket.setMjUserRelaxNum(search.getMjUserRelaxNum());
 				}
+				midjourneyAccountService.updateUserExpiryTime(ticket.getRelationId(), ticket.getExpiryTime());
 			} catch (Exception e) {
 			}
 		}
@@ -1504,4 +1603,24 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 		return null;
 	}
 
+	/**
+	 * 标记是否可抵扣会员车票
+	 */
+	private void markVipDeductTicket(List<Long> userIds, RenewalView ticket, List<Long> vipSkuIds, Boolean isVip) {
+		if (CollUtil.isNotEmpty(vipSkuIds) && !isVip) {
+			Long skuId = ticket.getSkuId();
+			if (vipSkuIds.contains(skuId)) {
+				//是否时长最长
+				Number number = beanSearcher.searchCount(RenewalView.class, MapUtils.builder()
+						.field(RenewalView::getRelationId, ticket.getRelationId()).op(Operator.NotEqual)
+						.field(RenewalView::getSkuId, skuId)
+						.field(RenewalView::getUserId, userIds).op(Operator.InList)
+						.field(RenewalView::getExpiryTime, ticket.getExpiryTime()).op(Operator.GreaterThan)
+						.build());
+				if (number.intValue() == 0) {
+					ticket.setIsVipDeduct(true);
+				}
+			}
+		}
+	}
 }

+ 3 - 0
netflix-service/src/main/java/com/cyksj/service/user/UserService.java

@@ -9,6 +9,7 @@ import com.cyksj.model.entity.User;
 import com.cyksj.model.entity.WxSyncLog;
 import com.cyksj.model.request.LoginReq;
 import com.cyksj.model.views.UserCertInfoView;
+import com.cyksj.model.views.UserSyncInfoView;
 
 import javax.servlet.http.HttpServletRequest;
 import java.util.List;
@@ -70,4 +71,6 @@ public interface UserService extends IService<User> {
 	void saveEpDistributeShared(Long userId, String dsCode);
 
 	UserCertInfoView getCertOverView(long userId);
+
+	UserSyncInfoView getUserSyncInfo(long userId);
 }

+ 22 - 5
netflix-service/src/main/java/com/cyksj/service/user/impl/UserServiceImpl.java

@@ -25,13 +25,11 @@ import com.cyksj.mapper.manage.distribute.UserDistributeSharedMapper;
 import com.cyksj.mapper.market.task.UserBindDetailMapper;
 import com.cyksj.mapper.mirrorshop.UserMirrorShopMapper;
 import com.cyksj.mapper.station.CollegeStudentStationUserRelateMapper;
+import com.cyksj.mapper.vip.VipUserMapper;
 import com.cyksj.model.dto.*;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.request.LoginReq;
-import com.cyksj.model.views.CorpUserFollowView;
-import com.cyksj.model.views.RenewalView;
-import com.cyksj.model.views.UserCertInfoView;
-import com.cyksj.model.views.UserTicketView;
+import com.cyksj.model.views.*;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.distribute.DistributeService;
 import com.cyksj.service.distribute.equipment.EquipmentDistributeService;
@@ -137,6 +135,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 
 	private final UserCommonService userCommonService;
 
+	private final VipUserMapper vipUserMapper;
+
 	private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
 
 	@Override
@@ -295,7 +295,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 				.in(OrderDon::getUserId, userIdList)
 				.notIn(OrderDon::getStatus, Constant.noOrderStatus));
 		userWhoami.setIsPay(selectCount > 0);
-	    return userWhoami;
+		return userWhoami;
     }
 
     @Override
@@ -874,6 +874,23 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 		return userCertInfoView;
 	}
 
+	@Override
+	public UserSyncInfoView getUserSyncInfo(long userId) {
+		UserSyncInfoView syncInfoView = new UserSyncInfoView();
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		//是否是会员
+		VipUser vipUser = vipUserMapper.selectOne(Wrappers.lambdaQuery(VipUser.class)
+				.in(VipUser::getUserId, userIdList)
+				.gt(VipUser::getExpiryTime, DateTime.now())
+				.last("limit 1"));
+		if (vipUser != null) {
+			syncInfoView.setIsVip(Boolean.TRUE);
+			syncInfoView.setVipStartTime(vipUser.getStartTime());
+			syncInfoView.setVipExpiryTime(vipUser.getExpiryTime());
+		}
+		return syncInfoView;
+	}
+
 	/**
 	 * 处理大学生快递站用户
 	 */

+ 16 - 0
netflix-service/src/main/java/com/cyksj/service/vip/VipFrontService.java

@@ -0,0 +1,16 @@
+package com.cyksj.service.vip;
+
+import com.cyksj.model.dto.VipDeductDto;
+import com.cyksj.model.response.VipUserPageResp;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipFrontService
+ * 创建者: JavaZou
+ * 创建时间:2025/5/15 15:59
+ */
+public interface VipFrontService {
+	void fillVipInfo(long userId, VipUserPageResp vipUserPageResp);
+
+	VipDeductDto checkAndGetOrderDeductMoney(Long userId);
+}

+ 240 - 0
netflix-service/src/main/java/com/cyksj/service/vip/impl/VipFrontServiceImpl.java

@@ -0,0 +1,240 @@
+package com.cyksj.service.vip.impl;
+
+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.exception.BusinessRuntimeException;
+import com.cyksj.mapper.*;
+import com.cyksj.mapper.sys.SysConfigMapper;
+import com.cyksj.mapper.vip.VipGoodsSkuMapper;
+import com.cyksj.model.dto.VipDeductDto;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.manage.views.GroupsRelationView;
+import com.cyksj.model.response.VipUserPageResp;
+import com.cyksj.service.user.UserBindRelationService;
+import com.cyksj.service.vip.VipFrontService;
+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.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: VipFrontServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2025/5/15 16:00
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class VipFrontServiceImpl implements VipFrontService {
+	private final SysConfigMapper sysConfigMapper;
+
+	private final VipGoodsSkuMapper vipGoodsSkuMapper;
+
+	private final UserBindRelationService userBindRelationService;
+
+	private final BeanSearcher beanSearcher;
+
+	private final GoodsDonSkuMapper skuMapper;
+
+	private final ChatgptUserMapper chatgptUserMapper;
+
+	private final OrderDonRenewRecordMapper orderDonRenewRecordMapper;
+
+	private final ClaudeUserMapper claudeUserMapper;
+
+	private final OrderDonMapper orderDonMapper;
+
+	@Override
+	public void fillVipInfo(long userId, VipUserPageResp vipUserPageResp) {
+		SysConfig sysConfig = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, Constant.VIP_FEE_KEY).last("limit 1"));
+		if (sysConfig != null && StrUtil.isNotBlank(sysConfig.getSysValue())) {
+			vipUserPageResp.setVipFee(BigDecimal.valueOf(Integer.parseInt(sysConfig.getSysValue())));
+		}
+		//可抵扣价格
+		BigDecimal deductMoney = BigDecimal.ZERO;
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		List<Long> vipSkuIds = vipGoodsSkuMapper.selectList(null).stream().map(VipGoodsSku::getSkuId).collect(Collectors.toList());
+		//可有抵扣车票
+		DateTime now = DateTime.now();
+		Number count = beanSearcher.searchCount(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getUserId, userIdList).op(Operator.InList).field(GroupsRelationView::getSkuId, vipSkuIds).op(Operator.InList).field(GroupsRelationView::getExpiryTime, now).op(Operator.GreaterThan).build());
+		//可抵扣车票
+		if (count.intValue() > 0) {
+			//处理vip抵扣金额
+			VipDeductDto vipDeductDto = commonHandleVipDeductMoney(userIdList, vipSkuIds);
+			deductMoney = vipDeductDto.getDeductMoney();
+			vipUserPageResp.setRelationUserViews(vipDeductDto.getRelationUserViews());
+		}
+		vipUserPageResp.setDeductMoney(deductMoney);
+	}
+
+	/**
+	 * 检查用户并获取订单抵扣金额
+	 */
+	@Override
+	public VipDeductDto checkAndGetOrderDeductMoney(Long userId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		List<Long> vipSkuIds = vipGoodsSkuMapper.selectList(null).stream().map(VipGoodsSku::getSkuId).collect(Collectors.toList());
+		Number count = beanSearcher.searchCount(GroupsRelationView.class, MapUtils.builder()
+				.field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
+				.field(GroupsRelationView::getSkuId, vipSkuIds).op(Operator.InList)
+				.field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
+				.field(GroupsRelationView::getIsVip, false)
+				.build());
+		if (count.intValue() == 0) {
+			throw BusinessRuntimeException.getInstance("暂无可抵扣AI车票");
+		}
+		VipDeductDto vipDeductDto = commonHandleVipDeductMoney(userIdList, vipSkuIds);
+		return vipDeductDto;
+	}
+
+
+	private BigDecimal getVipMoney() {
+		SysConfig sysConfig = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, Constant.VIP_FEE_KEY).last("limit 1"));
+		if (sysConfig == null || StrUtil.isEmpty(sysConfig.getSysValue())) {
+			throw BusinessRuntimeException.getInstance("系统异常");
+		}
+		BigDecimal vipFee = BigDecimal.valueOf(Long.valueOf(sysConfig.getSysValue()));
+		return vipFee;
+	}
+
+	/**
+	 * 统一处理vip抵扣金额
+	 */
+	public VipDeductDto commonHandleVipDeductMoney(List<Long> userIdList, List<Long> vipSkuIds) {
+		VipDeductDto vipDeductDto = new VipDeductDto();
+		DateTime now = DateTime.now();
+		List<Long> deductRelationIds = new ArrayList<>();
+		BigDecimal deductMoney = BigDecimal.ZERO;
+		BigDecimal vipMoney = getVipMoney();
+		List<GroupsRelationView> relations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
+				.field(GroupsRelationView::getSkuId, vipSkuIds).op(Operator.InList)
+				.field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
+				.field(GroupsRelationView::getIsVip, false)
+				.field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
+				.orderBy(GroupsRelationView::getExpiryTime).desc()
+				.build());
+		Set<Long> deductSkuIds = null;
+		List<GroupsRelationView> deductRelations = null;
+		for (GroupsRelationView relation : relations) {
+			if (deductSkuIds == null) {
+				deductSkuIds = new HashSet<>();
+			}
+			if (deductRelations == null) {
+				deductRelations = new ArrayList<>();
+			}
+			Long vipSkuId = relation.getSkuId();
+			if (deductSkuIds.contains(vipSkuId)) {
+				continue;
+			}
+			deductSkuIds.add(vipSkuId);
+			Long goodsId = relation.getGoodsId();
+			Long relationId = relation.getId();
+			Date relationExpiryTime = relation.getExpiryTime();
+			//GPT
+			if (goodsId == Constant.GPT_PLUS_GID) {
+				ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId).last("limit 1"));
+				if (chatgptUser != null) {
+					//获取抵扣金额
+					deductMoney = deductMoney.add(getFinalDeductMoney(relationId, relation.getSkuId(), chatgptUser.getRenewSkuId(), chatgptUser.getRenewOrderId(), chatgptUser.getRenewExpiryTime(), relation.getStartTime(), relationExpiryTime, now, userIdList));
+				}
+			} else if (goodsId == Constant.CLAUDE_PRO_GID) {
+				ClaudeUser claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class).eq(ClaudeUser::getRelationId, relationId).last("limit 1"));
+				if (claudeUser != null) {
+					//获取抵扣金额
+					deductMoney = deductMoney.add(getFinalDeductMoney(relationId, relation.getSkuId(), claudeUser.getRenewSkuId(), claudeUser.getRenewOrderId(), claudeUser.getRenewExpiryTime(), relation.getStartTime(), relationExpiryTime, now, userIdList));
+				}
+			} else {
+				//该车票抵扣金额
+				deductMoney = deductMoney.add(getDeductMoneyFromTicket(vipSkuId, now, relation.getId(), relation.getStartTime(), relationExpiryTime));
+			}
+			deductRelationIds.add(relationId);
+			//可抵扣车票
+			deductRelations.add(relation);
+			//超出可抵扣的金额 直接返回
+			if (deductMoney.compareTo(vipMoney) >= 0) {
+				deductMoney = vipMoney;
+				break;
+			}
+		}
+		vipDeductDto.setDeductMoney(deductMoney);
+		vipDeductDto.setDeductRelationIds(deductRelationIds);
+		vipDeductDto.setRelationUserViews(deductRelations);
+		return vipDeductDto;
+	}
+
+	/**
+	 * 获取最终抵扣金额
+	 */
+	public BigDecimal getFinalDeductMoney(Long relationId, Long relationSkuId, Long renewSkuId, Long renewOrderId, Date renewExpiryTime, Date relationStartTime, Date relationExpiryTime, DateTime now, List<Long> userIdList) {
+		BigDecimal deductMoney = BigDecimal.ZERO;
+		//一个月当30天算单价 往上取整
+		//续费规格
+		if (renewSkuId != null) {
+			OrderDon renewOrderDon = orderDonMapper.selectById(renewOrderId);
+			GoodsDonSku sku = skuMapper.selectById(renewSkuId);
+			Long betweenDay = DateUtil.betweenDay(now, renewExpiryTime, false) + 1;
+			//当天购买的按规格原价抵扣
+			if (now.toDateStr().equals(DateUtil.format(renewOrderDon.getCreatedTime(), "yyyy-MM-dd"))) {
+				deductMoney = deductMoney.add(sku.getPrice());
+			} else {
+				BigDecimal single = sku.getPrice().divide(BigDecimal.valueOf(sku.getMonths()).multiply(BigDecimal.valueOf(30)), 0, RoundingMode.UP);
+				//该车票抵扣金额
+				BigDecimal renewDeductMoney = single.multiply(BigDecimal.valueOf(betweenDay));
+				log.info("车票id:{}目前续费升级规格可抵扣的金额为:{}", relationId, renewDeductMoney);
+				deductMoney = deductMoney.add(renewDeductMoney);
+			}
+			relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -betweenDay.intValue());
+			//上一个续费升级规格
+			List<OrderDon> orderDOns = orderDonRenewRecordMapper.getOtherRenewSkus(renewOrderId, relationId, userIdList);
+			for (OrderDon orderDOn : orderDOns) {
+				Long skuId = orderDOn.getSkuId();
+				sku = skuMapper.selectById(skuId);
+				BigDecimal upDeductMoney = sku.getPrice();
+				log.info("车票id:{}目前次一级规格:{}可抵扣的金额为:{}", relationId, sku.getSpecVal(), upDeductMoney);
+				deductMoney = deductMoney.add(upDeductMoney);
+				if (sku.getDays() != null) {
+					relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -sku.getDays());
+				} else {
+					relationExpiryTime = DateUtil.offsetMonth(relationExpiryTime, -sku.getMonths());
+				}
+			}
+		}
+		if (relationExpiryTime.after(DateTime.now())) {
+			//该车票抵扣金额
+			BigDecimal deductMoneyFromTicket = getDeductMoneyFromTicket(relationSkuId, now, relationId, relationStartTime, relationExpiryTime);
+			deductMoney = deductMoney.add(deductMoneyFromTicket);
+		}
+		log.info("车票id:{}最终到期时间为:{}车票可返回 抵扣金额:{}", relationId, DateUtil.format(relationExpiryTime, "yyyy-MM-dd HH:mm:ss"), deductMoney);
+		return deductMoney;
+	}
+
+	/**
+	 * 获取车票可抵扣金额
+	 */
+	public BigDecimal getDeductMoneyFromTicket(Long skuId, DateTime now, Long relationId, Date relationStartTime, Date relationExpiryTime) {
+		GoodsDonSku sku = skuMapper.selectById(skuId);
+		BigDecimal single = sku.getPrice().divide(BigDecimal.valueOf(sku.getMonths()).multiply(BigDecimal.valueOf(30)), 0, RoundingMode.UP);
+		Long betweenDay = DateUtil.betweenDay(now, relationExpiryTime, false) + 1;
+		//当天购买的按规格原价抵扣
+		if (now.toDateStr().equals(DateUtil.format(relationStartTime, "yyyy-MM-dd"))) {
+			//并且没有续费 且未扣时长
+			if (DateUtil.betweenDay(DateUtil.offsetMonth(relationStartTime, 1), relationExpiryTime, true) == 0) {
+				return sku.getPrice();
+			}
+		}
+		//该车票抵扣金额
+		BigDecimal deductMoney = single.multiply(BigDecimal.valueOf(betweenDay));
+		return deductMoney;
+	}
+}

+ 1 - 1
netflix-web/src/main/java/com/cyksj/web/controller/external/ExternalController.java

@@ -121,7 +121,7 @@ public class ExternalController {
 
 				//清除gtpUser
 				if (orderDon.getOrderType() == 1) {
-					mirrorUserCleatService.delMirrorUser(goodsDon.getId(), orderDon.getRelationId());
+					mirrorUserCleatService.delMirrorUser(goodsDon.getId(), orderDon.getRelationId(), null);
 				}
 
 				//重新生成兑换码

+ 1 - 1
netflix-web/src/main/java/com/cyksj/web/controller/goods/GoodsDonController.java

@@ -1204,7 +1204,7 @@ public class GoodsDonController {
 		Object o = redisService.get(key);
 		if (o == null) {
 			MapBuilder builder = MapUtils.builder()
-					.field(HomeManagementFrontView::getAdType, List.of(HomeManagementConfig.AdType.pc.name(), HomeManagementConfig.AdType.pcSec.name(), HomeManagementConfig.AdType.pcDis.name())).op(Operator.InList);
+					.field(HomeManagementFrontView::getAdType, List.of(HomeManagementConfig.AdType.pc.name(), HomeManagementConfig.AdType.pcBanner.name(), HomeManagementConfig.AdType.pcSec.name(), HomeManagementConfig.AdType.pcDis.name())).op(Operator.InList);
 			if (filterConfigId != null) {
 				builder.field(HomeManagementFrontView::getHomeManageConfigId, filterConfigId).op(Operator.NotEqual);
 			}

+ 26 - 4
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsUserController.java

@@ -19,6 +19,7 @@ import com.cyksj.mapper.*;
 import com.cyksj.mapper.corp.CorpUserFollowRelationMapper;
 import com.cyksj.mapper.manage.cms.CmsUserMapper;
 import com.cyksj.mapper.market.task.UserBindDetailMapper;
+import com.cyksj.mapper.vip.VipUserMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.MaliceUserView;
 import com.cyksj.model.manage.views.UserView;
@@ -87,10 +88,12 @@ public class CmsUserController{
 
     private final UserDoubleVerifyCodeChangeRecordMapper userDoubleVerifyCodeChangeRecordMapper;
 
+    private final VipUserMapper vipUserMapper;
+
     private final List<Long> no_blacks_userIds = List.of(375274l, 545599l, 523937l, 507498l, 604146l, 613437l, 651728l, 614100l, 604148l);
 
     @GetMapping("/get")
-    public Result<SearchResult<UserView>> get(Long id, String otherPhone, Long sharedId, String userid, Long businessId, String sort, String order, Boolean isCert, Boolean isAbroad) {
+    public Result<SearchResult<UserView>> get(Long id, String otherPhone, Long sharedId, String userid, Long businessId, String sort, String order, Boolean isCert, Boolean isAbroad, Boolean isVip) {
         MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
         String otherSql = StrUtil.EMPTY;
         if (StrUtil.isNotBlank(otherPhone)) {
@@ -124,9 +127,16 @@ public class CmsUserController{
             }
             otherSql += String.format(" %s (select 1 from phone_code pc where pc.phone = u.login_phone and cid is not null and cid != 86)", isAbroad ? "exists" : "not exists");
         }
+        if (isVip != null) {
+            if (StrUtil.isNotBlank(otherSql)) {
+                otherSql += " and ";
+            }
+            otherSql += String.format(" %s (select 1 from vip_user vu where vu.user_id = u.id and vu.expiry_time > now())", isVip ? "exists" : "not exists");
+        }
         if (StrUtil.isNotBlank(otherSql)) {
             builder.put("otherConditionSql", otherSql);
         }
+        DateTime now = DateTime.now();
         SearchResult<UserView> search = beanSearcher.search(UserView.class, builder.build());
         search.getDataList().forEach(data -> {
             Long userId = data.getId();
@@ -145,18 +155,30 @@ public class CmsUserController{
             Integer selectCount = userCertRecordMapper.selectCount(Wrappers.lambdaQuery(UserCertRecord.class)
                     .eq(UserCertRecord::getUserId, userId));
             data.setIsCert(selectCount > 0 ? true : false);
+            VipUser vipUser = vipUserMapper.selectOne(Wrappers.lambdaQuery(VipUser.class)
+                    .eq(VipUser::getUserId, userId)
+                    .last("limit 1"));
+            if (vipUser != null && vipUser.getExpiryTime().after(now)) {
+                data.setIsVip(true);
+                data.setVipExpiryTime(vipUser.getExpiryTime());
+            }
         });
         return GatewayResponse.SUCCESS.newBuilder().toResult(search);
     }
 
     /**
      * 查看排除绑定重复用户数 真实用户数
+     * 会员用户数
      */
-    @GetMapping("/get/real/userNum")
-    public Result<Integer> getRealUserNum() {
+    @GetMapping("/get/userNum")
+    public Result<UserNumJhView> getUserNum() {
+        UserNumJhView userNumJhView = new UserNumJhView();
         Integer unBindUserNum = userMapper.selectUnBindUserNum();
         Integer bindUserNum = userBindDetailMapper.selectCount(null);
-        return GatewayResponse.SUCCESS.newBuilder().toResult(unBindUserNum + bindUserNum);
+        userNumJhView.setRealUserNum(unBindUserNum + bindUserNum);
+        userNumJhView.setVipNum(vipUserMapper.selectCount(Wrappers.lambdaQuery(VipUser.class)
+                .gt(VipUser::getExpiryTime, DateTime.now())));
+        return GatewayResponse.SUCCESS.newBuilder().toResult(userNumJhView);
     }
 
     /**

+ 10 - 4
netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/CmsGoodsDonController.java

@@ -232,7 +232,7 @@ public class CmsGoodsDonController {
      * 获取所有平台以及其规格
      */
     @GetMapping("/get/all/goods/sku")
-    public Result<List<GoodsDetailView>> getAllGoodsDetail(Boolean isNoBuyout) {
+    public Result<List<GoodsDetailView>> getAllGoodsDetail(Boolean isNoBuyout, Boolean isMirror) {
         MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
         //过滤买断制 平台
         String conditionSql;
@@ -242,7 +242,11 @@ public class CmsGoodsDonController {
         } else {
             conditionSql = "(special_type is null or special_type != 'giftCard')";
         }
-        conditionSql += "  and exists (select id from goods_don_sku sku where sku.goods_id = g.id limit 1)";
+        String mirrorSql = StrUtil.EMPTY;
+        if (isMirror != null) {
+            mirrorSql += " and is_mirror = " + isMirror;
+        }
+        conditionSql += String.format("  and exists (select id from goods_don_sku sku where sku.goods_id = g.id %s limit 1)", mirrorSql);
 
         List<GoodsDetailView> goodsDons = beanSearcher.searchAll(GoodsDetailView.class, mapBuilder
                 .put("condition", conditionSql)
@@ -251,10 +255,12 @@ public class CmsGoodsDonController {
                 .field(GoodsDetailView::getDeleted, true)
                 .onlySelect(GoodsDetailView::getId, GoodsDetailView::getTitle)
                 .build());
-        goodsDons.forEach(e->{
+        goodsDons.forEach(e -> {
             List<GoodsDonSku> donSkus = skuMapper.selectList(Wrappers.lambdaQuery(GoodsDonSku.class)
                     .eq(GoodsDonSku::getGoodsId, e.getId())
-                    .eq(GoodsDonSku::getStatus, 1).select(GoodsDonSku::getId, GoodsDonSku::getSpecVal));
+                    .eq(isMirror != null, GoodsDonSku::getIsMirror, isMirror)
+                    .eq(GoodsDonSku::getStatus, 1)
+                    .select(GoodsDonSku::getId, GoodsDonSku::getSpecVal));
             e.setSkuList(donSkus);
         });
         return GatewayResponse.SUCCESS.newBuilder().toResult(goodsDons);

+ 1 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupRelationController.java

@@ -72,6 +72,7 @@ public class CmsGroupRelationController {
     @GetMapping("/get")
     public Result<List<GroupsRelationBackView>> get(){
         List<GroupsRelationBackView> search = beanSearcher.searchAll(GroupsRelationBackView.class, MapUtils.flatBuilder(request.getParameterMap())
+                .field(GroupsRelationBackView::getIsVip, Boolean.FALSE)
                 .field(GroupsRelationBackView::getStatus, List.of(GroupsRelation.Status.none.name(), GroupsRelation.Status.locking.name())).op(Operator.NotIn).build()
         );
         return GatewayResponse.SUCCESS.newBuilder().toResult(search);

+ 92 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/vip/CmsVipController.java

@@ -0,0 +1,92 @@
+package com.cyksj.web.controller.manage.vip;
+
+import cn.dev33.satoken.stp.StpUtil;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.channel.ChannelPopularizeMapper;
+import com.cyksj.model.entity.VipGoodsSku;
+import com.cyksj.model.request.OrderRefundReq;
+import com.cyksj.model.views.CmsVipOrderDonView;
+import com.cyksj.model.views.VipGoodsSkuView;
+import com.cyksj.service.mange.vip.CmsVipOrderService;
+import com.cyksj.service.mange.vip.VipCommonService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.SearchResult;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CmsVipController
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 15:31
+ */
+@RestController
+@RequestMapping("/manage/vip")
+@RequiredArgsConstructor
+@Slf4j
+public class CmsVipController {
+	private final BeanSearcher beanSearcher;
+
+	private final HttpServletRequest request;
+
+	private final ChannelPopularizeMapper channelPopularizeMapper;
+
+	private final CmsVipOrderService cmsVipOrderService;
+
+	private final VipCommonService vipCommonService;
+
+	/**
+	 * 会员订单列表
+	 */
+	@GetMapping("/get/orders")
+	public Result<SearchResult<CmsVipOrderDonView>> getOrders() {
+		SearchResult<CmsVipOrderDonView> search = beanSearcher.search(CmsVipOrderDonView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		search.getDataList().forEach(data->{
+			if (data.getPopularizeId() != null) {
+				Optional.ofNullable(channelPopularizeMapper.selectChannelByPid(data.getPopularizeId()))
+						.ifPresent(channelName -> data.setChannel(channelName));
+			}
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 会员订单退款
+	 */
+	@PostMapping("/refund/order")
+	public Result<String> refundOrders(@RequestBody @Validated OrderRefundReq refundReq) throws Exception {
+		long adminId = StpUtil.getLoginIdAsLong();
+		refundReq.setAdminId(adminId);
+		cmsVipOrderService.refundOrders(refundReq);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+
+	//==================================================会员配置======================================================================
+
+	/**
+	 * 会员规格配置列表
+	 */
+	@GetMapping("/get/skus")
+	public Result<List<VipGoodsSkuView>> getVipGoodsSku() {
+		List<VipGoodsSkuView> vipGoodsSkuViews = beanSearcher.searchAll(VipGoodsSkuView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(vipGoodsSkuViews);
+	}
+
+	/**
+	 * 设置会员规格
+	 */
+	@PostMapping("/post/skus")
+	public Result<String> setVipGoodsSku(@RequestBody List<VipGoodsSku> vipGoodsSkus) {
+		vipCommonService.setVipGoodsSku(vipGoodsSkus);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+}

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

@@ -157,7 +157,7 @@ public class OrderController {
         String address = StringUtil.getNewInternalAddressByIp(ip);
         //address.contains("柬埔寨") ||
         if (address != null && address.contains("缅甸")) {
-            throw BusinessRuntimeException.getInstance("用户系统异常");
+            throw BusinessRuntimeException.getInstance("用户异常");
         }
         payRequest.setIp(ip);
 

+ 13 - 0
netflix-web/src/main/java/com/cyksj/web/controller/user/UserController.java

@@ -161,6 +161,7 @@ public class UserController {
                 .field(RenewalView::getUserId, 0).op(Operator.GreaterThan)
                 .field(RenewalView::getUserId, userIdList).op(Operator.InList)
                 .field(RenewalView::getStatus, List.of("validity", "overdue", "outside")).op(Operator.InList)
+                .field(RenewalView::getIsVip, false)
                 .orderBy(RenewalView::getExpiryTime).asc()
                 .onlySelect(RenewalView::getExpiryTime)
                 .build());
@@ -659,4 +660,16 @@ public class UserController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(userCertInfoView);
     }
 
+    /**
+     * 用户同步功能信息
+     */
+    @GetMapping("/get/user/syncInfo")
+    public Result<UserSyncInfoView> getUserSyncInfo() {
+        long userId = StpUserUtil.getLoginIdAsLong();
+
+        UserSyncInfoView syncInfoView = userService.getUserSyncInfo(userId);
+
+        return GatewayResponse.SUCCESS.newBuilder().toResult(syncInfoView);
+    }
+
 }

+ 52 - 0
netflix-web/src/main/java/com/cyksj/web/controller/vip/VipController.java

@@ -0,0 +1,52 @@
+package com.cyksj.web.controller.vip;
+
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.response.VipUserPageResp;
+import com.cyksj.model.views.VipGoodsSkuFrontView;
+import com.cyksj.service.vip.VipFrontService;
+import com.cyksj.web.util.StpUserUtil;
+import com.ejlchina.searcher.BeanSearcher;
+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;
+
+/**
+ * server/银河会员
+ * 项目名: yhlxj2
+ * 文件名: VipController
+ * 创建者: JavaZou
+ * 创建时间:2025/5/14 17:52
+ */
+@RestController
+@RequestMapping("/applet/vip")
+@RequiredArgsConstructor
+@Slf4j
+public class VipController {
+
+	private final BeanSearcher beanSearcher;
+
+	private final HttpServletRequest request;
+
+	private final VipFrontService vipFrontService;
+
+	/**
+	 * 会员信息
+	 */
+	@GetMapping("/get/info")
+	public Result<VipUserPageResp> getVipInfo() {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		VipUserPageResp vipUserPageResp = new VipUserPageResp();
+		//填充会员信息
+		vipFrontService.fillVipInfo(userId, vipUserPageResp);
+		List<VipGoodsSkuFrontView> vipGoodsSkuFrontViews = beanSearcher.searchAll(VipGoodsSkuFrontView.class, MapUtils.flatBuilder(request.getParameterMap()).orderBy(VipGoodsSkuFrontView::getGoodsId).build());
+		vipUserPageResp.setSkus(vipGoodsSkuFrontViews);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(vipUserPageResp);
+	}
+}

+ 235 - 0
netflix-web/src/main/java/com/cyksj/web/controller/vip/VipUserOrderController.java

@@ -0,0 +1,235 @@
+package com.cyksj.web.controller.vip;
+
+import cn.hutool.extra.servlet.ServletUtil;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.dto.AliPayParams;
+import com.cyksj.model.entity.PaypalPayConfig;
+import com.cyksj.model.entity.VipOrderDon;
+import com.cyksj.model.request.AlipayOrderDonReq;
+import com.cyksj.model.request.VipOrderDonPayReq;
+import com.cyksj.model.response.AliPayTradeStatus;
+import com.cyksj.service.order.VipOrderDonService;
+import com.cyksj.service.paypal.PayPalService;
+import com.cyksj.service.paypal.PaypalPayConfigService;
+import com.cyksj.service.paypal.PaypalV2Service;
+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 com.paypal.api.payments.Links;
+import com.paypal.api.payments.Payment;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * server/银河会员/订单
+ * 项目名: yhlxj2
+ * 文件名: VipUserOrderController
+ * 创建者: JavaZou
+ * 创建时间:2025/5/13 11:08
+ */
+@RestController
+@RequestMapping("/applet/vip/orders")
+@RequiredArgsConstructor
+@Slf4j
+public class VipUserOrderController {
+	private final HttpServletRequest request;
+
+	private final VipOrderDonService vipOrderDonService;
+
+	private final PayPalService payPalService;
+
+	private final PaypalV2Service paypalV2Service;
+
+	private final PaypalPayConfigService paypalPayConfigService;
+
+	private final HttpServletResponse response;
+
+	private final UserBindRelationService userBindRelationService;
+
+	private final BeanSearcher beanSearcher;
+
+	/**
+	 * 会员预下单
+	 */
+	@PostMapping("/post/submit")
+	public Result<VipOrderDon> submit(@Validated @RequestBody VipOrderDonPayReq payRequest) throws Exception {
+		final String methodName = "吊起会员预支付";
+		Long userId = StpUserUtil.getUserIdAfterLogin();
+		payRequest.setUserId(userId);
+		log.info("{}[start] params:{}", methodName, payRequest);
+
+		String ip = ServletUtil.getClientIP(request);
+		String address = StringUtil.getNewInternalAddressByIp(ip);
+		//address.contains("柬埔寨") ||
+		if (address != null && address.contains("缅甸")) {
+			throw BusinessRuntimeException.getInstance("用户异常");
+		}
+		VipOrderDon orderDon = vipOrderDonService.submit(payRequest);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
+	}
+
+	/**
+	 * 续费会员预下单
+	 */
+	@PostMapping("/post/renew")
+	public Result<VipOrderDon> renew(@Validated @RequestBody VipOrderDonPayReq payRequest) throws Exception {
+		final String methodName = "吊起会员续费预支付";
+		Long userId = StpUserUtil.getUserIdAfterLogin();
+		log.info("{}[start] params:{}", methodName, payRequest);
+		payRequest.setUserId(userId);
+		VipOrderDon orderDon = vipOrderDonService.renew(payRequest);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
+	}
+
+	/**
+	 * 查询订单状态
+	 */
+	@GetMapping("/get/order/status/{orderId}")
+	public Result<VipOrderDon> getOrderStatus(@PathVariable Long orderId) {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		VipOrderDon order = beanSearcher.searchFirst(VipOrderDon.class, MapUtils.builder()
+				.field(VipOrderDon::getId, orderId)
+				.field(VipOrderDon::getUserId, userIdList).op(Operator.InList)
+				.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(order);
+	}
+
+
+
+	/**
+	 * 支付宝支付
+	 */
+	@PostMapping("/post/pay/zfb")
+	public Result<String> aliPay(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
+		AliPayParams aliPayParams = vipOrderDonService.aliPay(re);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
+	}
+
+	/**
+	 * 支付宝回调
+	 */
+	@PostMapping("/zfb/notify")
+	public String aliPayNotify(HttpServletRequest request) throws Exception {
+		////将异步通知中收到的所有参数都存放到map中
+		Map<String, String> params = convertRequestParamsToMap(request);
+		String tradeStatus = params.get("trade_status");
+		String orderNo = params.get("out_trade_no");
+		if (AliPayTradeStatus.TRADE_SUCCESS.getStatus().equals(tradeStatus) || AliPayTradeStatus.TRADE_FINISHED.getStatus().equals(tradeStatus)) {
+			log.info("支付宝商家订单号orderNo:{},status:{}开始回调", orderNo, tradeStatus);
+			vipOrderDonService.aliPayNotify(params);
+		} else {
+			log.error("支付宝回调 error 状态不符合 成功或完成 status:{} ,orderNo:{}", tradeStatus, orderNo);
+			return "fail";
+		}
+		return "success";
+	}
+
+	/**
+	 * 关闭订单
+	 */
+	@PutMapping("/close/{orderId}")
+	public Result closeOrderDon(@PathVariable Long orderId) throws Exception {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		vipOrderDonService.closeOrderDon(userId, orderId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+
+	/**
+	 * paypal支付
+	 */
+	@PostMapping("/paypal/pay/{orderId}")
+	public Result<String> paypalPay(@PathVariable Long orderId, String successUrl) throws Exception {
+		final String methodName = "paypal支付";
+		log.info("{}[start] orderId:{}",methodName,orderId);
+
+		Payment payment = payPalService.createVipPayment(orderId,successUrl);
+		String payUrl = "/";
+		log.info("paypal links:{}",payment.getLinks());
+		for(Links links : payment.getLinks()){
+			if("approval_url".equals(links.getRel())){
+				// 客户付款登陆地址
+				String payPalUrl = links.getHref();
+				log.info("PayPal调起支付成功[end]:{}", orderId);
+				payUrl = payPalUrl;
+			}
+		}
+
+		log.info("paypal支付成功[end]");
+
+		return GatewayResponse.SUCCESS.newBuilder().toResult(payUrl);
+	}
+
+	/**
+	 * paypal支付回调
+	 */
+	@RequestMapping(value = "/paypal/notify")
+	public void successPay(@RequestParam(value = "paymentId") String paymentId, @RequestParam(value = "PayerID") String PayerID) throws Exception {
+		PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
+		response.sendRedirect(payPalService.successVipPayment(paymentId,PayerID,paypalPayConfig));
+	}
+
+	/**
+	 * paypalV2支付
+	 */
+	@PostMapping("/paypalV2/pay/{orderId}")
+	public Result<String> paypalV2Pay(@PathVariable Long orderId,String successUrl) throws Exception {
+		final String methodName = "paypal支付";
+		log.info("{}[start] orderId:{}",methodName,orderId);
+
+		String payUrl = paypalV2Service.createVipPayment(orderId,successUrl);
+		log.info("paypal links:{}",payUrl);
+		log.info("paypal支付成功[end]");
+
+		return GatewayResponse.SUCCESS.newBuilder().toResult(payUrl);
+	}
+
+	/**
+	 * paypalV2支付回调
+	 */
+	@RequestMapping(value = "/paypalV2/notify")
+	public void successV2Pay(@RequestParam(value = "token") String token, @RequestParam(value = "PayerID") String PayerID) throws Exception {
+		PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
+		response.sendRedirect(paypalV2Service.successVipPayment(token,PayerID,paypalPayConfig));
+	}
+
+	/**
+	 * 获取支付宝回调参数
+	 */
+	private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
+		Map<String, String> retMap = new HashMap<>();
+		Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
+		for (Map.Entry<String, String[]> entry : entrySet) {
+			String name = entry.getKey();
+			String[] values = entry.getValue();
+			int valLen = values.length;
+			if (valLen == 1) {
+				retMap.put(name, values[0]);
+			} else if (valLen > 1) {
+				StringBuilder sb = new StringBuilder();
+				for (String val : values) {
+					sb.append(",").append(val);
+				}
+				retMap.put(name, sb.substring(1));
+			} else {
+				retMap.put(name, "");
+			}
+		}
+		return retMap;
+	}
+}