From 8ddd99f54f1c66136401fb4eb688cce1fd2f68fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Tue, 7 Apr 2026 09:57:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(e2e):=20=E6=B7=BB=E5=8A=A0=E7=BD=91?= =?UTF-8?q?=E5=85=B3=E6=9C=8D=E5=8A=A1=E5=90=AF=E5=8A=A8=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=9E=B6=E6=9E=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - 前端直接访问后端(8084),未通过网关 - 网关服务(8080)未启动 - 导致认证和授权流程失败 修复: 1. 前端配置: - 修改 vite.config.ts,代理目标改为网关(8080) - 支持环境变量 VITE_API_TARGET 2. 测试环境: - 添加网关服务启动逻辑 - 添加 waitForGatewayReady 健康检查 - 添加网关服务停止逻辑 - 修改 cleanupTestData API 地址(8084 -> 8080) 架构: - 前端(3002)-> 网关(8080)-> 后端(8084) - 网关负责:JWT认证、RBAC授权、限流、熔断 --- novalon-manage-web/e2e/global-setup.ts | 129 +++++++++++++++++++++++-- novalon-manage-web/vite.config.ts | 2 +- 2 files changed, 124 insertions(+), 7 deletions(-) diff --git a/novalon-manage-web/e2e/global-setup.ts b/novalon-manage-web/e2e/global-setup.ts index 47924cf..8950036 100644 --- a/novalon-manage-web/e2e/global-setup.ts +++ b/novalon-manage-web/e2e/global-setup.ts @@ -8,6 +8,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); let backendProcess: ChildProcess | null = null; +let gatewayProcess: ChildProcess | null = null; let frontendProcess: ChildProcess | null = null; let healthCheckInterval: NodeJS.Timeout | null = null; @@ -118,6 +119,72 @@ async function globalSetup(config: FullConfig) { console.log('⏳ 等待后端服务就绪...'); await waitForBackendReady(); + const gatewayDir = path.resolve(__dirname, '../../novalon-manage-api/manage-gateway'); + const gatewayJarFile = path.join(gatewayDir, 'target/manage-gateway-1.0.0.jar'); + + let gatewayCommand: string; + let gatewayArgs: string[]; + + if (existsSync(gatewayJarFile)) { + console.log('🚪 使用JAR文件启动网关服务...'); + console.log(` JAR文件: ${gatewayJarFile}`); + gatewayCommand = 'java'; + gatewayArgs = [ + '-jar', + gatewayJarFile, + '--spring.profiles.active=test', + '-Xms128m', + '-Xmx256m' + ]; + } else { + console.log('🚪 使用Maven启动网关服务...'); + console.log(' 提示: 运行 "mvn clean package -DskipTests" 构建JAR文件以获得更快的启动速度'); + gatewayCommand = 'mvn'; + gatewayArgs = ['spring-boot:run', '-Dspring-boot.run.profiles=test']; + } + + console.log(` 目录: ${gatewayDir}`); + console.log(` 命令: ${gatewayCommand} ${gatewayArgs.join(' ')}`); + + gatewayProcess = spawn(gatewayCommand, gatewayArgs, { + cwd: gatewayDir, + stdio: 'pipe', + shell: true, + detached: false, + env: { ...process.env, SPRING_PROFILES_ACTIVE: 'test' } + }); + + if (gatewayProcess.stdout) { + gatewayProcess.stdout.on('data', (data) => { + const output = data.toString(); + if (output.includes('Started GatewayApplication') || output.includes('Netty started on port')) { + console.log('✅ 网关服务启动成功'); + } + }); + } + + if (gatewayProcess.stderr) { + gatewayProcess.stderr.on('data', (data) => { + const output = data.toString(); + if (output.includes('ERROR') || output.includes('Exception')) { + console.error('❌ 网关服务启动错误:', output); + } + }); + } + + gatewayProcess.on('error', (error) => { + console.error('❌ 网关服务启动失败:', error); + }); + + gatewayProcess.on('exit', (code, signal) => { + if (code !== 0 && code !== null) { + console.error(`❌ 网关服务异常退出,退出码: ${code}, 信号: ${signal}`); + } + }); + + console.log('⏳ 等待网关服务就绪...'); + await waitForGatewayReady(); + const frontendDir = path.resolve(__dirname, '..'); console.log('🌐 启动前端服务...'); console.log(` 目录: ${frontendDir}`); @@ -195,6 +262,32 @@ async function waitForBackendReady(): Promise { throw new Error('❌ 后端服务启动超时'); } +async function waitForGatewayReady(): Promise { + const maxRetries = 60; + const retryInterval = 1000; + + for (let i = 0; i < maxRetries; i++) { + try { + const response = await fetch('http://localhost:8080/actuator/health'); + if (response.ok) { + const data = await response.json(); + if (data.status === 'UP') { + console.log(`✅ 网关服务健康检查通过 (尝试 ${i + 1}/${maxRetries})`); + return; + } + } + } catch (error) { + // 服务还未就绪,继续等待 + } + + if (i < maxRetries - 1) { + await new Promise(resolve => setTimeout(resolve, retryInterval)); + } + } + + throw new Error('❌ 网关服务启动超时'); +} + async function waitForFrontendReady(): Promise { const maxRetries = 60; const retryInterval = 1000; @@ -222,8 +315,8 @@ async function waitForFrontendReady(): Promise { async function cleanupTestData(): Promise { try { - // 登录获取token - const loginResponse = await fetch('http://localhost:8084/api/auth/login', { + // 登录获取token(通过网关) + const loginResponse = await fetch('http://localhost:8080/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -243,7 +336,7 @@ async function cleanupTestData(): Promise { const token = loginData.token; // 获取所有用户 - const usersResponse = await fetch('http://localhost:8084/api/users', { + const usersResponse = await fetch('http://localhost:8080/api/users', { headers: { 'Authorization': `Bearer ${token}` } @@ -256,7 +349,7 @@ async function cleanupTestData(): Promise { for (const user of users) { if (user.id > 10) { try { - await fetch(`http://localhost:8084/api/users/${user.id}`, { + await fetch(`http://localhost:8080/api/users/${user.id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` @@ -271,7 +364,7 @@ async function cleanupTestData(): Promise { } // 获取所有角色 - const rolesResponse = await fetch('http://localhost:8084/api/roles', { + const rolesResponse = await fetch('http://localhost:8080/api/roles', { headers: { 'Authorization': `Bearer ${token}` } @@ -284,7 +377,7 @@ async function cleanupTestData(): Promise { for (const role of roles) { if (role.id > 4) { try { - await fetch(`http://localhost:8084/api/roles/${role.id}`, { + await fetch(`http://localhost:8080/api/roles/${role.id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` @@ -334,6 +427,30 @@ async function globalTeardown() { }); } + if (gatewayProcess) { + console.log('🛑 停止网关服务...'); + gatewayProcess.kill('SIGTERM'); + + await new Promise((resolve) => { + if (gatewayProcess) { + gatewayProcess.on('exit', () => { + console.log('✅ 网关服务已停止'); + resolve(); + }); + + setTimeout(() => { + if (gatewayProcess) { + gatewayProcess.kill('SIGKILL'); + console.log('⚠️ 强制停止网关服务'); + resolve(); + } + }, 10000); + } else { + resolve(); + } + }); + } + if (frontendProcess) { console.log('🛑 停止前端服务...'); frontendProcess.kill('SIGTERM'); diff --git a/novalon-manage-web/vite.config.ts b/novalon-manage-web/vite.config.ts index 7c9eb4b..efa547a 100644 --- a/novalon-manage-web/vite.config.ts +++ b/novalon-manage-web/vite.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ strictPort: true, proxy: { '/api': { - target: 'http://localhost:8084', + target: process.env.VITE_API_TARGET || 'http://localhost:8080', changeOrigin: true, secure: false }