Coze接口配置及调用变更
This commit is contained in:
@@ -0,0 +1,234 @@
|
|||||||
|
# API Base URL 字段调整总结
|
||||||
|
|
||||||
|
## 修改概述
|
||||||
|
|
||||||
|
将 `AiConfig` 实体类中的 `apiBaseUrl` 字段从"基础URL"调整为"完整的API URL",不再需要在调用时拼接任何后缀。
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
### 1. 实体类和DTO修改
|
||||||
|
|
||||||
|
#### 1.1 AiConfig.java
|
||||||
|
- **文件路径**: `backend-single/src/main/java/com/emotion/entity/AiConfig.java`
|
||||||
|
- **修改内容**: 更新 `apiBaseUrl` 字段注释
|
||||||
|
- **修改前**: `API基础URL`
|
||||||
|
- **修改后**: `API完整URL(包含完整的接口路径,不需要再拼接任何后缀)例如:https://api.coze.cn/v3/chat`
|
||||||
|
|
||||||
|
#### 1.2 AiConfigCreateRequest.java
|
||||||
|
- **文件路径**: `backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigCreateRequest.java`
|
||||||
|
- **修改内容**: 更新 `apiBaseUrl` 字段注释和验证消息
|
||||||
|
- **修改前**: `API基础URL` / `API基础URL不能为空`
|
||||||
|
- **修改后**: `API完整URL(包含完整的接口路径,不需要再拼接任何后缀)` / `API完整URL不能为空`
|
||||||
|
|
||||||
|
#### 1.3 AiConfigUpdateRequest.java
|
||||||
|
- **文件路径**: `backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigUpdateRequest.java`
|
||||||
|
- **修改内容**: 更新 `apiBaseUrl` 字段注释
|
||||||
|
- **修改前**: `API基础URL`
|
||||||
|
- **修改后**: `API完整URL(包含完整的接口路径,不需要再拼接任何后缀)`
|
||||||
|
|
||||||
|
#### 1.4 AiConfigResponse.java
|
||||||
|
- **文件路径**: `backend-single/src/main/java/com/emotion/dto/response/aiconfig/AiConfigResponse.java`
|
||||||
|
- **修改内容**: 更新 `apiBaseUrl` 字段注释
|
||||||
|
- **修改前**: `API基础URL`
|
||||||
|
- **修改后**: `API完整URL(包含完整的接口路径,不需要再拼接任何后缀)`
|
||||||
|
|
||||||
|
### 2. 后端服务层修改
|
||||||
|
|
||||||
|
#### 2.1 AiChatServiceImpl.java
|
||||||
|
- **文件路径**: `backend-single/src/main/java/com/emotion/service/impl/AiChatServiceImpl.java`
|
||||||
|
|
||||||
|
##### 修改1: getApiPath方法
|
||||||
|
```java
|
||||||
|
// 修改前
|
||||||
|
private String getApiPath(AiConfig config) {
|
||||||
|
// 默认使用 /v3/chat 路径
|
||||||
|
return "/v3/chat";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改后
|
||||||
|
private String getApiPath(AiConfig config) {
|
||||||
|
// apiBaseUrl已经是完整的URL,返回空字符串
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 修改2: 新增extractBaseUrl方法
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 从apiBaseUrl中提取基础URL(用于状态查询和消息查询等辅助接口)
|
||||||
|
* 例如:https://api.coze.cn/v3/chat -> https://api.coze.cn
|
||||||
|
*/
|
||||||
|
private String extractBaseUrl(String apiBaseUrl) {
|
||||||
|
if (apiBaseUrl == null || apiBaseUrl.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
java.net.URL url = new java.net.URL(apiBaseUrl);
|
||||||
|
return url.getProtocol() + "://" + url.getHost() + (url.getPort() > 0 ? ":" + url.getPort() : "");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("解析apiBaseUrl失败: {}", apiBaseUrl, e);
|
||||||
|
// 尝试简单截取
|
||||||
|
int pathIndex = apiBaseUrl.indexOf("/", apiBaseUrl.indexOf("://") + 3);
|
||||||
|
if (pathIndex > 0) {
|
||||||
|
return apiBaseUrl.substring(0, pathIndex);
|
||||||
|
}
|
||||||
|
return apiBaseUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 修改3: 状态查询URL构建
|
||||||
|
```java
|
||||||
|
// 修改前
|
||||||
|
String statusUrl = config.getApiBaseUrl() + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id=" + conversationId;
|
||||||
|
|
||||||
|
// 修改后
|
||||||
|
String baseUrl = extractBaseUrl(config.getApiBaseUrl());
|
||||||
|
String statusUrl = baseUrl + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id=" + conversationId;
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 修改4: 消息查询URL构建
|
||||||
|
```java
|
||||||
|
// 修改前
|
||||||
|
String messagesUrl = config.getApiBaseUrl() + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id=" + conversationId;
|
||||||
|
|
||||||
|
// 修改后
|
||||||
|
String baseUrl = extractBaseUrl(config.getApiBaseUrl());
|
||||||
|
String messagesUrl = baseUrl + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id=" + conversationId;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 前端修改
|
||||||
|
|
||||||
|
#### 3.1 web-admin/src/views/aiconfig/AiConfigList.vue
|
||||||
|
|
||||||
|
##### 修改1: 表单字段标签和提示
|
||||||
|
```vue
|
||||||
|
<!-- 修改前 -->
|
||||||
|
<el-form-item label="API基础URL" prop="apiBaseUrl">
|
||||||
|
<el-input v-model="formData.apiBaseUrl" placeholder="请输入API基础URL" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 修改后 -->
|
||||||
|
<el-form-item label="API完整URL" prop="apiBaseUrl">
|
||||||
|
<el-input v-model="formData.apiBaseUrl" placeholder="请输入完整的API URL,如:https://api.coze.cn/v3/chat" />
|
||||||
|
</el-form-item>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 修改2: 详情展示标签
|
||||||
|
```vue
|
||||||
|
<!-- 修改前 -->
|
||||||
|
<el-descriptions-item label="API基础URL">{{ viewData.apiBaseUrl }}</el-descriptions-item>
|
||||||
|
|
||||||
|
<!-- 修改后 -->
|
||||||
|
<el-descriptions-item label="API完整URL">{{ viewData.apiBaseUrl }}</el-descriptions-item>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 修改3: 表单验证规则
|
||||||
|
```javascript
|
||||||
|
// 修改前
|
||||||
|
apiBaseUrl: [{ required: true, message: '请输入API基础URL', trigger: 'blur' }]
|
||||||
|
|
||||||
|
// 修改后
|
||||||
|
apiBaseUrl: [{ required: true, message: '请输入完整的API URL', trigger: 'blur' }]
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 修改4: 测试功能URL构建
|
||||||
|
```javascript
|
||||||
|
// 修改前
|
||||||
|
const initTestData = (config: AiConfig) => {
|
||||||
|
testRequest.url = config.apiBaseUrl + '/v3/chat'
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改后
|
||||||
|
const initTestData = (config: AiConfig) => {
|
||||||
|
// apiBaseUrl已经是完整的API URL,直接使用
|
||||||
|
testRequest.url = config.apiBaseUrl
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 单元测试
|
||||||
|
|
||||||
|
#### 4.1 AiChatServiceImplTest.java
|
||||||
|
- **文件路径**: `backend-single/src/test/java/com/emotion/service/AiChatServiceImplTest.java`
|
||||||
|
- **测试内容**:
|
||||||
|
1. `testGetApiPath`: 测试getApiPath方法返回空字符串
|
||||||
|
2. `testExtractBaseUrl`: 测试extractBaseUrl方法提取基础URL
|
||||||
|
3. `testApiUrlConstruction`: 测试完整API URL的构建逻辑
|
||||||
|
4. `testStatusQueryUrlConstruction`: 测试状态查询URL的构建逻辑
|
||||||
|
5. `testMessageQueryUrlConstruction`: 测试消息查询URL的构建逻辑
|
||||||
|
6. `testDifferentApiBaseUrlFormats`: 测试不同格式的apiBaseUrl
|
||||||
|
7. `testEdgeCases`: 测试边界情况
|
||||||
|
|
||||||
|
**测试结果**: ✅ 所有7个测试用例全部通过
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
### 后端影响
|
||||||
|
1. **AiConfig实体类**: 字段语义变更,但数据库字段名不变
|
||||||
|
2. **AiChatServiceImpl**: API调用逻辑调整,不再拼接路径
|
||||||
|
3. **DTO类**: 注释和验证消息更新
|
||||||
|
|
||||||
|
### 前端影响
|
||||||
|
1. **web-admin AI配置管理页面**:
|
||||||
|
- 表单标签和提示文本更新
|
||||||
|
- 测试功能URL构建逻辑调整
|
||||||
|
- 用户需要输入完整的API URL
|
||||||
|
|
||||||
|
### 数据库影响
|
||||||
|
- **无需修改数据库**: 字段名 `api_base_url` 保持不变
|
||||||
|
- **数据迁移**: 需要将现有数据从基础URL更新为完整URL
|
||||||
|
- 例如: `https://api.coze.cn` → `https://api.coze.cn/v3/chat`
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 修改前
|
||||||
|
```java
|
||||||
|
AiConfig config = new AiConfig();
|
||||||
|
config.setApiBaseUrl("https://api.coze.cn"); // 基础URL
|
||||||
|
// 调用时拼接: config.getApiBaseUrl() + "/v3/chat"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改后
|
||||||
|
```java
|
||||||
|
AiConfig config = new AiConfig();
|
||||||
|
config.setApiBaseUrl("https://api.coze.cn/v3/chat"); // 完整URL
|
||||||
|
// 调用时直接使用: config.getApiBaseUrl()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试验证
|
||||||
|
|
||||||
|
### 后端测试
|
||||||
|
```bash
|
||||||
|
cd backend-single
|
||||||
|
mvn test -Dtest=AiChatServiceImplTest
|
||||||
|
```
|
||||||
|
|
||||||
|
**测试结果**:
|
||||||
|
- Tests run: 7
|
||||||
|
- Failures: 0
|
||||||
|
- Errors: 0
|
||||||
|
- Skipped: 0
|
||||||
|
- ✅ 全部通过
|
||||||
|
|
||||||
|
### 前端测试
|
||||||
|
1. 启动 web-admin 前端项目
|
||||||
|
2. 进入 AI配置管理页面
|
||||||
|
3. 点击"新增配置"或"编辑"按钮
|
||||||
|
4. 在"API完整URL"字段输入完整的API地址(如:`https://api.coze.cn/v3/chat`)
|
||||||
|
5. 保存配置
|
||||||
|
6. 点击"测试"按钮,验证接口调用是否成功
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **数据迁移**: 现有的AI配置数据需要更新,将基础URL改为完整URL
|
||||||
|
2. **向后兼容**: 如果有其他服务依赖旧的URL格式,需要同步更新
|
||||||
|
3. **文档更新**: 需要更新相关的API文档和使用说明
|
||||||
|
4. **配置示例**: 需要更新配置示例,明确说明应该填写完整的API URL
|
||||||
|
|
||||||
|
## 完成时间
|
||||||
|
2025-12-22
|
||||||
|
|
||||||
|
## 修改人员
|
||||||
|
System
|
||||||
+3
-2
@@ -40,9 +40,10 @@ public class AiConfigCreateRequest {
|
|||||||
private String provider;
|
private String provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API基础URL
|
* API完整URL(包含完整的接口路径,不需要再拼接任何后缀)
|
||||||
|
* 例如:https://api.coze.cn/v3/chat
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "API基础URL不能为空")
|
@NotBlank(message = "API完整URL不能为空")
|
||||||
private String apiBaseUrl;
|
private String apiBaseUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
-1
@@ -41,7 +41,8 @@ public class AiConfigUpdateRequest {
|
|||||||
private String provider;
|
private String provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API基础URL
|
* API完整URL(包含完整的接口路径,不需要再拼接任何后缀)
|
||||||
|
* 例如:https://api.coze.cn/v3/chat
|
||||||
*/
|
*/
|
||||||
private String apiBaseUrl;
|
private String apiBaseUrl;
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -35,7 +35,8 @@ public class AiConfigResponse extends BaseResponse {
|
|||||||
private String provider;
|
private String provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API基础URL
|
* API完整URL(包含完整的接口路径,不需要再拼接任何后缀)
|
||||||
|
* 例如:https://api.coze.cn/v3/chat
|
||||||
*/
|
*/
|
||||||
private String apiBaseUrl;
|
private String apiBaseUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ public class AiConfig extends BaseEntity {
|
|||||||
private String provider;
|
private String provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API基础URL
|
* API完整URL(包含完整的接口路径,不需要再拼接任何后缀)
|
||||||
|
* 例如:https://api.coze.cn/v3/chat
|
||||||
*/
|
*/
|
||||||
@TableField("api_base_url")
|
@TableField("api_base_url")
|
||||||
private String apiBaseUrl;
|
private String apiBaseUrl;
|
||||||
|
|||||||
@@ -1377,8 +1377,9 @@ public class AiChatServiceImpl implements AiChatService {
|
|||||||
while (attempt < maxAttempts) {
|
while (attempt < maxAttempts) {
|
||||||
log.info("轮询聊天状态,第{}次尝试: chatId={}, conversationId={}", attempt + 1, chatId, conversationId);
|
log.info("轮询聊天状态,第{}次尝试: chatId={}, conversationId={}", attempt + 1, chatId, conversationId);
|
||||||
|
|
||||||
// 构建状态查询URL
|
// 构建状态查询URL(使用基础URL拼接状态查询路径)
|
||||||
String statusUrl = config.getApiBaseUrl() + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id="
|
String baseUrl = extractBaseUrl(config.getApiBaseUrl());
|
||||||
|
String statusUrl = baseUrl + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id="
|
||||||
+ conversationId;
|
+ conversationId;
|
||||||
|
|
||||||
// 构建请求头
|
// 构建请求头
|
||||||
@@ -1440,8 +1441,9 @@ public class AiChatServiceImpl implements AiChatService {
|
|||||||
|
|
||||||
log.info("获取聊天消息: chatId={}, conversationId={}", chatId, conversationId);
|
log.info("获取聊天消息: chatId={}, conversationId={}", chatId, conversationId);
|
||||||
|
|
||||||
// 构建消息查询URL
|
// 构建消息查询URL(使用基础URL拼接消息查询路径)
|
||||||
String messagesUrl = config.getApiBaseUrl() + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id="
|
String baseUrl = extractBaseUrl(config.getApiBaseUrl());
|
||||||
|
String messagesUrl = baseUrl + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id="
|
||||||
+ conversationId;
|
+ conversationId;
|
||||||
|
|
||||||
// 构建请求头
|
// 构建请求头
|
||||||
@@ -1993,10 +1995,33 @@ public class AiChatServiceImpl implements AiChatService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取配置的API路径
|
* 获取配置的API路径
|
||||||
|
* apiBaseUrl已经是完整的API URL,不需要再拼接路径
|
||||||
*/
|
*/
|
||||||
private String getApiPath(AiConfig config) {
|
private String getApiPath(AiConfig config) {
|
||||||
// 默认使用 /v3/chat 路径
|
// apiBaseUrl已经是完整的URL,返回空字符串
|
||||||
return "/v3/chat";
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从apiBaseUrl中提取基础URL(用于状态查询和消息查询等辅助接口)
|
||||||
|
* 例如:https://api.coze.cn/v3/chat -> https://api.coze.cn
|
||||||
|
*/
|
||||||
|
private String extractBaseUrl(String apiBaseUrl) {
|
||||||
|
if (apiBaseUrl == null || apiBaseUrl.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
java.net.URL url = new java.net.URL(apiBaseUrl);
|
||||||
|
return url.getProtocol() + "://" + url.getHost() + (url.getPort() > 0 ? ":" + url.getPort() : "");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("解析apiBaseUrl失败: {}", apiBaseUrl, e);
|
||||||
|
// 尝试简单截取
|
||||||
|
int pathIndex = apiBaseUrl.indexOf("/", apiBaseUrl.indexOf("://") + 3);
|
||||||
|
if (pathIndex > 0) {
|
||||||
|
return apiBaseUrl.substring(0, pathIndex);
|
||||||
|
}
|
||||||
|
return apiBaseUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package com.emotion.service;
|
||||||
|
|
||||||
|
import com.emotion.entity.AiConfig;
|
||||||
|
import com.emotion.service.impl.AiChatServiceImpl;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI聊天服务测试类
|
||||||
|
* 测试apiBaseUrl字段调整后的接口调用逻辑
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-22
|
||||||
|
*/
|
||||||
|
@SpringBootTest
|
||||||
|
@ActiveProfiles("local")
|
||||||
|
public class AiChatServiceImplTest {
|
||||||
|
|
||||||
|
private AiChatServiceImpl aiChatService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
aiChatService = new AiChatServiceImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("测试getApiPath方法返回空字符串")
|
||||||
|
public void testGetApiPath() throws Exception {
|
||||||
|
// 使用反射调用私有方法
|
||||||
|
Method getApiPathMethod = AiChatServiceImpl.class.getDeclaredMethod("getApiPath", AiConfig.class);
|
||||||
|
getApiPathMethod.setAccessible(true);
|
||||||
|
|
||||||
|
AiConfig config = AiConfig.builder()
|
||||||
|
.apiBaseUrl("https://api.coze.cn/v3/chat")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String result = (String) getApiPathMethod.invoke(aiChatService, config);
|
||||||
|
|
||||||
|
// 验证返回空字符串,因为apiBaseUrl已经是完整URL
|
||||||
|
assertEquals("", result, "getApiPath应该返回空字符串");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("测试extractBaseUrl方法提取基础URL")
|
||||||
|
public void testExtractBaseUrl() throws Exception {
|
||||||
|
// 使用反射调用私有方法
|
||||||
|
Method extractBaseUrlMethod = AiChatServiceImpl.class.getDeclaredMethod("extractBaseUrl", String.class);
|
||||||
|
extractBaseUrlMethod.setAccessible(true);
|
||||||
|
|
||||||
|
// 测试标准URL
|
||||||
|
String apiBaseUrl1 = "https://api.coze.cn/v3/chat";
|
||||||
|
String result1 = (String) extractBaseUrlMethod.invoke(aiChatService, apiBaseUrl1);
|
||||||
|
assertEquals("https://api.coze.cn", result1, "应该正确提取基础URL");
|
||||||
|
|
||||||
|
// 测试带端口的URL
|
||||||
|
String apiBaseUrl2 = "http://localhost:8080/v3/chat";
|
||||||
|
String result2 = (String) extractBaseUrlMethod.invoke(aiChatService, apiBaseUrl2);
|
||||||
|
assertEquals("http://localhost:8080", result2, "应该正确提取带端口的基础URL");
|
||||||
|
|
||||||
|
// 测试只有域名的URL
|
||||||
|
String apiBaseUrl3 = "https://api.example.com";
|
||||||
|
String result3 = (String) extractBaseUrlMethod.invoke(aiChatService, apiBaseUrl3);
|
||||||
|
assertEquals("https://api.example.com", result3, "应该正确处理只有域名的URL");
|
||||||
|
|
||||||
|
// 测试空字符串
|
||||||
|
String apiBaseUrl4 = "";
|
||||||
|
String result4 = (String) extractBaseUrlMethod.invoke(aiChatService, apiBaseUrl4);
|
||||||
|
assertEquals("", result4, "空字符串应该返回空字符串");
|
||||||
|
|
||||||
|
// 测试null
|
||||||
|
String result5 = (String) extractBaseUrlMethod.invoke(aiChatService, (String) null);
|
||||||
|
assertEquals("", result5, "null应该返回空字符串");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("测试完整API URL的构建逻辑")
|
||||||
|
public void testApiUrlConstruction() throws Exception {
|
||||||
|
Method getApiPathMethod = AiChatServiceImpl.class.getDeclaredMethod("getApiPath", AiConfig.class);
|
||||||
|
getApiPathMethod.setAccessible(true);
|
||||||
|
|
||||||
|
// 创建配置对象,apiBaseUrl是完整的API URL
|
||||||
|
AiConfig config = AiConfig.builder()
|
||||||
|
.apiBaseUrl("https://api.coze.cn/v3/chat")
|
||||||
|
.apiToken("test_token")
|
||||||
|
.botId("test_bot_id")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String apiPath = (String) getApiPathMethod.invoke(aiChatService, config);
|
||||||
|
|
||||||
|
// 模拟实际调用时的URL构建
|
||||||
|
String fullUrl = config.getApiBaseUrl() + apiPath;
|
||||||
|
|
||||||
|
// 验证完整URL就是apiBaseUrl本身
|
||||||
|
assertEquals("https://api.coze.cn/v3/chat", fullUrl,
|
||||||
|
"完整URL应该等于apiBaseUrl,因为apiPath为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("测试状态查询URL的构建逻辑")
|
||||||
|
public void testStatusQueryUrlConstruction() throws Exception {
|
||||||
|
Method extractBaseUrlMethod = AiChatServiceImpl.class.getDeclaredMethod("extractBaseUrl", String.class);
|
||||||
|
extractBaseUrlMethod.setAccessible(true);
|
||||||
|
|
||||||
|
// 创建配置对象
|
||||||
|
AiConfig config = AiConfig.builder()
|
||||||
|
.apiBaseUrl("https://api.coze.cn/v3/chat")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String baseUrl = (String) extractBaseUrlMethod.invoke(aiChatService, config.getApiBaseUrl());
|
||||||
|
String chatId = "test_chat_id";
|
||||||
|
String conversationId = "test_conversation_id";
|
||||||
|
|
||||||
|
// 模拟状态查询URL构建
|
||||||
|
String statusUrl = baseUrl + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id=" + conversationId;
|
||||||
|
|
||||||
|
// 验证状态查询URL格式正确
|
||||||
|
assertEquals("https://api.coze.cn/v3/chat/retrieve?chat_id=test_chat_id&conversation_id=test_conversation_id",
|
||||||
|
statusUrl, "状态查询URL应该正确构建");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("测试消息查询URL的构建逻辑")
|
||||||
|
public void testMessageQueryUrlConstruction() throws Exception {
|
||||||
|
Method extractBaseUrlMethod = AiChatServiceImpl.class.getDeclaredMethod("extractBaseUrl", String.class);
|
||||||
|
extractBaseUrlMethod.setAccessible(true);
|
||||||
|
|
||||||
|
// 创建配置对象
|
||||||
|
AiConfig config = AiConfig.builder()
|
||||||
|
.apiBaseUrl("https://api.coze.cn/v3/chat")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String baseUrl = (String) extractBaseUrlMethod.invoke(aiChatService, config.getApiBaseUrl());
|
||||||
|
String chatId = "test_chat_id";
|
||||||
|
String conversationId = "test_conversation_id";
|
||||||
|
|
||||||
|
// 模拟消息查询URL构建
|
||||||
|
String messagesUrl = baseUrl + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id=" + conversationId;
|
||||||
|
|
||||||
|
// 验证消息查询URL格式正确
|
||||||
|
assertEquals("https://api.coze.cn/v3/chat/message/list?chat_id=test_chat_id&conversation_id=test_conversation_id",
|
||||||
|
messagesUrl, "消息查询URL应该正确构建");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("测试不同格式的apiBaseUrl")
|
||||||
|
public void testDifferentApiBaseUrlFormats() throws Exception {
|
||||||
|
Method getApiPathMethod = AiChatServiceImpl.class.getDeclaredMethod("getApiPath", AiConfig.class);
|
||||||
|
Method extractBaseUrlMethod = AiChatServiceImpl.class.getDeclaredMethod("extractBaseUrl", String.class);
|
||||||
|
getApiPathMethod.setAccessible(true);
|
||||||
|
extractBaseUrlMethod.setAccessible(true);
|
||||||
|
|
||||||
|
// 测试场景1:标准Coze API URL
|
||||||
|
AiConfig config1 = AiConfig.builder()
|
||||||
|
.apiBaseUrl("https://api.coze.cn/v3/chat")
|
||||||
|
.build();
|
||||||
|
String apiPath1 = (String) getApiPathMethod.invoke(aiChatService, config1);
|
||||||
|
String fullUrl1 = config1.getApiBaseUrl() + apiPath1;
|
||||||
|
assertEquals("https://api.coze.cn/v3/chat", fullUrl1);
|
||||||
|
|
||||||
|
// 测试场景2:自定义API URL
|
||||||
|
AiConfig config2 = AiConfig.builder()
|
||||||
|
.apiBaseUrl("https://custom-api.example.com/ai/chat")
|
||||||
|
.build();
|
||||||
|
String apiPath2 = (String) getApiPathMethod.invoke(aiChatService, config2);
|
||||||
|
String fullUrl2 = config2.getApiBaseUrl() + apiPath2;
|
||||||
|
assertEquals("https://custom-api.example.com/ai/chat", fullUrl2);
|
||||||
|
|
||||||
|
// 测试场景3:本地开发环境URL
|
||||||
|
AiConfig config3 = AiConfig.builder()
|
||||||
|
.apiBaseUrl("http://localhost:8080/api/v1/chat")
|
||||||
|
.build();
|
||||||
|
String apiPath3 = (String) getApiPathMethod.invoke(aiChatService, config3);
|
||||||
|
String fullUrl3 = config3.getApiBaseUrl() + apiPath3;
|
||||||
|
assertEquals("http://localhost:8080/api/v1/chat", fullUrl3);
|
||||||
|
|
||||||
|
// 验证extractBaseUrl对这些URL的处理
|
||||||
|
String baseUrl1 = (String) extractBaseUrlMethod.invoke(aiChatService, config1.getApiBaseUrl());
|
||||||
|
assertEquals("https://api.coze.cn", baseUrl1);
|
||||||
|
|
||||||
|
String baseUrl2 = (String) extractBaseUrlMethod.invoke(aiChatService, config2.getApiBaseUrl());
|
||||||
|
assertEquals("https://custom-api.example.com", baseUrl2);
|
||||||
|
|
||||||
|
String baseUrl3 = (String) extractBaseUrlMethod.invoke(aiChatService, config3.getApiBaseUrl());
|
||||||
|
assertEquals("http://localhost:8080", baseUrl3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("测试边界情况")
|
||||||
|
public void testEdgeCases() throws Exception {
|
||||||
|
Method extractBaseUrlMethod = AiChatServiceImpl.class.getDeclaredMethod("extractBaseUrl", String.class);
|
||||||
|
extractBaseUrlMethod.setAccessible(true);
|
||||||
|
|
||||||
|
// 测试只有协议和域名的URL
|
||||||
|
String url1 = "https://api.coze.cn";
|
||||||
|
String result1 = (String) extractBaseUrlMethod.invoke(aiChatService, url1);
|
||||||
|
assertEquals("https://api.coze.cn", result1);
|
||||||
|
|
||||||
|
// 测试带多级路径的URL
|
||||||
|
String url2 = "https://api.coze.cn/v3/chat/stream";
|
||||||
|
String result2 = (String) extractBaseUrlMethod.invoke(aiChatService, url2);
|
||||||
|
assertEquals("https://api.coze.cn", result2);
|
||||||
|
|
||||||
|
// 测试带查询参数的URL(虽然不应该出现在apiBaseUrl中)
|
||||||
|
String url3 = "https://api.coze.cn/v3/chat?version=1";
|
||||||
|
String result3 = (String) extractBaseUrlMethod.invoke(aiChatService, url3);
|
||||||
|
assertEquals("https://api.coze.cn", result3);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -262,8 +262,8 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-form-item label="API基础URL" prop="apiBaseUrl">
|
<el-form-item label="API完整URL" prop="apiBaseUrl">
|
||||||
<el-input v-model="formData.apiBaseUrl" placeholder="请输入API基础URL" />
|
<el-input v-model="formData.apiBaseUrl" placeholder="请输入完整的API URL,如:https://api.coze.cn/v3/chat" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="API访问令牌" prop="apiToken">
|
<el-form-item label="API访问令牌" prop="apiToken">
|
||||||
@@ -512,7 +512,7 @@
|
|||||||
<el-descriptions-item label="服务提供商">{{ getProviderLabel(viewData.provider) }}</el-descriptions-item>
|
<el-descriptions-item label="服务提供商">{{ getProviderLabel(viewData.provider) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="使用场景">{{ getUsageScenarioLabel(viewData.usageScenario) }}</el-descriptions-item>
|
<el-descriptions-item label="使用场景">{{ getUsageScenarioLabel(viewData.usageScenario) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="环境">{{ getEnvironmentLabel(viewData.environment || '') }}</el-descriptions-item>
|
<el-descriptions-item label="环境">{{ getEnvironmentLabel(viewData.environment || '') }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="API基础URL">{{ viewData.apiBaseUrl }}</el-descriptions-item>
|
<el-descriptions-item label="API完整URL">{{ viewData.apiBaseUrl }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="API令牌">{{ viewData.apiToken }}</el-descriptions-item>
|
<el-descriptions-item label="API令牌">{{ viewData.apiToken }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="模型名称">{{ viewData.modelName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="模型名称">{{ viewData.modelName || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="优先级">{{ viewData.priority || 0 }}</el-descriptions-item>
|
<el-descriptions-item label="优先级">{{ viewData.priority || 0 }}</el-descriptions-item>
|
||||||
@@ -749,7 +749,7 @@ const formRules: FormRules = {
|
|||||||
configKey: [{ required: true, message: '请输入配置键值', trigger: 'blur' }],
|
configKey: [{ required: true, message: '请输入配置键值', trigger: 'blur' }],
|
||||||
configType: [{ required: true, message: '请选择配置类型', trigger: 'change' }],
|
configType: [{ required: true, message: '请选择配置类型', trigger: 'change' }],
|
||||||
provider: [{ required: true, message: '请选择服务提供商', trigger: 'change' }],
|
provider: [{ required: true, message: '请选择服务提供商', trigger: 'change' }],
|
||||||
apiBaseUrl: [{ required: true, message: '请输入API基础URL', trigger: 'blur' }],
|
apiBaseUrl: [{ required: true, message: '请输入完整的API URL', trigger: 'blur' }],
|
||||||
apiToken: [{ required: true, message: '请输入API访问令牌', trigger: 'blur' }],
|
apiToken: [{ required: true, message: '请输入API访问令牌', trigger: 'blur' }],
|
||||||
usageScenario: [{ required: true, message: '请选择使用场景', trigger: 'change' }]
|
usageScenario: [{ required: true, message: '请选择使用场景', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
@@ -1081,8 +1081,8 @@ const handleDialogClose = () => {
|
|||||||
|
|
||||||
// 初始化测试数据
|
// 初始化测试数据
|
||||||
const initTestData = (config: AiConfig) => {
|
const initTestData = (config: AiConfig) => {
|
||||||
// 构建请求URL
|
// apiBaseUrl已经是完整的API URL,直接使用
|
||||||
testRequest.url = config.apiBaseUrl + '/v3/chat'
|
testRequest.url = config.apiBaseUrl
|
||||||
|
|
||||||
// 构建请求头
|
// 构建请求头
|
||||||
const headers = {
|
const headers = {
|
||||||
|
|||||||
Reference in New Issue
Block a user