zoujiajian 11 mēneši atpakaļ
vecāks
revīzija
8e0b4ab018

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

@@ -3,14 +3,18 @@ package com.cyksj.service.codex;
 import com.cyksj.model.entity.CodexUser;
 import com.cyksj.model.entity.GoodsDonSku;
 import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.request.ClaudeCodeApiKeysReq;
+import com.cyksj.model.request.ClaudeCodeDelReq;
 import com.cyksj.model.request.CodexUpgradePayReq;
 import com.cyksj.model.request.UpdateDailyLimitRequest;
 import com.cyksj.model.request.codex.CodexUserPackageReq;
 import com.cyksj.model.response.CodexDeductResp;
 import com.cyksj.model.response.claudecode.ClaudeCodeResp;
+import com.cyksj.model.views.ClaudeCodeUserApiKeysView;
 import com.cyksj.model.views.CodexUserInfoView;
 
 import java.util.Date;
+import java.util.List;
 
 /**
  * 项目名: yhlxj11111111
@@ -73,4 +77,10 @@ public interface CodexService {
     Boolean getUserSubs(Long userId);
 
     CodexUserInfoView getUserSubsInfo(long userId);
+
+    ClaudeCodeResp createApiKeys(ClaudeCodeApiKeysReq req) throws Exception;
+
+    List<ClaudeCodeUserApiKeysView> getUserApiKeys(long userId) throws Exception;
+
+    void deleteApiKeysById(ClaudeCodeDelReq delReq) throws Exception;
 }

+ 133 - 0
netflix-service/src/main/java/com/cyksj/service/codex/impl/CodexServiceImpl.java

@@ -28,11 +28,14 @@ import com.cyksj.mapper.manage.distribute.UserDistributeSharedMapper;
 import com.cyksj.model.dto.CouponPopularizeDto;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.GroupsRelationView;
+import com.cyksj.model.request.ClaudeCodeApiKeysReq;
+import com.cyksj.model.request.ClaudeCodeDelReq;
 import com.cyksj.model.request.CodexUpgradePayReq;
 import com.cyksj.model.request.UpdateDailyLimitRequest;
 import com.cyksj.model.request.codex.CodexUserPackageReq;
 import com.cyksj.model.response.CodexDeductResp;
 import com.cyksj.model.response.claudecode.ClaudeCodeResp;
+import com.cyksj.model.views.ClaudeCodeUserApiKeysView;
 import com.cyksj.model.views.CodexUserInfoView;
 import com.cyksj.model.views.CouponNewUserView;
 import com.cyksj.model.views.CouponUserView;
@@ -459,6 +462,56 @@ public class CodexServiceImpl implements CodexService {
 				.build();
 	}
 
+	@Override
+	public ClaudeCodeResp createApiKeys(ClaudeCodeApiKeysReq req) throws Exception {
+		Long userId = req.getUserId();
+		if (checkCodexUser(userId)) {
+			Long claudeCodeUserId = getCodexUserId(userId);
+			Assert.notNull(claudeCodeUserId, "您的codex账号已过期");
+			String apiKeyUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/keys", claudeCodeUserId);
+			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("创建失败");
+			}
+			return claudeCodeResp;
+		}
+		throw new BusinessRuntimeException("您还没有codex账号");
+	}
+
+	@Override
+	public void deleteApiKeysById(ClaudeCodeDelReq delReq) throws Exception {
+		Long userId = delReq.getUserId();
+		Long keyId = delReq.getKeyId();
+		if (checkCodexUser(userId)) {
+			Long claudeCodeUserId = getCodexUserId(userId);
+			String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/keys/%s/%s", claudeCodeUserId, 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("删除失败");
+			}
+		}
+	}
+
+	@Override
+	public List<ClaudeCodeUserApiKeysView> getUserApiKeys(long userId) throws Exception {
+		Long claudeCodeUserId = getCodexUserId(userId);
+		if (claudeCodeUserId != null) {
+			String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/keys", claudeCodeUserId);
+			ClaudeCodeResp claudeCodeResp = executeClaudeCodeGetApi(url);
+			return Jsons.parseList(claudeCodeResp.getData(), ClaudeCodeUserApiKeysView.class);
+		}
+		return null;
+	}
+
 	private GroupsRelation getClaudeCodeRelation(Long userId, GoodsDonSku sku) {
 		//获取车票车位
 		GroupsRelation relation = getRelation(sku);
@@ -1308,4 +1361,84 @@ public class CodexServiceImpl implements CodexService {
 			throw BusinessRuntimeException.getInstance("接口请求失败");
 		}
 	}
+
+	/**
+	 * 校验是否是codex用户
+	 * @param userId
+	 * @return
+	 */
+	public Boolean checkCodexUser(Long userId) {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		Integer count = relationMapper.selectCountByGoodsId(userIdList, Constant.CLAUDE_CODE_GOODS_ID, DateTime.now());
+		return count > 0;
+	}
+
+	/**
+	 * 获取codex用户id
+	 */
+	public Long getCodexUserId(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());
+		if (groupsRelationView != null) {
+			return groupsRelationView.getUserId();
+		}
+		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("接口请求失败");
+		}
+	}
 }

+ 36 - 0
netflix-web/src/main/java/com/cyksj/web/controller/codex/CodexController.java

@@ -13,6 +13,8 @@ import com.cyksj.model.entity.CodexUser;
 import com.cyksj.model.entity.GroupsRelation;
 import com.cyksj.model.entity.GroupsTrips;
 import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.request.ClaudeCodeApiKeysReq;
+import com.cyksj.model.request.ClaudeCodeDelReq;
 import com.cyksj.model.request.CodexUpgradePayReq;
 import com.cyksj.model.request.UpdateDailyLimitRequest;
 import com.cyksj.model.response.CodexDeductResp;
@@ -26,6 +28,7 @@ import com.ejlchina.searcher.param.Operator;
 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 java.util.ArrayList;
@@ -58,6 +61,39 @@ public class CodexController {
     private final BeanSearcher beanSearcher;
 
 
+    /**
+     * 用户创建API KEY
+     */
+    @PostMapping("/create/apiKeys")
+    public ClaudeCodeResp createApiKeys(@RequestBody @Validated ClaudeCodeApiKeysReq req) throws Exception {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        req.setUserId(userId);
+        ClaudeCodeResp resp = codexService.createApiKeys(req);
+        return resp;
+    }
+
+    /**
+     * 用户api keys列表
+     */
+    @GetMapping("/get/apiKeys")
+    public Result<List<ClaudeCodeUserApiKeysView>> getUserApiKeys() throws Exception {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        List<ClaudeCodeUserApiKeysView> userApiKeys = codexService.getUserApiKeys(userId);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(userApiKeys);
+    }
+
+    /**
+     * 删除api key
+     */
+    @DeleteMapping("/delete/apiKeys")
+    public Result<String> deleteApiKeysById(@RequestBody ClaudeCodeDelReq delReq) throws Exception {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        delReq.setUserId(userId);
+        codexService.deleteApiKeysById(delReq);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
+
     /**
      * codex 用户是否存在订阅
      */