合并login

Change-Id: Ie06ed019cbb00d52e0b9e1f3c7a56c947b57a42c
diff --git a/Merge/front/src/pages/TestDashboard/TestDashboard.js b/Merge/front/src/pages/TestDashboard/TestDashboard.js
new file mode 100644
index 0000000..0cc9914
--- /dev/null
+++ b/Merge/front/src/pages/TestDashboard/TestDashboard.js
@@ -0,0 +1,295 @@
+import React, { useState, useEffect } from 'react';
+import { Card, Button, Descriptions, Avatar, Tag, Space, message } from 'antd';
+import { UserOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons';
+import { getUserInfo, getAuthToken, isLoggedIn, saveAuthInfo, createAuthenticatedRequest } from '../../utils/auth';
+import LogoutButton from '../../components/LogoutButton';
+import './TestDashboard.css';
+
+const TestDashboard = () => {
+  const [userInfo, setUserInfo] = useState(null);
+  const [token, setToken] = useState(null);
+  const [loading, setLoading] = useState(false);
+  const [jwtTestLoading, setJwtTestLoading] = useState(false);
+
+  useEffect(() => {
+    // 检查用户是否已登录
+    if (!isLoggedIn()) {
+      window.location.href = '/';
+      return;
+    }
+
+    // 获取用户信息和token
+    const authToken = getAuthToken();
+    const authUserInfo = getUserInfo();
+    
+    setToken(authToken);
+    setUserInfo(authUserInfo);
+  }, []);
+
+  const handleRefreshProfile = async () => {
+    if (!token) {
+      message.error('未找到认证token');
+      return;
+    }
+
+    setLoading(true);
+    try {
+      const response = await fetch('http://10.126.59.25:8082/profile', createAuthenticatedRequest());
+
+      const result = await response.json();
+      
+      if (result.success) {
+        setUserInfo(result.user);
+        // 更新存储的用户信息,保持原有的存储方式(localStorage或sessionStorage)
+        const isRemembered = localStorage.getItem('authToken');
+        saveAuthInfo(token, result.user, !!isRemembered);
+        message.success('用户信息刷新成功');
+      } else {
+        message.error(`获取用户信息失败: ${result.message}`);
+      }
+    } catch (error) {
+      console.error('刷新用户信息失败:', error);
+      message.error('网络连接失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleLogout = async () => {
+    if (!token) {
+      // 清除存储并跳转
+      localStorage.removeItem('authToken');
+      localStorage.removeItem('userInfo');
+      sessionStorage.removeItem('authToken');
+      sessionStorage.removeItem('userInfo');
+      window.location.href = '/';
+      return;
+    }
+
+    try {
+      const response = await fetch('http://10.126.59.25:8082/logout', {
+        method: 'POST',
+        headers: {
+          'Authorization': `Bearer ${token}`,
+          'Content-Type': 'application/json',
+        }
+      });
+
+      const result = await response.json();
+      
+      if (result.success) {
+        message.success('退出登录成功');
+      } else {
+        message.warning(`退出登录: ${result.message}`);
+      }
+    } catch (error) {
+      console.error('退出登录请求失败:', error);
+      message.warning('网络请求失败,但将清除本地数据');
+    } finally {
+      // 无论请求成功与否,都清除本地存储并跳转
+      localStorage.removeItem('authToken');
+      localStorage.removeItem('userInfo');
+      sessionStorage.removeItem('authToken');
+      sessionStorage.removeItem('userInfo');
+      window.location.href = '/';
+    }
+  };
+
+  const handleTestJWT = async () => {
+    if (!token) {
+      message.error('未找到认证token');
+      return;
+    }
+
+    setJwtTestLoading(true);
+    try {
+      const response = await fetch('http://10.126.59.25:8082/test-jwt', {
+        method: 'POST',
+        headers: {
+          'Authorization': `Bearer ${token}`,
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          token: token, // 可选:在请求体中也发送token进行额外验证
+          test_purpose: 'frontend_jwt_test'
+        })
+      });
+
+      const result = await response.json();
+      
+      if (result.success) {
+        message.success(`JWT令牌验证成功!用户: ${result.user.username}`);
+        console.log('JWT验证详细结果:', result);
+        
+        // 如果有额外的token验证结果,也显示出来
+        if (result.additional_token_verification) {
+          console.log('额外token验证:', result.additional_token_verification);
+        }
+      } else {
+        message.error(`JWT令牌验证失败: ${result.message}`);
+      }
+    } catch (error) {
+      console.error('JWT令牌验证失败:', error);
+      message.error('网络连接失败');
+    } finally {
+      setJwtTestLoading(false);
+    }
+  };
+
+  const getRoleColor = (role) => {
+    switch (role) {
+      case 'superadmin':
+        return 'red';
+      case 'admin':
+        return 'orange';
+      case 'user':
+      default:
+        return 'blue';
+    }
+  };
+
+  const getStatusColor = (status) => {
+    switch (status) {
+      case 'active':
+        return 'green';
+      case 'banned':
+        return 'red';
+      case 'muted':
+        return 'orange';
+      default:
+        return 'default';
+    }
+  };
+
+  if (!userInfo) {
+    return (
+      <div className="test-dashboard">
+        <div className="loading-container">
+          <div className="spinner"></div>
+          <p>加载用户信息中...</p>
+        </div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="test-dashboard">
+      <div className="dashboard-header">
+        <h1>测试仪表板</h1>
+        <p>登录成功!以下是从后端返回的用户信息:</p>
+      </div>
+
+      <div className="dashboard-content">
+        <Card
+          title={
+            <Space>
+              <Avatar size={40} icon={<UserOutlined />} src={userInfo.avatar} />
+              <span>用户信息</span>
+            </Space>
+          }
+          extra={
+            <Space>
+              <Button 
+                type="primary" 
+                icon={<ReloadOutlined />}
+                loading={loading}
+                onClick={handleRefreshProfile}
+              >
+                刷新信息
+              </Button>
+              <LogoutButton onLogout={() => window.location.href = '/'} />
+            </Space>
+          }
+          className="user-info-card"
+        >
+          <Descriptions column={2} bordered>
+            <Descriptions.Item label="用户ID">{userInfo.id}</Descriptions.Item>
+            <Descriptions.Item label="用户名">{userInfo.username}</Descriptions.Item>
+            <Descriptions.Item label="邮箱">{userInfo.email}</Descriptions.Item>
+            <Descriptions.Item label="角色">
+              <Tag color={getRoleColor(userInfo.role)}>
+                {userInfo.role}
+              </Tag>
+            </Descriptions.Item>
+            <Descriptions.Item label="账号状态">
+              <Tag color={getStatusColor(userInfo.status)}>
+                {userInfo.status}
+              </Tag>
+            </Descriptions.Item>
+            <Descriptions.Item label="个人简介" span={2}>
+              {userInfo.bio || '暂无个人简介'}
+            </Descriptions.Item>
+            <Descriptions.Item label="创建时间">
+              {userInfo.created_at ? new Date(userInfo.created_at).toLocaleString() : '未知'}
+            </Descriptions.Item>
+            <Descriptions.Item label="更新时间">
+              {userInfo.updated_at ? new Date(userInfo.updated_at).toLocaleString() : '未知'}
+            </Descriptions.Item>
+          </Descriptions>
+        </Card>
+
+        <Card title="登录状态信息" className="login-status-card">
+          <div className="login-status-display">
+            <Descriptions column={1} bordered>
+              <Descriptions.Item label="登录方式">
+                <Tag color={localStorage.getItem('authToken') ? 'green' : 'blue'}>
+                  {localStorage.getItem('authToken') ? '记住我登录 (持久化)' : '普通登录 (会话)'}
+                </Tag>
+              </Descriptions.Item>
+              <Descriptions.Item label="Token存储位置">
+                {localStorage.getItem('authToken') ? 'localStorage (浏览器关闭后仍保持登录)' : 'sessionStorage (浏览器关闭后需重新登录)'}
+              </Descriptions.Item>
+              <Descriptions.Item label="记住的登录信息">
+                {localStorage.getItem('rememberMe') === 'true' ? 
+                  `已保存邮箱: ${localStorage.getItem('rememberedEmail') || '无'}` : 
+                  '未保存登录信息'
+                }
+              </Descriptions.Item>
+            </Descriptions>
+          </div>
+        </Card>
+
+        <Card title="Token信息" className="token-info-card">
+          <div className="token-display">
+            <p><strong>认证Token:</strong></p>
+            <div className="token-text">
+              {token ? `${token.substring(0, 50)}...` : '未找到token'}
+            </div>
+            <p className="token-note">
+              * Token已被安全截断显示,完整token存储在浏览器存储中
+            </p>
+          </div>
+        </Card>
+
+        <Card title="API测试" className="api-test-card">
+          <Space direction="vertical" style={{ width: '100%' }}>
+            <p>您可以使用以下按钮测试不同的API接口:</p>
+            <Space wrap>
+              <Button onClick={handleRefreshProfile} loading={loading}>
+                测试 GET /profile
+              </Button>
+              <Button onClick={handleLogout}>
+                测试 POST /logout
+              </Button>
+              <Button 
+                onClick={handleTestJWT} 
+                loading={jwtTestLoading}
+                type="primary"
+              >
+                测试 POST /test-jwt
+              </Button>
+              <Button 
+                type="dashed"
+                onClick={() => window.open('http://10.126.59.25:8082/health', '_blank')}
+              >
+                测试 GET /health
+              </Button>
+            </Space>
+          </Space>
+        </Card>
+      </div>
+    </div>
+  );
+};
+
+export default TestDashboard;