|
|
@@ -6,7 +6,6 @@ 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;
|
|
|
@@ -40,6 +39,7 @@ import com.cyksj.model.views.CodexUserInfoView;
|
|
|
import com.cyksj.model.views.CouponNewUserView;
|
|
|
import com.cyksj.model.views.CouponUserView;
|
|
|
import com.cyksj.redis.RedisService;
|
|
|
+import com.cyksj.service.api.ClaudeCodexApiService;
|
|
|
import com.cyksj.service.codex.CodexService;
|
|
|
import com.cyksj.service.coupon.CouponFontService;
|
|
|
import com.cyksj.service.groups.GroupsFuncService;
|
|
|
@@ -48,7 +48,10 @@ 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.*;
|
|
|
+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 lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.dao.DuplicateKeyException;
|
|
|
@@ -114,69 +117,8 @@ public class CodexServiceImpl implements CodexService {
|
|
|
private final CouponFontService couponFontService;
|
|
|
|
|
|
private static final Sequence SEQUENCE = new Sequence(0);
|
|
|
- @Override
|
|
|
- public CodexUserInfoView getCodexUserInfo(Long userId) {
|
|
|
- List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
- CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
|
|
|
- // 查询Claude Code信息
|
|
|
- CodexUserInfoView.ClaudeCodeInfo claudeCodeInfo = getClaudeCodeInfo(userId);
|
|
|
-
|
|
|
- if (codexUser == null) {
|
|
|
- //是否有claude code
|
|
|
- if (claudeCodeInfo != null && claudeCodeInfo.getClaudeDailyLimit() != null) {
|
|
|
- GoodsDonSku sku = null;
|
|
|
- if (claudeCodeInfo.getRenewSkuId() != null) {
|
|
|
- sku = skuMapper.selectById(claudeCodeInfo.getRenewSkuId());
|
|
|
- }
|
|
|
- //续费升级规格不存在 或者 不赠送codex
|
|
|
- if (sku == null || !sku.getIsCodex()) {
|
|
|
- GroupsRelation relation = relationMapper.selectById(claudeCodeInfo.getRelationId());
|
|
|
- GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
|
|
|
- sku = skuMapper.selectById(groupsTrips.getSkuId());
|
|
|
- }
|
|
|
-
|
|
|
- //sku 存在并且开启赠送codex
|
|
|
- if (sku != null && sku.getIsCodex()) {
|
|
|
- CodexUserPackageReq codexPackageReq = CodexUserPackageReq.builder()
|
|
|
- .userId(claudeCodeInfo.getUserId())
|
|
|
- .planName(sku.getCodexPlanName())
|
|
|
- .openaiDailyLimit(sku.getOpenaiDailyLimit())
|
|
|
- .openaiQuota(sku.getOpenaiQuota())
|
|
|
- .expiryTime(claudeCodeInfo.getExpiryTime())
|
|
|
- .build();
|
|
|
- createOrUpdateUserPackage(codexPackageReq);
|
|
|
- codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
|
|
|
- }
|
|
|
- }
|
|
|
- if (codexUser == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
-
|
|
|
- return CodexUserInfoView.builder().planName(codexUser.getPlanName()).openaiDailyLimit(codexUser.getOpenaiDailyLimit()).openaiQuota(codexUser.getOpenaiQuota()).relationId(codexUser.getRelationId()).claudeCodeInfo(claudeCodeInfo).build();
|
|
|
- }
|
|
|
-
|
|
|
- private CodexUserInfoView.ClaudeCodeInfo getClaudeCodeInfo(Long userId) {
|
|
|
- // 获取用户关联的ID列表
|
|
|
- List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
-
|
|
|
- ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).in(ClaudeCodeUser::getUserId, userIdList).last("limit 1"));
|
|
|
-
|
|
|
- if (claudeCodeUser == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- return CodexUserInfoView.ClaudeCodeInfo.builder()
|
|
|
- .userId(claudeCodeUser.getUserId())
|
|
|
- .planName(claudeCodeUser.getPlanName())
|
|
|
- .relationId(claudeCodeUser.getRelationId())
|
|
|
- .claudeDailyLimit(claudeCodeUser.getClaudeDailyLimit())
|
|
|
- .claudeQuota(claudeCodeUser.getClaudeQuota())
|
|
|
- .expiryTime(claudeCodeUser.getExpiryTime())
|
|
|
- .renewSkuId(claudeCodeUser.getRenewSkuId())
|
|
|
- .renewExpiryTime(claudeCodeUser.getRenewExpiryTime()).build();
|
|
|
- }
|
|
|
+ private final ClaudeCodexApiService claudeCodexApiService;
|
|
|
|
|
|
@Override
|
|
|
public void createOrUpdateUserPackage(CodexUserPackageReq req) {
|
|
|
@@ -213,23 +155,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
*/
|
|
|
@Override
|
|
|
public ClaudeCodeResp createOrUpdateCodexUserPackageApi(Long userId, String planName, Integer openaiDailyLimit, Integer openaiQuota, Date expiryTime) throws Exception {
|
|
|
- Map<String, Object> params = new HashMap<>();
|
|
|
- params.put("plan_name", planName.replaceAll(";", ""));
|
|
|
- params.put("openai_daily_limit", openaiDailyLimit);
|
|
|
- params.put("openai_quota", openaiQuota);
|
|
|
- params.put("expire_time", DateUtil.format(expiryTime, "yyyy-MM-dd HH:mm:ss"));
|
|
|
- //claude 类型
|
|
|
- params.put("service_type", "openai");
|
|
|
-
|
|
|
- String planUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/plan", userId);
|
|
|
- ClaudeCodeResp codexResp = executeCodexPostApi(planUrl, Jsons.toJson(params));
|
|
|
-
|
|
|
- if (!"success".equals(codexResp.getMessage())) {
|
|
|
- log.info("用户codex用户:{}套餐:{}创建或更新失败,info:{}", userId, planName, codexResp.getMessage());
|
|
|
- throw BusinessRuntimeException.getInstance("调用codex 套餐接口失败");
|
|
|
- }
|
|
|
-
|
|
|
- return codexResp;
|
|
|
+ return claudeCodexApiService.createOrUpdateCodexUserPackageApi(userId, planName, openaiDailyLimit, openaiQuota, expiryTime);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -468,17 +394,9 @@ public class CodexServiceImpl implements CodexService {
|
|
|
if (checkCodexUser(userId)) {
|
|
|
Long codexUserId = getCodexUserId(userId);
|
|
|
Assert.notNull(codexUserId, "您的codex账号已过期");
|
|
|
- String apiKeyUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/keys", codexUserId);
|
|
|
- Map<String, Object> parmas = new HashMap<>();
|
|
|
- parmas.put("name", req.getName());
|
|
|
- Integer expiresDays = req.getExpiresDays();
|
|
|
- if (expiresDays != null) {
|
|
|
- parmas.put("expires_days", expiresDays);
|
|
|
- }
|
|
|
- ClaudeCodeResp claudeCodeResp = executeClaudeCodePostApi(apiKeyUrl, Jsons.toJson(parmas));
|
|
|
- if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {
|
|
|
- throw BusinessRuntimeException.getInstance("创建失败");
|
|
|
- }
|
|
|
+
|
|
|
+ req.setUserId(codexUserId);
|
|
|
+ ClaudeCodeResp claudeCodeResp = claudeCodexApiService.createApiKeys(req);
|
|
|
return claudeCodeResp;
|
|
|
}
|
|
|
throw new BusinessRuntimeException("您还没有codex账号");
|
|
|
@@ -490,14 +408,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
Long keyId = delReq.getKeyId();
|
|
|
if (checkCodexUser(userId)) {
|
|
|
Long codexUserId = getCodexUserId(userId);
|
|
|
- String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/keys/%s/%s", codexUserId, keyId);
|
|
|
- HttpResponse execute = HttpUtil.createRequest(Method.DELETE, url).header(Constant.CLAUDE_CODE_HEARD_KEY, Constant.CLAUDE_CODE_API_ADMIN_KEY).setConnectionTimeout(Constant.CONNECT_MILLISECONDS).execute();
|
|
|
- String responseBody = execute.body();
|
|
|
- ClaudeCodeResp claudeCodeResp = Jsons.parseObject(responseBody, ClaudeCodeResp.class);
|
|
|
- if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {
|
|
|
- log.error("删除key失败:{}", claudeCodeResp.getMessage());
|
|
|
- throw BusinessRuntimeException.getInstance("删除失败");
|
|
|
- }
|
|
|
+ claudeCodexApiService.deleteApiKeysById(codexUserId, keyId);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -505,9 +416,8 @@ public class CodexServiceImpl implements CodexService {
|
|
|
public List<ClaudeCodeUserApiKeysView> getUserApiKeys(long userId) throws Exception {
|
|
|
Long codexUserId = getCodexUserId(userId);
|
|
|
if (codexUserId != null) {
|
|
|
- String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/keys", codexUserId);
|
|
|
- ClaudeCodeResp claudeCodeResp = executeClaudeCodeGetApi(url);
|
|
|
- return Jsons.parseList(claudeCodeResp.getData(), ClaudeCodeUserApiKeysView.class);
|
|
|
+ List<ClaudeCodeUserApiKeysView> keysViews = claudeCodexApiService.getUserApiKeys(codexUserId);
|
|
|
+ return keysViews;
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
@@ -699,10 +609,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
if (codexUser == null) {
|
|
|
return null;
|
|
|
}
|
|
|
- // 调用Codex API获取实时使用统计
|
|
|
- String usageUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/users/%s/usage?period=%s", codexUser.getUserId(), period);
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(usageUrl);
|
|
|
- return codexResp;
|
|
|
+ return claudeCodexApiService.getCodexUserOpenAIUsage(codexUser.getUserId(), period);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -756,42 +663,6 @@ public class CodexServiceImpl implements CodexService {
|
|
|
throw new BusinessRuntimeException("更新每日限额失败");
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- @Override
|
|
|
- public ClaudeCodeResp getSystemMetrics(String period, String metric) {
|
|
|
- try {
|
|
|
- // 构建系统指标API URL
|
|
|
- String metricsUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/system/metrics");
|
|
|
-
|
|
|
- // 添加查询参数
|
|
|
- List<String> queryParams = new ArrayList<>();
|
|
|
- if (period != null && !period.isEmpty()) {
|
|
|
- queryParams.add("period=" + period);
|
|
|
- }
|
|
|
- if (metric != null && !metric.isEmpty()) {
|
|
|
- queryParams.add("metric=" + metric);
|
|
|
- }
|
|
|
-
|
|
|
- if (!queryParams.isEmpty()) {
|
|
|
- metricsUrl += "?" + String.join("&", queryParams);
|
|
|
- }
|
|
|
-
|
|
|
- // 调用Codex API获取系统指标
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(metricsUrl);
|
|
|
-
|
|
|
- if (codexResp == null) {
|
|
|
- log.error("获取系统指标失败: API返回null");
|
|
|
- throw new BusinessRuntimeException("获取系统指标失败");
|
|
|
- }
|
|
|
-
|
|
|
- log.info("成功获取系统指标, period={}, metric={}", period, metric);
|
|
|
- return codexResp;
|
|
|
-
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("获取系统指标异常, period={}, metric={}, error={}", period, metric, StringUtil.getErrorText(e));
|
|
|
- throw new BusinessRuntimeException("获取系统指标失败");
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
@Override
|
|
|
public ClaudeCodeResp getJavaUserDashboard(Long userId) {
|
|
|
@@ -801,11 +672,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
if (codexUser == null) {
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
- // 调用Codex API获取用户控制台数据
|
|
|
- String dashboardUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/users/%s/dashboard", codexUser.getUserId());
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(dashboardUrl);
|
|
|
- return codexResp;
|
|
|
+ return claudeCodexApiService.getCodexUserDashboard(codexUser.getUserId());
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -822,12 +689,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
start = DateUtil.offsetDay(now, -1).toString();
|
|
|
end = now.toString();
|
|
|
}
|
|
|
-
|
|
|
- // 调用Codex API获取用户分析数据
|
|
|
- String analyticsUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/users/%s/analytics?start=%s&end=%s&page=%s&limit=%s&order=%s", codexUser.getUserId(), start, end, page, limit, order);
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(analyticsUrl);
|
|
|
-
|
|
|
- return codexResp;
|
|
|
+ return claudeCodexApiService.getCodexUserAnalytics(codexUser.getUserId(), start, end, page, limit, order);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -975,34 +837,6 @@ public class CodexServiceImpl implements CodexService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Codex GET请求
|
|
|
- */
|
|
|
- public ClaudeCodeResp executeCodexGetApi(String url) {
|
|
|
- Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
|
|
|
- try {
|
|
|
- return build.call(() -> {
|
|
|
- try {
|
|
|
- HttpResponse execute = HttpUtil.createGet(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 (!"success".equals(resp.getMessage())) {
|
|
|
- log.error("codex GET URL:{}接口返回msg:{}", url, resp.getMessage());
|
|
|
- }
|
|
|
- return resp;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("codex GET请求异常: url={}, error={}", url, StringUtil.getErrorText(e));
|
|
|
- return null;
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (ExecutionException | com.github.rholder.retry.RetryException e) {
|
|
|
- log.error("重试调用codex GET请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
|
|
|
- throw BusinessRuntimeException.getInstance("获取codex使用统计失败");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 获取订单单价
|
|
|
*/
|
|
|
@@ -1325,18 +1159,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
|
|
|
@Override
|
|
|
public 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;
|
|
|
+ return claudeCodexApiService.delCodexUserPackages(userId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -1345,30 +1168,6 @@ public class CodexServiceImpl implements CodexService {
|
|
|
createOrUpdateCodexUserInfo(orderId, relation.getId(), relation.getUserId(), sku, 2);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 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("接口请求失败");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 校验是否是codex用户
|
|
|
* @param userId
|
|
|
@@ -1395,57 +1194,4 @@ public class CodexServiceImpl implements CodexService {
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * GET请求
|
|
|
- */
|
|
|
- public ClaudeCodeResp executeClaudeCodeGetApi(String url) {
|
|
|
- Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
|
|
|
- try {
|
|
|
- return build.call(() -> {
|
|
|
- try {
|
|
|
- HttpResponse execute = HttpUtil.createGet(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 GET URL:{}接口返回msg:{}", url, resp.getMessage());
|
|
|
- }
|
|
|
- return resp;
|
|
|
- } catch (HttpException e) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (ExecutionException | RetryException e) {
|
|
|
- log.error("重试调用claude code GET请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
|
|
|
- throw BusinessRuntimeException.getInstance("接口请求失败");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public ClaudeCodeResp executeClaudeCodePostApi(String url, String body) {
|
|
|
- Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
|
|
|
- try {
|
|
|
- return build.call(() -> {
|
|
|
- try {
|
|
|
- HttpResponse execute = HttpUtil.createPost(url)
|
|
|
- .header(Constant.CLAUDE_CODE_HEARD_KEY, Constant.CLAUDE_CODE_API_ADMIN_KEY)
|
|
|
- .setConnectionTimeout(Constant.CONNECT_MILLISECONDS)
|
|
|
- .body(body)
|
|
|
- .execute();
|
|
|
- ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class);
|
|
|
- if (!Constant.SUCCESS.equals(resp.getMessage())) {
|
|
|
- log.error("claude code POST URL:{}接口返回msg:{}", url, resp.getMessage());
|
|
|
- }
|
|
|
- return resp;
|
|
|
- } catch (Exception e) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (ExecutionException | RetryException e) {
|
|
|
- log.info("重试调用claude code POST请求 url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
|
|
|
- throw BusinessRuntimeException.getInstance("接口请求失败");
|
|
|
- }
|
|
|
- }
|
|
|
}
|