zoujiajian 11 月之前
父节点
当前提交
9f0a33a5a9

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

@@ -720,4 +720,9 @@ public interface Constant {
 
 	//连接超时时间 毫秒
 	int CONNECT_MILLISECONDS = 10000;
+
+	/**
+	 * claude code补充额度
+	 */
+	Integer CLAUDE_CODE_SUP_QUOTA = 10;
 }

+ 0 - 5
netflix-dao/src/main/java/com/cyksj/model/views/ClaudeCodeUserInfoView.java

@@ -54,9 +54,4 @@ public class ClaudeCodeUserInfoView {
 	 * cc最高约可使用次数
 	 */
 	private Integer highestUsage;
-
-	/**
-	 * codex info
-	 */
-	private CodexUserInfoView codexUserInfo;
 }

+ 15 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CodexUserInfoView.java

@@ -35,6 +35,21 @@ public class CodexUserInfoView {
      * 每月预算上限
      */
     private Integer openaiQuota;
+
+    private Date expiryTime;
+
+
+    private Long renewOrderId;
+
+    /**
+     * 续费升级skuId
+     */
+    private Long renewSkuId;
+
+    /**
+     * 续费升级过期时间
+     */
+    private Date renewExpiryTime;
     
     /**
      * 座位ID

+ 1 - 20
netflix-service/src/main/java/com/cyksj/service/claude/impl/ClaudeCodeServiceImpl.java

@@ -318,15 +318,6 @@ public class ClaudeCodeServiceImpl implements ClaudeCodeService {
 					codexUserMapper.delete(Wrappers.lambdaQuery(CodexUser.class).eq(CodexUser::getRelationId, claudeCodeUser.getRelationId()).in(CodexUser::getUserId, userIdList));
 				}
 			}
-			//查看是否有自购 codex
-			codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).gt(CodexUser::getExpiryTime, DateTime.now()).last("limit 1"));
-			if (codexUser != null) {
-				updateCodexUserIfTry(codexUser);
-				codexUserInfoView = CodexUserInfoView.builder().isPay(codexUser.getIsPay()).planName(codexUser.getPlanName()).openaiDailyLimit(codexUser.getOpenaiDailyLimit()).openaiQuota(codexUser.getOpenaiQuota()).relationId(codexUser.getRelationId()).build();
-				return ClaudeCodeUserInfoView.builder()
-						.codexUserInfo(codexUserInfoView)
-						.build();
-			}
 			return null;
 		}
 		GoodsDonSku sku = null;
@@ -389,15 +380,6 @@ public class ClaudeCodeServiceImpl implements ClaudeCodeService {
 				codexService.createOrUpdateUserPackage(codexPackageReq);
 			}
 		}
-		//不存在赠送  查看是否有自购
-		if (codexUser == null) {
-			codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).gt(CodexUser::getExpiryTime, DateTime.now()).last("limit 1"));
-		}
-		if (codexUser != null) {
-			updateCodexUserIfTry(codexUser);
-			codexUserInfoView = CodexUserInfoView.builder().isPay(codexUser.getIsPay()).planName(codexUser.getPlanName()).openaiDailyLimit(codexUser.getOpenaiDailyLimit()).openaiQuota(codexUser.getOpenaiQuota()).relationId(codexUser.getRelationId()).build();
-		}
-
 		return ClaudeCodeUserInfoView.builder()
 				.planName(claudeCodeUser.getPlanName())
 				.relationId(claudeCodeUser.getRelationId())
@@ -408,7 +390,6 @@ public class ClaudeCodeServiceImpl implements ClaudeCodeService {
 				.renewOrderId(claudeCodeUser.getRenewOrderId())
 				.renewExpiryTime(claudeCodeUser.getRenewExpiryTime())
 				.highestUsage(sku.getHighestUsage())
-				.codexUserInfo(codexUserInfoView)
 				.build();
 	}
 
@@ -486,7 +467,7 @@ public class ClaudeCodeServiceImpl implements ClaudeCodeService {
 
 	@Override
 	public Boolean delUserPackages(Long userId) {
-		String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/plan", userId);
+		String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/plan?service_type=claude", userId);
 		try {
 			ClaudeCodeResp claudeCodeResp = executeClaudeCodeDeletedApi(url);
 			if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {

+ 4 - 0
netflix-service/src/main/java/com/cyksj/service/codex/CodexService.java

@@ -69,4 +69,8 @@ public interface CodexService {
     CodexDeductResp getDeductMoney(Long userId, Long relationId);
 
     OrderDon deductSubmitOrder(CodexUpgradePayReq payReq) throws Exception;
+
+    Boolean getUserSubs(Long userId);
+
+    CodexUserInfoView getUserSubsInfo(long userId);
 }

+ 145 - 4
netflix-service/src/main/java/com/cyksj/service/codex/impl/CodexServiceImpl.java

@@ -6,6 +6,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpException;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.http.Method;
@@ -44,10 +45,7 @@ import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapBuilder;
 import com.ejlchina.searcher.util.MapUtils;
-import com.github.rholder.retry.Retryer;
-import com.github.rholder.retry.RetryerBuilder;
-import com.github.rholder.retry.StopStrategies;
-import com.github.rholder.retry.WaitStrategies;
+import com.github.rholder.retry.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.dao.DuplicateKeyException;
@@ -357,6 +355,110 @@ public class CodexServiceImpl implements CodexService {
 		return orderDon;
 	}
 
+	@Override
+	public Boolean getUserSubs(Long userId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		//是否有该车票
+		GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
+				.field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
+				.field(GroupsRelationView::getGoodsId, Constant.CODEX_GOODS_ID)
+				.field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
+				.build());
+		CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
+		//车票不存在
+		if (groupsRelationView == null) {
+			//套餐是否存在
+			if (codexUser != null) {
+				//清除用户套餐
+				if (delUserPackages(codexUser.getUserId())) {
+					codexUserMapper.deleteById(codexUser.getId());
+				}
+			}
+			return false;
+		}
+		return true;
+	}
+
+
+	@Override
+	public CodexUserInfoView getUserSubsInfo(long userId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		//是否有该车票
+		GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
+				.field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
+				.field(GroupsRelationView::getGoodsId, Constant.CODEX_GOODS_ID)
+				.field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
+				.build());
+		CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
+		//车票不存在
+		if (groupsRelationView == null) {
+			//套餐是否存在
+			if (codexUser != null) {
+				//清除用户套餐
+				if (delUserPackages(codexUser.getUserId())) {
+					codexUserMapper.deleteById(codexUser.getId());
+				}
+			}
+			return null;
+		}
+
+		GoodsDonSku sku = null;
+		if (codexUser == null) {
+			OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+					.eq(OrderDon::getGoodsId, Constant.CODEX_GOODS_ID)
+					.eq(OrderDon::getRelationId, groupsRelationView.getId())
+					.in(OrderDon::getUserId, userIdList)
+					.notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
+					.orderByDesc(OrderDon::getId)
+					.last("limit 1"));
+			if (orderDon != null) {
+				sku = skuMapper.selectById(orderDon.getSkuId());
+				createOrUpdateCodexUserInfo(orderDon.getId(), orderDon.getRelationId(), orderDon.getUserId(), sku, orderDon.getOrderType());
+			} else {
+				//试用车票
+				Long skuId = groupsRelationView.getSkuId();
+				sku = skuMapper.selectById(skuId);
+				createOrUpdateCodexUserInfo(null, groupsRelationView.getId(), groupsRelationView.getUserId(), sku, null);
+			}
+			codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
+		} else {
+			codexUser.setExpiryTime(groupsRelationView.getExpiryTime());
+			codexUserMapper.updateById(codexUser);
+		}
+		//续费升级
+		if (codexUser.getRenewSkuId() != null) {
+			sku = skuMapper.selectById(codexUser.getRenewSkuId());
+		}
+		if (sku == null) {
+			sku = skuMapper.selectById(groupsRelationView.getSkuId());
+		}
+		//是否需要重试更新 cladueCode userInfo信息
+		if (codexUser.getIsRetry()) {
+			try {
+				ClaudeCodeResp codeUserPackage = createOrUpdateCodexUserPackageApi(codexUser.getUserId(), codexUser.getPlanName(), codexUser.getOpenaiDailyLimit(), codexUser.getOpenaiQuota(), codexUser.getExpiryTime());
+				if (!Constant.SUCCESS.equals(codeUserPackage.getMessage())) {
+					codexUser.setIsRetry(Boolean.TRUE);
+					codexUserMapper.updateById(codexUser);
+				} else {
+					codexUser.setIsRetry(Boolean.FALSE);
+					codexUserMapper.updateById(codexUser);
+				}
+			} catch (Exception e) {
+
+			}
+		}
+		return CodexUserInfoView.builder()
+				.planName(codexUser.getPlanName())
+				.relationId(codexUser.getRelationId())
+				.openaiDailyLimit(codexUser.getOpenaiDailyLimit())
+				.openaiQuota(codexUser.getOpenaiQuota())
+				.expiryTime(codexUser.getExpiryTime())
+				.renewSkuId(codexUser.getRenewSkuId())
+				.renewOrderId(codexUser.getRenewOrderId())
+				.renewExpiryTime(codexUser.getRenewExpiryTime())
+				.build();
+	}
+
 	private GroupsRelation getClaudeCodeRelation(Long userId, GoodsDonSku sku) {
 		//获取车票车位
 		GroupsRelation relation = getRelation(sku);
@@ -1167,4 +1269,43 @@ public class CodexServiceImpl implements CodexService {
 			orderDon.setCouponId(couponUser.getCouponId());
 		}
 	}
+
+	private boolean delUserPackages(Long userId) {
+		String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/plan?service_type=openai", userId);
+		try {
+			ClaudeCodeResp claudeCodeResp = executeClaudeCodeDeletedApi(url);
+			if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {
+				log.error("删除用户:{} claude code套餐失败:{}", userId, claudeCodeResp.getMessage());
+				return Boolean.FALSE;
+			}
+			return Boolean.TRUE;
+		} catch (Exception e) {
+			log.error("删除用户:{} claude code套餐报错:{}", userId, StringUtil.getErrorText(e));
+		}
+		return Boolean.FALSE;
+	}
+
+	/**
+	 * claude code DELETE请求
+	 */
+	public ClaudeCodeResp executeClaudeCodeDeletedApi(String url) {
+		Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
+		try {
+			return build.call(() -> {
+				try {
+					HttpResponse execute = HttpUtil.createRequest(Method.DELETE, url).header(Constant.CLAUDE_CODE_HEARD_KEY, Constant.CLAUDE_CODE_API_ADMIN_KEY).setConnectionTimeout(Constant.CONNECT_MILLISECONDS).execute();
+					ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class);
+					if (!Constant.SUCCESS.equals(resp.getMessage())) {
+						log.error("claude code DELETE URL:{}接口返回msg:{}", url, resp.getMessage());
+					}
+					return resp;
+				} catch (HttpException e) {
+					return null;
+				}
+			});
+		} catch (ExecutionException | RetryException e) {
+			log.error("重试调用claude code DELETE请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
+			throw BusinessRuntimeException.getInstance("接口请求失败");
+		}
+	}
 }

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/distribute/impl/DistributeServiceImpl.java

