浏览代码

GPT代充

zoujiajian 1 年之前
父节点
当前提交
027bcd15dd
共有 21 个文件被更改,包括 592 次插入36 次删除
  1. 5 0
      netflix-common/src/main/java/com/cyksj/common/constant/Constant.java
  2. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/GptRechargeCardKeyMapper.java
  3. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/GptUserRechargeNumRecordMapper.java
  4. 26 0
      netflix-dao/src/main/java/com/cyksj/model/entity/GptRechargeCardKey.java
  5. 30 0
      netflix-dao/src/main/java/com/cyksj/model/entity/GptUserRechargeNumRecord.java
  6. 22 0
      netflix-dao/src/main/java/com/cyksj/model/entity/GroupsRelation.java
  7. 25 0
      netflix-dao/src/main/java/com/cyksj/model/request/GroupsRelationRechargeReq.java
  8. 18 0
      netflix-dao/src/main/java/com/cyksj/model/request/RechargeRemainNumReq.java
  9. 12 0
      netflix-dao/src/main/java/com/cyksj/model/views/GroupsRelationRechargeView.java
  10. 12 0
      netflix-dao/src/main/java/com/cyksj/model/views/RenewalView.java
  11. 5 1
      netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml
  12. 3 1
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  13. 3 0
      netflix-service/src/main/java/com/cyksj/server/recharge/GptProxyRechargeService.java
  14. 201 4
      netflix-service/src/main/java/com/cyksj/server/recharge/impl/GptProxyRechargeServiceImpl.java
  15. 12 0
      netflix-service/src/main/java/com/cyksj/service/chatgpt/GptUserRechargeRecordService.java
  16. 32 0
      netflix-service/src/main/java/com/cyksj/service/chatgpt/impl/GptUserRechargeRecordServiceImpl.java
  17. 6 0
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  18. 1 1
      netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java
  19. 23 1
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java
  20. 109 2
      netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupRelationController.java
  21. 21 26
      netflix-web/src/main/java/com/cyksj/web/controller/recharge/GptRechargeController.java

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

@@ -626,4 +626,9 @@ public interface Constant {
 	 * 银河币消费支付抵扣key
 	 */
 	String GALAXY_COIN_PAY_DEDUCT_KEY = "galaxy_coin_pay_deduct_key";
+
+	/**
+	 * GPT代充平台id
+	 */
+	Long GPT_RECHARGE_GOODS_ID = 40L;
 }

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.GptRechargeCardKey;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: GptRechargeCardKeyMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/7/31 14:19
+ */
+public interface GptRechargeCardKeyMapper extends BaseMapper<GptRechargeCardKey> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.GptUserRechargeNumRecord;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: GptUserRechargeNumRecordMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/8/1 10:37
+ */
+public interface GptUserRechargeNumRecordMapper extends BaseMapper<GptUserRechargeNumRecord> {
+}

+ 26 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/GptRechargeCardKey.java

@@ -0,0 +1,26 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: GptRechargeCardKey
+ * 创建者: JavaZou
+ * 创建时间:2025/7/31 14:18
+ */
+@Getter
+@Setter
+public class GptRechargeCardKey extends BaseEntity{
+	private String cardKey;
+
+	private Boolean status;
+
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private Long orderId;
+
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String orderNo;
+}

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

@@ -0,0 +1,30 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: GptUserRechargeNumRecord
+ * 创建者: JavaZou
+ * 创建时间:2025/8/1 10:35
+ */
+@Getter
+@Setter
+public class GptUserRechargeNumRecord extends BaseEntity{
+	private Long userId;
+
+	private Long relationId;
+
+	/**
+	 * 代充账号
+	 */
+	private String account;
+
+	private String operator;
+
+	/**
+	 * 次数 正为扣除 负为增加
+	 */
+	private Integer num;
+}

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

