修复角色中心组件中的类型不匹配错误,改进skinId和capeId的类型处理逻辑

This commit is contained in:
Mikuisnotavailable
2025-10-16 01:59:17 +08:00
parent d9a15dd13d
commit 167c51b20d
21 changed files with 4548 additions and 329 deletions

View File

@@ -0,0 +1,477 @@
'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<string[]>([]);
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;
}
// 真实API调用模式
try {
// 根据后端架构文档中的API网关信息
const API_ENDPOINTS = [
{
name: 'API网关v1版本',
url: 'https://code.littlelan.cn/CarrotSkin/APIgateway/api/v1/auth/send-code'
},
{
name: 'API网关v2版本',
url: 'https://code.littlelan.cn/CarrotSkin/APIgateway/api/v2/auth/send-code'
},
{
name: '直接API',
url: 'https://code.littlelan.cn/CarrotSkin/api/auth/send-code'
}
];
// 测试多个可能的端点
for (const endpoint of API_ENDPOINTS) {
addLog(`\n=== 测试 ${endpoint.name} ===`);
addLog(`请求URL: ${endpoint.url}`);
addLog(`请求参数: {"email": "${email}"}`);
try {
// 发送请求
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({ email }),
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.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 (
<div className="verify-code-container">
{/* 页面标题 */}
<div className="verify-code-header">
<h1></h1>
<p></p>
</div>
{/* 测试表单 */}
<div className="verify-code-card">
<h2></h2>
{/* 邮箱输入 */}
<div className="verify-code-form-group">
<label htmlFor="email"></label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="请输入邮箱地址"
disabled={isSending}
/>
</div>
{/* 发送按钮 */}
<button
onClick={sendVerificationCode}
disabled={isSending || !email}
className="verify-code-btn verify-code-btn-primary"
>
{isSending ? '发送中...' : '发送验证码'}
</button>
{/* 消息显示 */}
{message && (
<div className={`verify-code-message verify-code-message-${messageType}`}>
{message}
</div>
)}
{/* 后端架构信息 */}
<div className="verify-code-info-box">
<h3></h3>
<p></p>
<ul>
<li>认证模块: internal/handler/auth_handler.go</li>
<li>邮箱验证码模块: internal/service/verification_service.go</li>
<li>验证码: 6位数字Redis存储(10)</li>
<li>发送频率: 限制1分钟</li>
<li>邮件格式: HTML格式</li>
</ul>
</div>
{/* 测试模式选择 */}
<div className="verify-code-test-mode">
<label></label>
<div className="verify-code-test-mode-options">
<label>
<input
type="radio"
name="testMode"
value="real"
checked={testMode === 'real'}
onChange={() => setTestMode('real')}
/>
API调用
</label>
<label>
<input
type="radio"
name="testMode"
value="simulation"
checked={testMode === 'simulation'}
onChange={() => setTestMode('simulation')}
/>
</label>
</div>
<p className="verify-code-test-mode-description">
{testMode === 'real'
? '将实际调用后端API发送验证码'
: '模拟验证码发送过程,生成测试验证码'}
</p>
</div>
</div>
{/* 测试日志 */}
<div className="verify-code-logs-container">
<div className="verify-code-logs-header">
<h3></h3>
{logs.length > 0 && (
<button
onClick={clearLogs}
className="verify-code-clear-logs-btn"
>
</button>
)}
</div>
<div className="verify-code-logs">
{logs.length === 0 ? (
<p className="verify-code-logs-empty"></p>
) : (
logs.map((log, index) => (
<div key={index}>{log}</div>
))
)}
</div>
</div>
</div>
);
}