Parcourir la source

fix codex渠道列表

zoujiajian il y a 2 jours
Parent
commit
c146b55b9e

+ 17 - 0
netflix-dao/src/main/java/com/cyksj/model/request/codex/CodexChannelEnabledReq.java

@@ -0,0 +1,17 @@
+package com.cyksj.model.request.codex;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Codex 上游渠道开关请求。
+ */
+@Getter
+@Setter
+public class CodexChannelEnabledReq {
+
+    @NotNull(message = "enabled 字段不能为空")
+    private Boolean enabled;
+}

+ 11 - 0
netflix-service/src/main/java/com/cyksj/service/api/ClaudeCodexApiService.java

@@ -1,6 +1,7 @@
 package com.cyksj.service.api;
 
 import com.cyksj.model.request.ClaudeCodeApiKeysReq;
+import com.cyksj.model.request.codex.CodexChannelEnabledReq;
 import com.cyksj.model.response.claudecode.ClaudeCodeResp;
 import com.cyksj.model.views.ClaudeCodeUserApiKeysView;
 
@@ -58,4 +59,14 @@ public interface ClaudeCodexApiService {
 	 * codex用户使用量统计
 	 */
 	ClaudeCodeResp getCodexUserAnalytics(Long userId, String start, String end, Integer page, Integer limit, String order);
+
+	/**
+	 * 查询全部 Codex 上游渠道(包含已禁用渠道)
+	 */
+	ClaudeCodeResp getCodexChannels();
+
+	/**
+	 * 启用或禁用指定 Codex 上游渠道
+	 */
+	ClaudeCodeResp updateCodexChannelEnabled(Long channelId, CodexChannelEnabledReq req) throws Exception;
 }

+ 55 - 0
netflix-service/src/main/java/com/cyksj/service/api/impl/ClaudeCodexApiServiceImpl.java

@@ -10,6 +10,7 @@ import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.common.util.StringUtil;
 import com.cyksj.model.request.ClaudeCodeApiKeysReq;
+import com.cyksj.model.request.codex.CodexChannelEnabledReq;
 import com.cyksj.model.response.claudecode.ClaudeCodeResp;
 import com.cyksj.model.views.ClaudeCodeUserApiKeysView;
 import com.cyksj.service.api.ClaudeCodexApiService;
@@ -132,6 +133,31 @@ public class ClaudeCodexApiServiceImpl implements ClaudeCodexApiService {
 		return codexResp;
 	}
 
+	@Override
+	public ClaudeCodeResp getCodexChannels() {
+		String url = Constant.CLAUDE_CODE_API_PREFIX + "api/codex/channels";
+		return checkCodexChannelResponse(executeGetApi(url));
+	}
+
+	@Override
+	public ClaudeCodeResp updateCodexChannelEnabled(Long channelId, CodexChannelEnabledReq req) throws Exception {
+		String url = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/codex/channels/%s/enabled", channelId);
+		Map<String, Object> params = new HashMap<>();
+		params.put("enabled", req.getEnabled());
+		return checkCodexChannelResponse(executePutApi(url, Jsons.toJson(params)));
+	}
+
+	private ClaudeCodeResp checkCodexChannelResponse(ClaudeCodeResp resp) {
+		if (resp == null) {
+			throw BusinessRuntimeException.getInstance("Codex 渠道服务无响应");
+		}
+		if (!Boolean.TRUE.equals(resp.getSuccess()) || !Integer.valueOf(0).equals(resp.getCode())) {
+			String message = StringUtil.isEmpty(resp.getMessage()) ? "Codex 渠道服务请求失败" : resp.getMessage();
+			throw BusinessRuntimeException.getInstance(message);
+		}
+		return resp;
+	}
+
 	/**
 	 * GET请求
 	 */
@@ -187,6 +213,35 @@ public class ClaudeCodexApiServiceImpl implements ClaudeCodexApiService {
 		}
 	}
 
+	/**
+	 * PUT请求
+	 */
+	public ClaudeCodeResp executePutApi(String url, String body) {
+		Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
+		try {
+			return build.call(() -> {
+				try {
+					HttpResponse execute = HttpUtil.createRequest(Method.PUT, url)
+							.header(Constant.CLAUDE_CODE_HEARD_KEY, Constant.CLAUDE_CODE_API_ADMIN_KEY)
+							.header("Content-Type", "application/json")
+							.setConnectionTimeout(Constant.CONNECT_MILLISECONDS)
+							.body(body)
+							.execute();
+					ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class);
+					if (!Constant.SUCCESS.equals(resp.getMessage())) {
+						log.error("PUT URL:{}接口返回msg:{}", url, resp.getMessage());
+					}
+					return resp;
+				} catch (Exception e) {
+					return null;
+				}
+			});
+		} catch (ExecutionException | RetryException e) {
+			log.error("重试调用PUT请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
+			throw BusinessRuntimeException.getInstance("接口请求失败");
+		}
+	}
+
 	/**
 	 * claude code DELETE请求
 	 */

+ 46 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/codex/CmsCodexChannelController.java

@@ -0,0 +1,46 @@
+package com.cyksj.web.controller.manage.codex;
+
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.request.codex.CodexChannelEnabledReq;
+import com.cyksj.model.response.claudecode.ClaudeCodeResp;
+import com.cyksj.service.api.ClaudeCodexApiService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 管理端 - Codex 上游渠道管理。
+ */
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/manage/codex/channels")
+public class CmsCodexChannelController {
+
+    private final ClaudeCodexApiService claudeCodexApiService;
+
+    /** 查询全部 Codex 渠道,包含已启用和已禁用渠道。 */
+    @GetMapping
+    public Result<Object> getChannels() {
+        ClaudeCodeResp resp = claudeCodexApiService.getCodexChannels();
+        return GatewayResponse.SUCCESS.newBuilder().toResult(resp.getData());
+    }
+
+    /** 幂等地设置指定 Codex 渠道的启用状态。 */
+    @PutMapping("/{id}/enabled")
+    public Result<Object> updateEnabled(@PathVariable Long id,
+                                        @Validated @RequestBody CodexChannelEnabledReq req) throws Exception {
+        if (id == null || id <= 0) {
+            throw BusinessRuntimeException.getInstance("无效的 Codex 渠道 ID");
+        }
+        ClaudeCodeResp resp = claudeCodexApiService.updateCodexChannelEnabled(id, req);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(resp.getData());
+    }
+}