@@ -44,6 +44,26 @@ public class GroupsRelation extends BaseEntity {
      */
     private String password;
 
+    /***
+     * 代充次数
+     */
+    private Integer rechargeNum;
+
+    /**
+     * 代充剩余次数
+     */
+    private Integer rechargeRemainNum;
+
+    /**
+     * gpt卡密
+     */
+    private String cardKey;
+
+    /**
+     * gpt自动充值任务id
+     */
+    private String gptTaskId;
+
     private Date submitTime;
 
     private Date sendTime;
@@ -155,6 +175,8 @@ public class GroupsRelation extends BaseEntity {
         waiting("待充值"),
         complete("已完成"),
         error("账号错误"),
+        recharge_error("充值错误"),
+        recharging("充值中"),
         ;
 
         private String desc;

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

@@ -0,0 +1,25 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: GroupsRelationRechargeReq
+ * 创建者: JavaZou
+ * 创建时间:2025/7/31 15:45
+ */
+@Getter
+@Setter
+public class GroupsRelationRechargeReq {
+	@NotNull
+	private Long relationId;
+
+	private Long userId;
+
+	@NotEmpty
+	private String accessToken;
+}

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/model/request/RechargeRemainNumReq.java

@@ -0,0 +1,18 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: RechargeRemainNumReq
+ * 创建者: JavaZou
+ * 创建时间:2025/7/31 13:44
+ */
+@Getter
+@Setter
+public class RechargeRemainNumReq {
+	private Long relationId;
+
+	private Integer num;
+}

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

@@ -58,6 +58,18 @@ public class GroupsRelationRechargeView {
 	@DbField("gr.recharge_status")
 	private GroupsRelation.RechargeStatus rechargeStatus;
 
+	/***
+	 * 代充次数
+	 */
+	@DbField("gr.recharge_num")
+	private Integer rechargeNum;
+
+	/**
+	 * 代充剩余次数
+	 */
+	@DbField("gr.recharge_remain_num")
+	private Integer rechargeRemainNum;
+
 	/**
 	 * 家庭组邀请链接
 	 */

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

@@ -171,9 +171,21 @@ public class RenewalView {
     @DbField("gr.password")
     private String grPassword;
 
+    /**
+     * 代充剩余次数
+     */
+    @DbField("gr.recharge_remain_num")
+    private Integer rechargeRemainNum;
+
     @DbField("gr.recharge_status")
     private GroupsRelation.RechargeStatus rechargeStatus;
 
+    /**
+     * 代充任务id
+     */
+    @DbField("gr.gpt_task_id")
+    private String gptTaskId;
+
     /**
      * 家庭组邀请链接
      */

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

@@ -22,7 +22,11 @@
         aq_type = 1,
         invite_link = null,
         contact = null,
-        is_vip = 0
+        is_vip = 0,
+        recharge_num = null,
+        recharge_remain_num = null,
+        card_key = null,
+        gpt_task_id = null
         where id = #{entity.id}
         <if test="expireTime != null">
             and expiry_time = #{expireTime}

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

@@ -856,7 +856,9 @@ public class RedisService {
         NETFLIX_CHANGE_TICKET_KEY("netflix_change_ticket_key:", "奈飞更换车票key", 60l),
         CORP_KF_ACCOUNT_URL("corp_kf_account_url:", "微信客服url:", 60 * 60 * 6),
         CORP_WX_YL_KEY("corp_wx_yl_key:", "企业微信引流", 60 * 30),
-        TG_MINI_USER_SAHRE_COUNT_KEY("mini_popualrize_user_sahre_count_key:", "推广小程序用户分享次数key", 60 * 60 * 24l)
+        TG_MINI_USER_SAHRE_COUNT_KEY("mini_popualrize_user_sahre_count_key:", "推广小程序用户分享次数key", 60 * 60 * 24l),
+        GPT_RECHARGE_KEY("gpt_recharge_key:", "gpt代充", 60),
+        GPT_RECHARGE_RECOVER_KEY("gpt_recharge_recover_key:", "gpt代充恢复key", 60 * 5l),
         ;
 
         private String name;

+ 3 - 0
netflix-service/src/main/java/com/cyksj/server/recharge/GptProxyRechargeService.java

@@ -1,5 +1,6 @@
 package com.cyksj.server.recharge;
 
+import com.cyksj.model.request.GroupsRelationRechargeReq;
 import com.cyksj.server.recharge.dto.*;
 
 /**
@@ -27,4 +28,6 @@ public interface GptProxyRechargeService {
      * 获取任务结果
      */
     TaskResult getTaskResult(String taskId);
+
+    String confirmRecharge(GroupsRelationRechargeReq req);
 }

+ 201 - 4
netflix-service/src/main/java/com/cyksj/server/recharge/impl/GptProxyRechargeServiceImpl.java

@@ -4,10 +4,27 @@ import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.constant.Constant;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.mapper.*;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.request.GroupsRelationRechargeReq;
+import com.cyksj.redis.RedisService;
 import com.cyksj.server.recharge.GptProxyRechargeService;
-import com.cyksj.server.recharge.dto.*;
+import com.cyksj.server.recharge.dto.CardKeyValidationResult;
+import com.cyksj.server.recharge.dto.TaskResult;
+import com.cyksj.server.recharge.dto.TaskSubmitResult;
+import com.cyksj.server.recharge.dto.TokenParseResult;
+import com.cyksj.service.chatgpt.GptUserRechargeRecordService;
+import com.cyksj.service.user.UserBindRelationService;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 /**
  * @author chan
@@ -15,11 +32,30 @@ import org.springframework.stereotype.Service;
  */
 @Slf4j
 @Service
+@RequiredArgsConstructor
 public class GptProxyRechargeServiceImpl implements GptProxyRechargeService {
 
     private static final String BASE_URL = "https://api.ow520.com/api";
 
-    private HttpRequest createBaseRequest(String uri) {
+    private final GptRechargeCardKeyMapper gptRechargeCardKeyMapper;
+
+    private final GroupsRelationMapper relationMapper;
+
+    private final UserBindRelationService userBindRelationService;
+
+    private final GroupsMapper groupsMapper;
+
+    private final GoodsDonSkuMapper skuMapper;
+
+    private final OrderDonMapper orderDonMapper;
+
+	private final GptUserRechargeRecordService gptUserRechargeRecordService;
+
+	private final RedisService redisService;
+
+	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+	private HttpRequest createBaseRequest(String uri) {
         return HttpRequest.get(BASE_URL + uri)
                 .header("accept", "application/json, text/plain, */*")
                 .header("accept-language", "zh-CN,zh;q=0.9,en;q=0.8")
@@ -123,17 +159,178 @@ public class GptProxyRechargeServiceImpl implements GptProxyRechargeService {
                     .execute();
             String body = response.body();
             log.info("任务查询响应: {}", body);
-            
+
             JSONObject jsonResponse = JSONUtil.parseObj(body);
             TaskResult result = new TaskResult(
                 jsonResponse.getStr("status"),
                 jsonResponse.getStr("result")
             );
             log.info("任务查询结果: {}", result);
-            return result;
+	        String status = result.getStatus();
+			//更新代充状态
+	        resetGroupsRelationRechargeStatus(taskId, status);
+	        return result;
         } catch (Exception e) {
             log.error("获取任务结果失败: {}", e.getMessage(), e);
             throw new RuntimeException("获取任务结果失败: " + e.getMessage(), e);
         }
     }
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public String confirmRecharge(GroupsRelationRechargeReq req) {
+		Long relationId = req.getRelationId();
+		Long userId = req.getUserId();
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		GroupsRelation relation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+				.in(GroupsRelation::getUserId, userIdList)
+				.eq(GroupsRelation::getId, relationId)
+				.eq(GroupsRelation::getStatus, GroupsRelation.Status.validity));
+		if (relation == null) {
+			throw BusinessRuntimeException.getInstance("车票不存在");
+		}
+		GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
+		GoodsDonSku sku = skuMapper.selectById(groupsTrips.getSkuId());
+		//GPT 代充
+		if (sku.getGoodsId() != Constant.GPT_RECHARGE_GOODS_ID) {
+			throw BusinessRuntimeException.getInstance("车票类型错误");
+		}
+		Integer rechargeRemainNum = relation.getRechargeRemainNum();
+		if (rechargeRemainNum == null || rechargeRemainNum <= 0) {
+			throw BusinessRuntimeException.getInstance("剩余可代充次数为0");
+		}
+		if (relation.getRechargeStatus() == GroupsRelation.RechargeStatus.recharging) {
+			throw BusinessRuntimeException.getInstance("确认充值中");
+		}
+		if (relation.getRechargeStatus() == GroupsRelation.RechargeStatus.recharge_error) {
+			throw BusinessRuntimeException.getInstance("充值失败,请联系客服开通");
+		}
+		String key = RedisService.key.GPT_RECHARGE_KEY.getName() + relationId;
+		boolean b = redisService.setNx(key, relationId, RedisService.key.GPT_RECHARGE_KEY.getTimeout());
+		if (!b) {
+			throw BusinessRuntimeException.getInstance("确认充值中");
+		}
+		try {
+			int updateRemainNum = relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+					.set(GroupsRelation::getRechargeRemainNum, rechargeRemainNum - 1)
+					.eq(GroupsRelation::getId, relationId)
+					.eq(GroupsRelation::getUserId, relation.getUserId())
+					.eq(GroupsRelation::getRechargeRemainNum, rechargeRemainNum));
+			if (updateRemainNum == 0) {
+				throw BusinessRuntimeException.getInstance("代充异常,请重试");
+			}
+			OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+					.in(OrderDon::getUserId, userIdList)
+					.eq(OrderDon::getRelationId, relationId)
+					.notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
+					.orderByDesc(OrderDon::getId)
+					.last("limit 1"));
+			if (orderDon == null) {
+				throw BusinessRuntimeException.getInstance("代充订单不存在或已退款");
+			}
+			String accessToken = req.getAccessToken();
+			//验证用户token是否正确
+			TokenParseResult tokenParseResult = parseToken(accessToken);
+			if (!tokenParseResult.getSuccess()) {
+				throw BusinessRuntimeException.getInstance("您输入的凭证有误,请重新输入");
+			}
+			String rechargeAccount = tokenParseResult.getMessage();
+			//记录代充账号 token
+			relation.setAccount(rechargeAccount);
+			//获取GPT代充key
+			GptRechargeCardKey gptRechargeCardKey = gptRechargeCardKeyMapper.selectOne(Wrappers.lambdaQuery(GptRechargeCardKey.class)
+					.eq(GptRechargeCardKey::getStatus, Boolean.FALSE)
+					.last("order by rand() limit 1"));
+			if (gptRechargeCardKey == null) {
+				setRechargeRelationStatusAutoError(relation);
+				throw BusinessRuntimeException.getInstance("充值失败,请联系客服开通");
+			}
+			int update = gptRechargeCardKeyMapper.update(null, Wrappers.lambdaUpdate(GptRechargeCardKey.class)
+					.set(GptRechargeCardKey::getStatus, Boolean.TRUE)
+					.set(GptRechargeCardKey::getOrderId, orderDon.getId())
+					.set(GptRechargeCardKey::getOrderNo, orderDon.getOrderNo())
+					.eq(GptRechargeCardKey::getId, gptRechargeCardKey.getId())
+					.eq(GptRechargeCardKey::getStatus, Boolean.FALSE));
+			if (update == 0) {
+				setRechargeRelationStatusAutoError(relation);
+				throw BusinessRuntimeException.getInstance("充值失败,请联系客服开通");
+			}
+			String gptCardKey = gptRechargeCardKey.getCardKey();
+			//校验卡密是否可用
+			validateCardKey(gptCardKey);
+			//提交任务
+			TaskSubmitResult result = submitTask(gptCardKey, accessToken, "auth0");
+			if (!result.getSuccess()) {
+				setRechargeRelationStatusAutoError(relation);
+				throw BusinessRuntimeException.getInstance("充值失败,请联系客服开通");
+			}
+			String taskId = result.getTaskId();
+			relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+					//记录代充账号
+					.set(GroupsRelation::getAccount, rechargeAccount)
+					.set(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharging)
+					.set(GroupsRelation::getCardKey, gptCardKey)
+					.set(GroupsRelation::getGptTaskId, taskId)
+					.eq(GroupsRelation::getId, relationId));
+			return taskId;
+		} finally {
+			if (redisService.hasKey(key)) {
+				redisService.del(key);
+			}
+		}
+	}
+
+	/**
+	 * 设置代充自动充值异常
+	 */
+	public void setRechargeRelationStatusAutoError(GroupsRelation relation) {
+		TASK_EXECUTOR.execute(()->{
+			relation.setRechargeStatus(GroupsRelation.RechargeStatus.recharge_error);
+			relationMapper.updateById(relation);
+		});
+	}
+
+	/**
+	 * 更新代充状态
+	 */
+	private void resetGroupsRelationRechargeStatus(String taskId, String status) {
+		if ("completed".equals(status) || "failed".equals(status)) {
+			GroupsRelation relation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+					.eq(GroupsRelation::getGptTaskId, taskId)
+					.last("limit 1"));
+			if (relation != null && relation.getRechargeStatus() == GroupsRelation.RechargeStatus.recharging) {
+				Integer rechargeRemainNum = relation.getRechargeRemainNum();
+				GroupsRelation.RechargeStatus rechargeStatus = "completed".equals(status) ? GroupsRelation.RechargeStatus.complete : GroupsRelation.RechargeStatus.recharge_error;
+				if (rechargeStatus == GroupsRelation.RechargeStatus.recharge_error) {
+					String cardKey = relation.getCardKey();
+					GptRechargeCardKey gptRechargeCardKey = gptRechargeCardKeyMapper.selectOne(Wrappers.lambdaQuery(GptRechargeCardKey.class)
+							.eq(GptRechargeCardKey::getCardKey, cardKey)
+							.last("limit 1"));
+					if (gptRechargeCardKey != null) {
+						gptRechargeCardKeyMapper.update(null, Wrappers.lambdaUpdate(GptRechargeCardKey.class)
+								.set(GptRechargeCardKey::getStatus, Boolean.FALSE)
+								.set(GptRechargeCardKey::getOrderId, null)
+								.set(GptRechargeCardKey::getOrderNo, null)
+								.eq(GptRechargeCardKey::getId, gptRechargeCardKey.getId())
+								.eq(GptRechargeCardKey::getStatus, Boolean.TRUE));
+					}
+				}
+				if (rechargeStatus == GroupsRelation.RechargeStatus.recharge_error) {
+					if (!redisService.setNx(RedisService.key.GPT_RECHARGE_RECOVER_KEY.getName() + taskId, taskId, RedisService.key.GPT_RECHARGE_RECOVER_KEY.getTimeout())) {
+						return;
+					}
+				}
+				relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+						.set(rechargeStatus == GroupsRelation.RechargeStatus.recharge_error, GroupsRelation::getRechargeRemainNum, rechargeRemainNum + 1)
+						.set(GroupsRelation::getRechargeStatus, rechargeStatus)
+						.eq(GroupsRelation::getId, relation.getId())
+						.le(GroupsRelation::getRechargeRemainNum, relation.getRechargeNum())
+						.eq(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharging));
+				//记录代充记录
+				if (rechargeStatus == GroupsRelation.RechargeStatus.complete) {
+					gptUserRechargeRecordService.recordGptUserRechargeNum(relation.getUserId(), relation.getId(), relation.getAccount(), 1, null);
+				}
+			}
+		}
+	}
 }

