'use client'; import React, { useState, useEffect } from 'react'; // 验证码测试工具 - 专注于验证功能的测试 // 注意:尽管控制台显示NextAuth错误,但这不影响本工具的核心功能 export default function VerifyCodeTest() { // 状态管理 const [email, setEmail] = useState(''); const [isSending, setIsSending] = useState(false); const [message, setMessage] = useState(''); const [messageType, setMessageType] = useState<'success' | 'error' | 'info'>('info'); const [logs, setLogs] = useState([]); const [testMode, setTestMode] = useState<'real' | 'simulation'>('simulation'); // 添加CSS样式 useEffect(() => { // 避免水合错误的样式注入方式 const styleId = 'verify-code-test-styles'; if (!document.getElementById(styleId)) { const style = document.createElement('style'); style.id = styleId; style.textContent = ` .verify-code-container { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-height: 100vh; padding: 2rem; display: flex; flex-direction: column; align-items: center; background: linear-gradient(to bottom, #f0f4f8, #e2e8f0); } .verify-code-header { text-align: center; margin-bottom: 2rem; } .verify-code-header h1 { color: #3730a3; font-size: 2rem; margin-bottom: 0.5rem; } .verify-code-header p { color: #6b7280; font-size: 1rem; } .verify-code-card { background: white; border-radius: 0.5rem; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); padding: 1.5rem; width: 100%; max-width: 450px; margin-bottom: 1.5rem; } .verify-code-form-group { margin-bottom: 1rem; } .verify-code-form-group label { display: block; margin-bottom: 0.5rem; font-weight: 500; color: #374151; } .verify-code-form-group input[type="email"] { width: 100%; padding: 0.75rem; border: 1px solid #d1d5db; border-radius: 0.375rem; font-size: 1rem; } .verify-code-form-group input[type="email"]:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); } .verify-code-btn { width: 100%; padding: 0.75rem; border: none; border-radius: 0.375rem; font-weight: 500; font-size: 1rem; cursor: pointer; transition: background-color 0.2s; } .verify-code-btn-primary { background-color: #3b82f6; color: white; } .verify-code-btn-primary:hover:not(:disabled) { background-color: #2563eb; } .verify-code-btn-primary:disabled { background-color: #d1d5db; color: #9ca3af; cursor: not-allowed; } .verify-code-message { margin-top: 1rem; padding: 0.75rem; border-radius: 0.375rem; border: 1px solid; } .verify-code-message-success { background-color: #dcfce7; color: #15803d; border-color: #bbf7d0; } .verify-code-message-error { background-color: #fee2e2; color: #b91c1c; border-color: #fecaca; } .verify-code-info-box { margin-top: 1rem; padding: 1rem; background-color: #eff6ff; border: 1px solid #dbeafe; border-radius: 0.375rem; font-size: 0.9rem; } .verify-code-info-box h3 { margin-top: 0; color: #1e40af; font-size: 1rem; } .verify-code-info-box p { margin-bottom: 0.5rem; color: #374151; } .verify-code-info-box ul { margin-top: 0.5rem; margin-bottom: 0; padding-left: 1.5rem; } .verify-code-test-mode { margin-top: 1rem; } .verify-code-test-mode-options { display: flex; gap: 1rem; margin-bottom: 0.5rem; } .verify-code-test-mode-options label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; } .verify-code-test-mode-description { font-size: 0.8rem; color: #6b7280; } .verify-code-logs-container { width: 100%; max-width: 600px; background: white; border-radius: 0.5rem; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); padding: 1.5rem; } .verify-code-logs-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; } .verify-code-logs-header h3 { margin: 0; color: #1f2937; font-size: 1.1rem; } .verify-code-clear-logs-btn { background: none; border: none; color: #3b82f6; cursor: pointer; font-size: 0.85rem; } .verify-code-clear-logs-btn:hover { text-decoration: underline; } .verify-code-logs { background-color: #f9fafb; padding: 1rem; border-radius: 0.375rem; max-height: 250px; overflow-y: auto; font-family: 'Courier New', monospace; font-size: 0.8rem; line-height: 1.4; } .verify-code-logs-empty { color: #9ca3af; font-style: italic; } `; document.head.appendChild(style); } return () => { const style = document.getElementById(styleId); if (style) { style.remove(); } }; }, []); // 添加日志 const addLog = (text: string) => { setLogs(prev => [...prev, `[${new Date().toLocaleTimeString()}] ${text}`]); }; // 清除日志 const clearLogs = () => { setLogs([]); }; // 发送验证码 const sendVerificationCode = async () => { // 重置状态 setMessage(''); setLogs([]); setIsSending(true); addLog('开始发送验证码...'); addLog(`测试模式: ${testMode === 'real' ? '真实API调用' : '本地模拟'}`); // 验证邮箱格式 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setMessageType('error'); setMessage('请输入有效的邮箱地址'); setIsSending(false); return; } // 模拟模式 if (testMode === 'simulation') { try { addLog(`模拟发送验证码到: ${email}`); // 模拟后端处理流程 addLog('模拟验证邮箱格式...'); await new Promise(resolve => setTimeout(resolve, 200)); addLog('模拟生成6位数字验证码...'); await new Promise(resolve => setTimeout(resolve, 200)); // 生成测试验证码 const testCode = Math.floor(100000 + Math.random() * 900000).toString(); addLog(`模拟验证码已生成: ${testCode}`); addLog('模拟存储验证码到Redis (10分钟有效期)...'); await new Promise(resolve => setTimeout(resolve, 200)); addLog('模拟发送HTML格式邮件...'); await new Promise(resolve => setTimeout(resolve, 200)); addLog('模拟返回成功响应'); setMessageType('success'); setMessage(`模拟发送成功!测试验证码: ${testCode}`); } catch (error) { addLog(`模拟异常: ${error instanceof Error ? error.message : String(error)}`); setMessageType('error'); setMessage(`模拟失败: ${error instanceof Error ? error.message : '未知错误'}`); } finally { setIsSending(false); } return; } try { // 根据后端代码分析,使用正确的API接口格式 const API_ENDPOINTS = [ { name: 'API v1版本', url: `${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/send-code` } ]; // 测试多个可能的端点 for (const endpoint of API_ENDPOINTS) { try { // 根据后端代码分析,需要包含type参数 const requestBody = { email, type: 'register' // 默认使用register类型,可以根据需要修改为reset_password或change_email }; addLog(`\n=== 测试 ${endpoint.name} ===`); addLog(`请求URL: ${endpoint.url}`); addLog(`请求参数: ${JSON.stringify(requestBody)}`); // 发送请求 const startTime = Date.now(); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 8000); // 8秒超时 const response = await fetch(endpoint.url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody), signal: controller.signal, credentials: 'include' }); clearTimeout(timeoutId); const endTime = Date.now(); addLog(`响应状态码: ${response.status}`); addLog(`响应时间: ${endTime - startTime}ms`); // 解析响应 const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { const data = await response.json(); addLog(`响应数据: ${JSON.stringify(data)}`); if (response.ok) { setMessageType('success'); setMessage(`验证码发送成功,请检查您的邮箱 (使用 ${endpoint.name})`); setIsSending(false); return; } else { addLog(`请求失败: ${data.message || response.status}`); } } else { const text = await response.text(); addLog(`非JSON响应: ${text.substring(0, 150)}${text.length > 150 ? '...' : ''}`); } } catch (fetchError) { if (fetchError instanceof Error && fetchError.name === 'AbortError') { addLog('请求超时 (8秒)'); } else { addLog(`请求错误: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}`); } } } // 诊断信息 addLog('\n=== 诊断信息 ==='); addLog('1. 后端API网关可能未启动或网络不可达'); addLog('2. 请检查API端点地址是否正确'); addLog('3. 根据架构文档,端点应位于auth_handler.go中'); addLog('4. 验证码服务使用Redis存储(10分钟有效期)'); addLog('5. 建议使用模拟模式测试前端功能'); setMessageType('error'); setMessage('所有API端点连接失败,请检查后端服务状态'); } catch (error) { addLog(`系统异常: ${error instanceof Error ? error.message : String(error)}`); setMessageType('error'); setMessage(`系统错误: ${error instanceof Error ? error.message : '未知错误'}`); } finally { setIsSending(false); } }; return (
{/* 页面标题 */}

验证码测试工具

测试验证码发送功能,基于提供的后端架构文档

{/* 测试表单 */}

发送验证码

{/* 邮箱输入 */}
setEmail(e.target.value)} placeholder="请输入邮箱地址" disabled={isSending} />
{/* 发送按钮 */} {/* 消息显示 */} {message && (
{message}
)} {/* 后端架构信息 */}

后端API接口信息

  • API端点: POST /api/v1/auth/send-code
  • 请求参数: {'{"email": "user@example.com", "type": "register|reset_password|change_email"}'}
  • 验证码规则: 6位数字,Redis存储(10分钟有效期)
  • 限制: 每分钟只能发送1次
  • 实现文件: internal/handler/auth_handler.go 和 internal/service/verification_service.go
{/* 测试模式选择 */}

API调用: 真实调用后端API接口
本地模拟: 在前端模拟验证码发送流程

{/* 测试日志 */}

测试日志

{logs.length > 0 && ( )}
{logs.length === 0 ? (

暂无日志

) : ( logs.map((log, index) => (
{log}
)) )}
); }