@@ -348,7 +348,7 @@ public class DistributeServiceImpl implements DistributeService {
 			userDistributeSharedMapper.insert(userDistributeShared);
 			if (Constant.CC_DISTRIBUTE_MARK.equals(userDistributeShared.getRemark())) {
 				//若是cc普通推广 发送claude code积分卡
-				claudeCodeService.sendCreditCard(null, userDistributeShared.getSharedId(), 1000, Constant.CLAUDE_CODE_CARD_NUMBER + System.currentTimeMillis(), "新用户注册");
+				claudeCodeService.sendCreditCard(null, userDistributeShared.getSharedId(), Constant.CLAUDE_CODE_SUP_QUOTA, Constant.CLAUDE_CODE_CARD_NUMBER + System.currentTimeMillis(), "新用户注册");
 				//存在Claude code车票不发送体验卡
 				if (!claudeCodeService.checkClaudeCodeUser(userId)) {
 					//发送claude code 体验卡

+ 22 - 3
netflix-web/src/main/java/com/cyksj/web/controller/codex/CodexController.java

@@ -17,9 +17,7 @@ import com.cyksj.model.request.CodexUpgradePayReq;
 import com.cyksj.model.request.UpdateDailyLimitRequest;
 import com.cyksj.model.response.CodexDeductResp;
 import com.cyksj.model.response.claudecode.ClaudeCodeResp;
-import com.cyksj.model.views.ClaudeCodePackageView;
-import com.cyksj.model.views.GoodsDonSkuView;
-import com.cyksj.model.views.RenewUpgradePackageFrontView;
+import com.cyksj.model.views.*;
 import com.cyksj.service.codex.CodexService;
 import com.cyksj.service.user.UserBindRelationService;
 import com.cyksj.web.util.StpUserUtil;
@@ -59,6 +57,27 @@ public class CodexController {
 
     private final BeanSearcher beanSearcher;
 
+
+    /**
+     * codex 用户是否存在订阅
+     */
+    @GetMapping("/get/user/subs")
+    public Result<Boolean> getUserSubs() {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        Boolean flag = codexService.getUserSubs(userId);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(flag);
+    }
+
+    /**
+     * codex 用户订阅车票信息
+     */
+    @GetMapping("/get/user/subs/info")
+    public Result<CodexUserInfoView> getUserSubsInfo() {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        CodexUserInfoView codexUserInfoView = codexService.getUserSubsInfo(userId);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(codexUserInfoView);
+    }
+
     /**
      * 获取用户OpenAI使用统计
      * period 时间周期(today/7d/30d)