+ 12 - 0
netflix-service/src/main/java/com/cyksj/service/chatgpt/GptUserRechargeRecordService.java

@@ -0,0 +1,12 @@
+package com.cyksj.service.chatgpt;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: GptUserRechargeRecordService
+ * 创建者: JavaZou
+ * 创建时间:2025/8/1 10:49
+ */
+public interface GptUserRechargeRecordService {
+
+	void recordGptUserRechargeNum(Long userId, Long relationId, String account, Integer num, String operator);
+}

+ 32 - 0
netflix-service/src/main/java/com/cyksj/service/chatgpt/impl/GptUserRechargeRecordServiceImpl.java

@@ -0,0 +1,32 @@
+package com.cyksj.service.chatgpt.impl;
+
+import com.cyksj.mapper.GptUserRechargeNumRecordMapper;
+import com.cyksj.model.entity.GptUserRechargeNumRecord;
+import com.cyksj.service.chatgpt.GptUserRechargeRecordService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: GptUserRechargeRecordServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2025/8/1 10:50
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class GptUserRechargeRecordServiceImpl implements GptUserRechargeRecordService {
+	private final GptUserRechargeNumRecordMapper gptUserRechargeNumRecordMapper;
+	@Override
+	public void recordGptUserRechargeNum(Long userId, Long relationId, String account, Integer num, String operator) {
+		//记录代充记录
+		GptUserRechargeNumRecord gptUserRechargeNumRecord = new GptUserRechargeNumRecord();
+		gptUserRechargeNumRecord.setUserId(userId);
+		gptUserRechargeNumRecord.setRelationId(relationId);
+		gptUserRechargeNumRecord.setAccount(account);
+		gptUserRechargeNumRecord.setNum(num);
+		gptUserRechargeNumRecord.setOperator(operator);
+		gptUserRechargeNumRecordMapper.insert(gptUserRechargeNumRecord);
+	}
+}

