feat: AI 场景路由、ASR 服务及前后端全链路同步
- 新增 AI 场景路由控制器和管理接口 - 新增 ASR 语音识别服务及前后端集成 - 同步 AI Runtime 客户端到 Web/小程序/Life-Script - 完善 AI 配置测试修复和管理后台路由配置 - 新增数据库迁移脚本 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.service.AuthService;
|
||||
import com.emotion.service.TokenService;
|
||||
import com.emotion.util.JwtUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@@ -17,7 +21,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
* @author huazhongmin
|
||||
* @date 2025-07-26
|
||||
*/
|
||||
@WebMvcTest(AuthController.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc(addFilters = false)
|
||||
public class AuthControllerTest {
|
||||
|
||||
@Autowired
|
||||
@@ -26,13 +31,28 @@ public class AuthControllerTest {
|
||||
@MockBean
|
||||
private AuthService authService;
|
||||
|
||||
@MockBean
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@MockBean
|
||||
private TokenService tokenService;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(jwtUtil.validateToken("test-token")).thenReturn(true);
|
||||
when(jwtUtil.getUserIdFromToken("test-token")).thenReturn("test-user");
|
||||
when(jwtUtil.getUsernameFromToken("test-token")).thenReturn("tester");
|
||||
when(jwtUtil.getUserTypeFromToken("test-token")).thenReturn("user");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckAccountExists() throws Exception {
|
||||
// 模拟账户存在的情况
|
||||
when(authService.existsByAccount("existingUser")).thenReturn(true);
|
||||
|
||||
mockMvc.perform(get("/auth/check-account")
|
||||
.param("account", "existingUser"))
|
||||
mockMvc.perform(get("/api/auth/checkAccount").contextPath("/api")
|
||||
.param("account", "existingUser")
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data").value(true));
|
||||
@@ -43,8 +63,9 @@ public class AuthControllerTest {
|
||||
// 模拟账户不存在的情况
|
||||
when(authService.existsByAccount("nonExistingUser")).thenReturn(false);
|
||||
|
||||
mockMvc.perform(get("/auth/check-account")
|
||||
.param("account", "nonExistingUser"))
|
||||
mockMvc.perform(get("/api/auth/checkAccount").contextPath("/api")
|
||||
.param("account", "nonExistingUser")
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data").value(false));
|
||||
@@ -55,8 +76,9 @@ public class AuthControllerTest {
|
||||
// 模拟邮箱存在的情况
|
||||
when(authService.existsByEmail("existing@example.com")).thenReturn(true);
|
||||
|
||||
mockMvc.perform(get("/auth/check-email")
|
||||
.param("email", "existing@example.com"))
|
||||
mockMvc.perform(get("/api/auth/checkEmail").contextPath("/api")
|
||||
.param("email", "existing@example.com")
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data").value(true));
|
||||
@@ -67,8 +89,9 @@ public class AuthControllerTest {
|
||||
// 模拟邮箱不存在的情况
|
||||
when(authService.existsByEmail("nonexisting@example.com")).thenReturn(false);
|
||||
|
||||
mockMvc.perform(get("/auth/check-email")
|
||||
.param("email", "nonexisting@example.com"))
|
||||
mockMvc.perform(get("/api/auth/checkEmail").contextPath("/api")
|
||||
.param("email", "nonexisting@example.com")
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data").value(false));
|
||||
@@ -79,8 +102,9 @@ public class AuthControllerTest {
|
||||
// 模拟手机号存在的情况
|
||||
when(authService.existsByPhone("13800138000")).thenReturn(true);
|
||||
|
||||
mockMvc.perform(get("/auth/check-phone")
|
||||
.param("phone", "13800138000"))
|
||||
mockMvc.perform(get("/api/auth/checkPhone").contextPath("/api")
|
||||
.param("phone", "13800138000")
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data").value(true));
|
||||
@@ -91,8 +115,9 @@ public class AuthControllerTest {
|
||||
// 模拟手机号不存在的情况
|
||||
when(authService.existsByPhone("13900139000")).thenReturn(false);
|
||||
|
||||
mockMvc.perform(get("/auth/check-phone")
|
||||
.param("phone", "13900139000"))
|
||||
mockMvc.perform(get("/api/auth/checkPhone").contextPath("/api")
|
||||
.param("phone", "13900139000")
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data").value(false));
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
@@ -35,6 +36,8 @@ public class CozeWorkflowIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
private AiChatServiceImpl aiChatServiceImpl;
|
||||
|
||||
@Autowired
|
||||
private AiConfigService aiConfigService;
|
||||
@@ -49,6 +52,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
random = new Random();
|
||||
aiChatServiceImpl = AopTestUtils.getTargetObject(aiChatService);
|
||||
}
|
||||
|
||||
// ==================== Property 1: Request Format Correctness ====================
|
||||
@@ -82,7 +86,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> requestBody = (Map<String, Object>) buildWorkflowRequestMethod.invoke(
|
||||
aiChatService, config, parameters, userId);
|
||||
aiChatServiceImpl, config, parameters, userId);
|
||||
|
||||
// 验证必需字段
|
||||
// 2.1: workflow_id - 应该与数据库配置一致
|
||||
@@ -155,7 +159,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
parseMethod.setAccessible(true);
|
||||
|
||||
java.util.stream.Stream<String> lines = sseResponse.lines();
|
||||
String result = (String) parseMethod.invoke(aiChatService, lines);
|
||||
String result = (String) parseMethod.invoke(aiChatServiceImpl, lines);
|
||||
|
||||
// 验证正确提取output内容
|
||||
assertEquals("这是AI生成的内容", result,
|
||||
@@ -184,7 +188,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
parseMethod.setAccessible(true);
|
||||
|
||||
java.util.stream.Stream<String> lines = sseResponse.lines();
|
||||
String result = (String) parseMethod.invoke(aiChatService, lines);
|
||||
String result = (String) parseMethod.invoke(aiChatServiceImpl, lines);
|
||||
|
||||
// 验证正确提取随机output内容
|
||||
assertEquals(randomOutput, result,
|
||||
@@ -218,7 +222,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
parseMethod.setAccessible(true);
|
||||
|
||||
java.util.stream.Stream<String> lines = sseResponse.lines();
|
||||
String result = (String) parseMethod.invoke(aiChatService, lines);
|
||||
String result = (String) parseMethod.invoke(aiChatServiceImpl, lines);
|
||||
|
||||
// 验证只提取End节点的内容
|
||||
assertEquals("最终输出内容", result,
|
||||
@@ -244,7 +248,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
parseMethod.setAccessible(true);
|
||||
|
||||
java.util.stream.Stream<String> lines = sseResponse.lines();
|
||||
String result = (String) parseMethod.invoke(aiChatService, lines);
|
||||
String result = (String) parseMethod.invoke(aiChatServiceImpl, lines);
|
||||
|
||||
// 当content不是JSON或没有output字段时,应返回原始content
|
||||
assertEquals("直接内容,没有output字段", result,
|
||||
@@ -276,7 +280,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> mergedParams = (Map<String, Object>) mergeMethod.invoke(
|
||||
aiChatService, config, runtimeParams);
|
||||
aiChatServiceImpl, config, runtimeParams);
|
||||
|
||||
// 验证运行时参数被正确设置
|
||||
assertEquals(runtimeInput, mergedParams.get("input"),
|
||||
@@ -295,7 +299,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
extractMethod.setAccessible(true);
|
||||
|
||||
String content = "{\"output\":\"提取的内容\"}";
|
||||
String result = (String) extractMethod.invoke(aiChatService, content);
|
||||
String result = (String) extractMethod.invoke(aiChatServiceImpl, content);
|
||||
|
||||
assertEquals("提取的内容", result, "应正确提取output字段");
|
||||
}
|
||||
@@ -308,7 +312,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
extractMethod.setAccessible(true);
|
||||
|
||||
String content = "这不是JSON内容";
|
||||
String result = (String) extractMethod.invoke(aiChatService, content);
|
||||
String result = (String) extractMethod.invoke(aiChatServiceImpl, content);
|
||||
|
||||
assertEquals("这不是JSON内容", result, "非JSON内容应原样返回");
|
||||
}
|
||||
@@ -324,7 +328,7 @@ public class CozeWorkflowIntegrationTest {
|
||||
String randomOutput = "随机输出_" + UUID.randomUUID().toString();
|
||||
String content = "{\"output\":\"" + randomOutput + "\"}";
|
||||
|
||||
String result = (String) extractMethod.invoke(aiChatService, content);
|
||||
String result = (String) extractMethod.invoke(aiChatServiceImpl, content);
|
||||
|
||||
assertEquals(randomOutput, result, "应正确提取随机生成的output内容");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user