feat(torrents): 优化种子页面功能和交互

- 调整 torrents API 调用,增加更多接口支持
- 优化种子列表展示和操作,支持下载和查看详情
- 新增种子详情页面路由和组件
- 改进上传种子功能,增加表单验证和错误提示
- 优化用户信息展示

Change-Id: I9343f2f446639733ee5800a86bab85a4ac6d1a72
diff --git a/src/features/profile/pages/ProfilePage.jsx b/src/features/profile/pages/ProfilePage.jsx
index d39589b..4b25a98 100644
--- a/src/features/profile/pages/ProfilePage.jsx
+++ b/src/features/profile/pages/ProfilePage.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
 import {
   Typography,
   Card,
@@ -33,15 +33,16 @@
   CameraOutlined
 } from '@ant-design/icons';
 import { useAuth } from '@/features/auth/contexts/AuthContext';
-import { getUserStats, updateUserProfile, uploadAvatar } from '@/api/user';
+import { updateUserProfile, uploadAvatar } from '@/api/user';
 
 const { Title, Text, Paragraph } = Typography;
 
 const ProfilePage = () => {
-  const { user } = useAuth();
+  const { user, updateUserInfo } = useAuth();
   const [loading, setLoading] = useState(false);
   const [editModalVisible, setEditModalVisible] = useState(false);
   const [form] = Form.useForm();
+  const hasUpdatedRef = useRef(false); // 用来追踪是否已经更新过用户信息
 
   // PT站统计数据
   const [ptStats, setPtStats] = useState({
@@ -59,49 +60,83 @@
     hitAndRuns: 0
   });
 
-  // 获取用户统计信息
+  // 页面加载时更新用户信息
   useEffect(() => {
-    if (user?.username) {
-      fetchUserStats();
+    const handleUserInfoUpdate = async () => {
+      if (user?.username && updateUserInfo && !hasUpdatedRef.current) {
+        console.log('页面加载,正在更新用户信息...');
+        hasUpdatedRef.current = true; // 标记为已更新
+        try {
+          await updateUserInfo(user.username);
+          console.log('用户信息更新成功');
+        } catch (error) {
+          console.error('更新用户信息失败:', error);
+          hasUpdatedRef.current = false; // 如果失败,重置标记以便重试
+        }
+      }
+    };
+
+    handleUserInfoUpdate();
+  }, [user?.username, updateUserInfo]); // 依赖用户名和更新函数
+
+  // 用户信息更新后,从用户数据中提取统计信息
+  useEffect(() => {
+    if (user) {
+      console.log('用户数据变化,更新统计信息:', user);
+      updateStatsFromUser(user);
     }
   }, [user]);
 
-  // 监听ptStats的变化
-  useEffect(() => {
-    console.log('ptStats updated:', ptStats);
-  }, [ptStats]);
-
-  const fetchUserStats = async () => {
-    try {
-      setLoading(true);
-      const response = await getUserStats(user.username);
+  // 从用户数据中提取统计信息
+  const updateStatsFromUser = (userData) => {
+    if (userData) {
+      const uploaded = Number(userData.uploaded) || 0;
+      const downloaded = Number(userData.downloaded) || 0;
+      const shareRatio = Number(userData.shareRatio) || 0;
       
-      if (response && response.data) {
-        const newStats = {
-          ...ptStats,
-          ...response.data
-        };
-        setPtStats(newStats);
-      } else {
-        message.error('获取用户统计信息失败:数据格式错误');
-      }
-    } catch (error) {
-      if (error.response) {
-        message.error(error.response.data.message || '获取用户统计信息失败');
-      } else {
-        message.error('获取用户统计信息失败,请检查网络连接');
-      }
-    } finally {
-      setLoading(false);
+      
+      const newStats = {
+        uploadSize: uploaded, // 保存原始字节值
+        downloadSize: downloaded, // 保存原始字节值  
+        ratio: shareRatio,
+        points: Number(userData.points) || 0,
+        userClass: `用户等级`,
+        level: Number(userData.level) || 1
+      };
+      
+      console.log('原始数据:', { uploaded, downloaded, shareRatio });
+      console.log('更新后的统计数据:', newStats);
+      setPtStats(newStats);
     }
   };
 
+
   // 格式化文件大小
-  const formatSize = (sizeInGB) => {
-    if (sizeInGB >= 1024) {
-      return `${(sizeInGB / 1024).toFixed(2)} TB`;
+  const formatSize = (sizeInBytes) => {
+    if (sizeInBytes === 0) {
+      return '0 B';
     }
-    return `${sizeInGB.toFixed(2)} GB`;
+    
+    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
+    const k = 1024;
+    let bytes = Math.abs(sizeInBytes);
+    let unitIndex = 0;
+    
+    // 找到合适的单位
+    while (bytes >= k && unitIndex < units.length - 1) {
+      bytes /= k;
+      unitIndex++;
+    }
+    
+    // 根据大小决定小数位数
+    let decimals = 2;
+    if (bytes >= 100) {
+      decimals = 0; // 大于等于100时不显示小数
+    } else if (bytes >= 10) {
+      decimals = 1; // 10-99时显示1位小数
+    }
+    
+    return `${bytes.toFixed(decimals)} ${units[unitIndex]}`;
   };
 
   // 获取分享率颜色
@@ -150,7 +185,13 @@
       if (response && response.data) {
         message.success('资料更新成功');
         setEditModalVisible(false);
-        // 可以触发AuthContext的用户信息更新
+        // 资料更新成功后刷新用户信息
+        try {
+          await updateUserInfo(user.username);
+          console.log('资料更新后用户信息已刷新');
+        } catch (error) {
+          console.error('资料更新后刷新用户信息失败:', error);
+        }
       } else {
         message.error('更新失败,请重试');
       }
@@ -184,7 +225,13 @@
       const response = await uploadAvatar(formData);
       if (response && response.data) {
         message.success('头像上传成功');
-        // 可以触发AuthContext的用户信息更新或重新获取用户信息
+        // 头像上传成功后更新用户信息
+        try {
+          await updateUserInfo(user.username);
+          console.log('头像上传后用户信息已更新');
+        } catch (error) {
+          console.error('头像上传后更新用户信息失败:', error);
+        }
       } else {
         message.error('头像上传失败');
       }
@@ -207,13 +254,48 @@
     <div className="space-y-6">
       <div className="flex justify-between items-center">
         <Title level={2}>个人资料</Title>
-        <Button 
-          type="primary" 
-          icon={<EditOutlined />} 
-          onClick={showEditModal}
-        >
-          编辑资料
-        </Button>
+        <Space>
+          <Button 
+            icon={<SyncOutlined />} 
+            onClick={async () => {
+              if (!user?.username) {
+                message.error('用户信息不完整,请重新登录');
+                return;
+              }
+              
+              console.log('手动刷新用户信息...');
+              setLoading(true);
+              
+              try {
+                // 重置标记,允许手动更新
+                hasUpdatedRef.current = false;
+                
+                // 调用getUserInfo更新用户信息
+                await updateUserInfo(user.username);
+                console.log('用户信息更新成功');
+                
+                // 统计信息会通过 useEffect 自动更新
+                
+                message.success('用户信息已更新');
+              } catch (error) {
+                console.error('刷新用户信息失败:', error);
+                message.error('刷新失败: ' + (error.message || '网络错误'));
+              } finally {
+                setLoading(false);
+              }
+            }}
+            loading={loading}
+          >
+            刷新信息
+          </Button>
+          <Button 
+            type="primary" 
+            icon={<EditOutlined />} 
+            onClick={showEditModal}
+          >
+            编辑资料
+          </Button>
+        </Space>
       </div>
 
       <Row gutter={[24, 24]}>
@@ -246,7 +328,7 @@
                   color={getUserClassColor(ptStats.userClass)} 
                   className="text-lg px-3 py-1"
                 >
-                  用户等级{user.level}
+                  用户等级 {user?.level || ptStats.level}
                 </Tag>
                 
                 <Text type="secondary">邮箱:{user?.email || '未设置'}</Text>
@@ -308,7 +390,7 @@
             <div className="mb-4">
               <Text strong>当前分享率:{ptStats.ratio.toFixed(2)}</Text>
               <Progress
-                percent={Math.min(ptStats.ratio * 50, 100)} // 转换为百分比显示
+                percent={ptStats.ratio >= 999 ? 100 : Math.min(ptStats.ratio * 50, 100)} // 转换为百分比显示
                 strokeColor={getRatioColor(ptStats.ratio)}
                 format={() => ptStats.ratio.toFixed(2)}
               />