+ 6 - 0
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -1744,6 +1744,12 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				if ((goodsDon.getSpecialType() != null && goodsDon.getSpecialType() == GoodsDon.SpecialType.recharge) || Constant.RECHARGE_INVITE_GOODS_IDS.contains(order.getGoodsId())) {
 					relation.setRechargeStatus(GroupsRelation.RechargeStatus.waiting);
 					relation.setSubmitTime(DateTime.now());
+					//GPT代充
+					if (goodsDon.getId() == Constant.GPT_RECHARGE_GOODS_ID) {
+						//代充次数
+						relation.setRechargeNum(sku.getMonths());
+						relation.setRechargeRemainNum(relation.getRechargeNum());
+					}
 				}
 				GroupsTrips.Status tripsStatus = groupsTrips.getStatus();
 				//邀请制 不给车票设置时间

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

@@ -206,8 +206,8 @@ public class GroupRelationController {
             if (re.getRechargeStatus() == GroupsRelation.RechargeStatus.error) {
                 updateWrapper.set(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.waiting);
             }
+            relationMapper.update(relation, updateWrapper);
         }
-        relationMapper.update(relation, updateWrapper);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
 

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

@@ -40,6 +40,7 @@ import com.cyksj.model.manage.views.OrderDonBackView;
 import com.cyksj.model.manage.views.UpgradeOrderDonBackView;
 import com.cyksj.model.request.*;
 import com.cyksj.model.views.*;
+import com.cyksj.service.chatgpt.GptUserRechargeRecordService;
 import com.cyksj.service.mange.CmsOrderDonService;
 import com.cyksj.service.order.OrderDonCommentService;
 import com.cyksj.service.user.UserBindRelationService;
@@ -55,6 +56,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.dao.DuplicateKeyException;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -135,6 +137,8 @@ public class CmsOrderController {
 
 	private final OrderDonGalaxyCoinRecordMapper orderDonGalaxyCoinRecordMapper;
 
+	private final GptUserRechargeRecordService gptUserRechargeRecordService;
+
 	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
 
 	@GetMapping("/get")
@@ -589,6 +593,7 @@ public class CmsOrderController {
 	 * 修改订单状态
 	 */
 	@PutMapping("/put/status/{orderId}")
+	@Transactional(rollbackFor = Throwable.class)
 	public Result<String> updateOrderStatus(@PathVariable Long orderId) {
 		long adminId = StpUtil.getLoginIdAsLong();
 		CmsUser cmsUser = cmsUserMapper.selectById(adminId);
@@ -610,7 +615,7 @@ public class CmsOrderController {
 				.eq(GroupsRelation::getUserId, orderDon.getUserId())
 				.last("limit 1"))).ifPresent(relation->{
 			GroupsRelation.RechargeStatus rechargeStatus = relation.getRechargeStatus();
-			if (rechargeStatus != null && rechargeStatus == GroupsRelation.RechargeStatus.waiting) {
+			if (rechargeStatus != null && (rechargeStatus == GroupsRelation.RechargeStatus.waiting || rechargeStatus == GroupsRelation.RechargeStatus.recharge_error)) {
 				relation.setRechargeStatus(GroupsRelation.RechargeStatus.complete);
 				if (cmsUser != null) {
 					relation.setOperator(cmsUser.getNickname());
@@ -626,6 +631,23 @@ public class CmsOrderController {
 				}
 				relation.setExpiryTime(expiryTime);
 				relationMapper.updateById(relation);
+
+				//GPT代充
+				if (orderDon.getGoodsId() == Constant.GPT_RECHARGE_GOODS_ID) {
+					//扣除次数
+					Integer rechargeRemainNum = relation.getRechargeRemainNum();
+					if (rechargeRemainNum != null && rechargeRemainNum > 0) {
+						int update = relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+								.set(GroupsRelation::getRechargeRemainNum, rechargeRemainNum - 1)
+								.eq(GroupsRelation::getId, relationId)
+								.eq(GroupsRelation::getRechargeRemainNum, rechargeRemainNum));
+						if (update == 0) {
+							throw BusinessRuntimeException.getInstance("更新用户代充剩余次数失败,请重试");
+						}
+						//记录gpt代充记录
+						gptUserRechargeRecordService.recordGptUserRechargeNum(relation.getUserId(), relation.getId(), relation.getAccount(), 1, cmsUser != null ? cmsUser.getNickname() : null);
+					}
+				}
 			}
 		});
 

+ 109 - 2
netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupRelationController.java

@@ -20,10 +20,12 @@ import com.cyksj.mapper.manage.cms.CmsUserMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.AccountView;
 import com.cyksj.model.manage.views.GroupsRelationBackView;
+import com.cyksj.model.request.RechargeRemainNumReq;
 import com.cyksj.model.views.DisableAccountView;
 import com.cyksj.model.views.GroupsRelationIndependentView;
 import com.cyksj.model.views.GroupsRelationRechargeView;
 import com.cyksj.model.views.OrderDonRenewView;
+import com.cyksj.service.chatgpt.GptUserRechargeRecordService;
 import com.cyksj.service.relation.GroupRelationClearService;
 import com.cyksj.service.scheduler.SchedulerService;
 import com.ejlchina.searcher.BeanSearcher;
@@ -69,6 +71,10 @@ public class CmsGroupRelationController {
 
     private final GroupRelationClearService groupRelationClearService;
 
+    private final GptRechargeCardKeyMapper gptRechargeCardKeyMapper;
+
+    private final GptUserRechargeRecordService gptUserRechargeRecordService;
+
     @GetMapping("/get")
     public Result<List<GroupsRelationBackView>> get(){
         List<GroupsRelationBackView> search = beanSearcher.searchAll(GroupsRelationBackView.class, MapUtils.flatBuilder(request.getParameterMap())
@@ -148,16 +154,17 @@ public class CmsGroupRelationController {
         relation.setOperator(cmsUser.getNickname());
         //邀请制家庭组链接
         relation.setInviteLink(rechargeView.getInviteLink());
+        OrderDon orderDon = null;
         if (rechargeStatus == GroupsRelation.RechargeStatus.complete) {
             relation.setSendTime(DateTime.now());
-            OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+            orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
                     .eq(OrderDon::getUserId, relation.getUserId())
                     .eq(OrderDon::getRelationId, relationId)
                     .notIn(OrderDon::getStatus, Constant.noOrderStatus)
                     .orderByDesc(OrderDon::getId)
                     .last("limit 1"));
             if (orderDon == null) throw BusinessRuntimeException.getInstance("该代充订单不存在");
-            if (orderDon.getStatus() == OrderDon.Status.refund){
+            if (orderDon.getStatus() == OrderDon.Status.refund) {
                 throw BusinessRuntimeException.getInstance("该代充订单已退款");
             }
             orderDon.setStatus(OrderDon.Status.complete);
@@ -175,6 +182,22 @@ public class CmsGroupRelationController {
             relation.setExpiryTime(expiryTime);
         }
         groupsRelationMapper.updateById(relation);
+        //GPT代充
+        if (orderDon != null && orderDon.getGoodsId() == Constant.GPT_RECHARGE_GOODS_ID) {
+            //扣除次数
+            Integer rechargeRemainNum = relation.getRechargeRemainNum();
+            if (rechargeRemainNum != null && rechargeRemainNum > 0) {
+                int update = groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+                        .set(GroupsRelation::getRechargeRemainNum, rechargeRemainNum - 1)
+                        .eq(GroupsRelation::getId, relationId)
+                        .eq(GroupsRelation::getRechargeRemainNum, rechargeRemainNum));
+                if (update == 0) {
+                    throw BusinessRuntimeException.getInstance("更新用户代充剩余次数失败,请重试");
+                }
+                //记录gpt代充记录
+                gptUserRechargeRecordService.recordGptUserRechargeNum(relation.getUserId(), relation.getId(), relation.getAccount(), 1, cmsUser != null ? cmsUser.getNickname() : null);
+            }
+        }
         return GatewayResponse.SUCCESS.newBuilder().toResult("状态修改成功");
     }
 
@@ -193,6 +216,90 @@ public class CmsGroupRelationController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(false);
     }
 
+    /**
+     * GPT代充 设置剩余次数
+     */
+    @PutMapping("/recharge/update/remain/num")
+    public Result<String> updateRechargeRemainNum(@RequestBody RechargeRemainNumReq req) {
+        long adminId = StpUtil.getLoginIdAsLong();
+        Long relationId = req.getRelationId();
+        Integer num = req.getNum();
+        GroupsRelation relation = groupsRelationMapper.selectById(relationId);
+        if (relation.getRechargeNum() != null) {
+            Integer rechargeRemainNum = relation.getRechargeRemainNum();
+            if (rechargeRemainNum == 0 && num < 0) {
+                throw BusinessRuntimeException.getInstance("剩余代充次数为0");
+            }
+            Integer subAfterNum = rechargeRemainNum - num;
+            if (subAfterNum < 0) {
+                throw BusinessRuntimeException.getInstance("扣除次数不能为负数");
+            }
+            int update = groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+                    .set(GroupsRelation::getRechargeRemainNum, subAfterNum)
+                    .eq(GroupsRelation::getId, relationId)
+                    .eq(GroupsRelation::getRechargeRemainNum, rechargeRemainNum));
+            if (update == 0) {
+                throw BusinessRuntimeException.getInstance("修改用户代充次数错误,请重试");
+            }
+            //记录gpt代充记录
+            CmsUser cmsUser = cmsUserMapper.selectById(adminId);
+            gptUserRechargeRecordService.recordGptUserRechargeNum(relation.getUserId(), relation.getId(), relation.getAccount(), num, cmsUser != null ? cmsUser.getNickname() : null);
+        }
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
+    /**
+     * GPT代充卡密列表
+     */
+    @GetMapping("/get/gpt/cardKey")
+    public Result<SearchResult<GptRechargeCardKey>> getGptCardKey() {
+        SearchResult<GptRechargeCardKey> search = beanSearcher.search(GptRechargeCardKey.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+    }
+
+
+
+    /**
+     * 编辑GPT代充卡密
+     */
+    @PutMapping("/update/gpt/cardKey")
+    public Result<SearchResult<GptRechargeCardKey>> updateGptCardKey(@RequestBody GptRechargeCardKey cardKey) {
+        Long id = cardKey.getId();
+        GptRechargeCardKey gptRechargeCardKey = gptRechargeCardKeyMapper.selectById(id);
+        if (gptRechargeCardKey == null) {
+            throw BusinessRuntimeException.getInstance("卡密不存在");
+        }
+        gptRechargeCardKeyMapper.updateById(cardKey);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
+
+    /**
+     * 删除GPT代充卡密
+     */
+    @DeleteMapping("/del/gpt/cardKey/{id}")
+    public Result<SearchResult<GptRechargeCardKey>> deleteGptCardKeyById(@PathVariable Long id) {
+        GptRechargeCardKey gptRechargeCardKey = gptRechargeCardKeyMapper.selectById(id);
+        if (gptRechargeCardKey == null) {
+            throw BusinessRuntimeException.getInstance("卡密不存在");
+        }
+        if (gptRechargeCardKey.getStatus()) {
+            throw BusinessRuntimeException.getInstance("已使用的卡密不可删除");
+        }
+        gptRechargeCardKeyMapper.deleteById(id);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
+
+    /**
+     * 新增GPT代充卡密
+     */
+    @PostMapping("/post/gpt/cardKey")
+    public Result<String> postGptCardKey(@RequestBody GptRechargeCardKey gptRechargeCardKey) {
+        gptRechargeCardKeyMapper.insert(gptRechargeCardKey);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
     /**
      * Chat GPT独立规格续费 列表
      */

+ 21 - 26
netflix-web/src/main/java/com/cyksj/web/controller/recharge/GptRechargeController.java

@@ -1,60 +1,55 @@
 package com.cyksj.web.controller.recharge;
 
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.request.GroupsRelationRechargeReq;
 import com.cyksj.server.recharge.GptProxyRechargeService;
-import com.cyksj.server.recharge.dto.CardKeyValidationResult;
 import com.cyksj.server.recharge.dto.TaskResult;
-import com.cyksj.server.recharge.dto.TaskSubmitResult;
 import com.cyksj.server.recharge.dto.TokenParseResult;
 import com.cyksj.web.controller.recharge.req.ParseTokenRequest;
-import com.cyksj.web.controller.recharge.req.SubmitTaskRequest;
-import com.cyksj.web.controller.recharge.req.ValidateCardRequest;
+import com.cyksj.web.util.StpUserUtil;
 import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 /**
- * GPT代理充值控制器
+ * server/GPT代理充值控制器
  */
 @RestController
-@RequestMapping("/api/gpt-recharge")
+@RequestMapping("/gpt-recharge")
 @RequiredArgsConstructor
 @CrossOrigin(origins = "*")
 public class GptRechargeController {
 
     private final GptProxyRechargeService gptProxyRechargeService;
 
-    /**
-     * 验证卡密
-     */
-    @PostMapping("/validate-card")
-    public CardKeyValidationResult validateCard(@RequestBody ValidateCardRequest request) {
-        return gptProxyRechargeService.validateCardKey(request.getCardKey());
-    }
-
     /**
      * 验证凭证
      */
     @PostMapping("/parse-token")
-    public TokenParseResult parseToken(@RequestBody ParseTokenRequest request) {
-        return gptProxyRechargeService.parseToken(request.getAccessToken());
+    public Result<TokenParseResult> parseToken(@RequestBody ParseTokenRequest request) {
+        TokenParseResult tokenParseResult = gptProxyRechargeService.parseToken(request.getAccessToken());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(tokenParseResult);
     }
 
     /**
-     * 提交任务
+     * 用户确认充值
      */
-    @PostMapping("/submit-task")
-    public TaskSubmitResult submitTask(@RequestBody SubmitTaskRequest request) {
-        return gptProxyRechargeService.submitTask(
-            request.getCardKey(), 
-            request.getAccessToken(), 
-            request.getIdp()
-        );
+    @PostMapping("/confirm")
+    public Result<String> confirmRecharge(@RequestBody @Validated GroupsRelationRechargeReq req) {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        req.setUserId(userId);
+        String taskId = gptProxyRechargeService.confirmRecharge(req);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(taskId);
     }
 
+
     /**
      * 查询任务状态
      */
     @GetMapping("/task-status/{taskId}")
-    public TaskResult getTaskStatus(@PathVariable String taskId) {
-        return gptProxyRechargeService.getTaskResult(taskId);
+    public Result<TaskResult> getTaskStatus(@PathVariable String taskId) {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        return GatewayResponse.SUCCESS.newBuilder().toResult(gptProxyRechargeService.getTaskResult(taskId));
     }
 }