调试种子列表、下载种子

Change-Id: Iaf8fc868ae0f26af06e7e1e0ef4af436cd4f959d
diff --git a/src/App.js b/src/App.js
index bebc250..ecaf5fc 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,68 +1,3 @@
-// import { Route } from 'wouter';
-// import { Redirect } from 'wouter';
-// import AuthPage from './pages/AuthPage/AuthPage';
-// import HomePage from './pages/HomePage';
-// import FriendMoments from './pages/FriendMoments/FriendMoments';
-// import ForumPage from './pages/Forum/posts-main/ForumPage';
-// import SeedList from './pages/SeedList/SeedList';
-// import PostDetailPage from './pages/Forum/posts-detail/PostDetailPage';
-// import { UserProvider } from './context/UserContext';
-// import { GroupProvider } from './context/useGroupStore'; // 导入 GroupProvider
-// import PublishSeed from './pages/PublishSeed/PublishSeed';
-// import SeedDetail from './pages/SeedList/SeedDetail/SeedDetail';
-// import InterestGroup from './pages/InterestGroup/InterestGroup';
-// import UserProfile from './pages/UserCenter/UserProfile';
-// import CreatePostPage from './pages/Forum/posts-create/CreatePostPage';
-// import MessagePage from './pages/MessagePage/MessagePage';
-// import CreateMoment from './pages/FriendMoments/CreateMoment';
-// import PromotionsPage from './pages/PromotionsPage/PromotionsPage';
-// import LevelPage from './pages/LevelPage/LevelPage';
-// import NewbieTasks from './pages/UserCenter/NewbieTasks';
-// // import UserDynamics from './pages/UserCenter/UserDynamics';
-// import UserFriends from './pages/UserCenter/UserFriends';
-// import UserCollect from './pages/UserCenter/UserCollect';
-// import UserInvite from './pages/UserCenter/UserInvite';
-// import SimpleUploader from './pages/PublishSeed/SimpleUploader';
-
-// function RedirectToAuth() {
-//   if (typeof window !== 'undefined') {
-//     window.location.replace('/auth');
-//   }
-//   return null;
-// }
-
-// function App() {
-//   return (
-//     <UserProvider>
-//       <GroupProvider> {/* 添加 GroupProvider */}
-//         <>
-//           <Route path="/" component={RedirectToAuth} />
-//           <Route path="/auth" component={AuthPage} />
-//           <Route path="/friend-moments" component={FriendMoments} />
-//           <Route path="/friend-moments/create" component={CreateMoment} />
-//           <Route path="/forum" component={ForumPage} />
-//           <Route path="/forum/post/:postId" component={PostDetailPage} />
-//           <Route path="/forum/create-post" component={CreatePostPage} />
-//           <Route path="/seed-list" component={SeedList} />
-//           {/* <Route path="/publish-seed" component={PublishSeed} /> */}
-//           <Route path="/publish-seed" component={SimpleUploader} />
-//           <Route path="/seed/:seed_id" component={SeedDetail} />
-//           <Route path="/interest-groups" component={InterestGroup} />
-//           <Route path="/user/profile" component={UserProfile} />
-//           <Route path="/messages" component={MessagePage} />
-//           <Route path="/promotions" component={PromotionsPage} />
-//           <Route path="/level" component={LevelPage} />
-//           <Route path="/user/newbie-tasks" component={NewbieTasks} />
-//           {/* <Route path="/user/dynamics" component={UserDynamics} /> */}
-//           <Route path="/user/friends" component={UserFriends} />
-//           <Route path="/user/collections" component={UserCollect} />
-//           <Route path="/user/invite" component={UserInvite} />
-//         </>
-//       </GroupProvider>
-//     </UserProvider>
-//   );
-// }
-
 // export default App;
 
 import { Route } from 'wouter';
@@ -113,8 +48,9 @@
           <Route path="/forum/post/:postId" component={PostDetailPage} />
           <Route path="/forum/create-post" component={CreatePostPage} />
           <Route path="/seed-list" component={SeedList} />
-          <Route path="/publish-seed" component={SimpleUploader} />
-          <Route path="/seed/:seed_id" component={SeedDetail} />
+          <Route path="/publish-seed" component={PublishSeed} />
+          {/* <Route path="/publish-seed" component={SimpleUploader} /> */}
+          <Route path="/seed/:id" component={SeedDetail} />
           <Route path="/interest-groups" component={InterestGroup} />
           <Route path="/messages" component={MessagePage} />
           <Route path="/promotions" component={PromotionsPage} />
diff --git a/src/pages/Forum/promotion-part/Promotion.jsx b/src/pages/Forum/promotion-part/Promotion.jsx
index 49863e3..28fd7ac 100644
--- a/src/pages/Forum/promotion-part/Promotion.jsx
+++ b/src/pages/Forum/promotion-part/Promotion.jsx
@@ -1,142 +1,3 @@
-// import React, { useEffect, useState, useRef } from 'react';
-// import './Promotion.css';
-
-// const Promotion = () => {
-//   const [promotions, setPromotions] = useState([]);
-//   const [coldResources, setColdResources] = useState([]);
-//   const [loading, setLoading] = useState(true);
-
-//   const [promoIndex, setPromoIndex] = useState(0);
-//   const [coldIndex, setColdIndex] = useState(0);
-
-//   const promoTimerRef = useRef(null);
-//   const coldTimerRef = useRef(null);
-
-//   useEffect(() => {
-//     fetchData();
-//   }, []);
-
-//   useEffect(() => {
-//     if (promotions.length === 0) return;
-//     clearInterval(promoTimerRef.current);
-//     promoTimerRef.current = setInterval(() => {
-//       setPromoIndex(prev => (prev + 1) % promotions.length);
-//     }, 5000);
-//     return () => clearInterval(promoTimerRef.current);
-//   }, [promotions]);
-
-//   useEffect(() => {
-//     if (coldResources.length === 0) return;
-//     clearInterval(coldTimerRef.current);
-//     coldTimerRef.current = setInterval(() => {
-//       setColdIndex(prev => (prev + 1) % coldResources.length);
-//     }, 5000);
-//     return () => clearInterval(coldTimerRef.current);
-//   }, [coldResources]);
-
-//   const fetchData = async () => {
-//     try {
-//       // ✅ 获取促销活动列表(新接口)
-//       const promoResponse = await fetch(`seeds/promotions`);
-//       const promoJson = await promoResponse.json();
-//       const promoData = Array.isArray(promoJson?.result) ? promoJson.result : [];
-//       setPromotions(promoData);
-
-//       // 冷门资源(接口保持不变,若已更换请提供文档)
-//       const coldResponse = await fetch(`/echo/resources/cold`);
-//       const coldData = await coldResponse.json();
-//       setColdResources(coldData);
-//     } catch (error) {
-//       console.error('获取数据失败:', error);
-//     } finally {
-//       setLoading(false);
-//     }
-//   };
-
-//   if (loading) {
-//     return <div className="promotion-container">加载中...</div>;
-//   }
-
-//   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
-//   const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
-//   const prevCold = () => setColdIndex((coldIndex - 1 + coldResources.length) % coldResources.length);
-//   const nextCold = () => setColdIndex((coldIndex + 1) % coldResources.length);
-
-//   const currentPromo = promotions[promoIndex];
-//   const currentCold = coldResources[coldIndex];
-
-//   return (
-//     <div className="promotion-container carousel-container">
-//       {/* 促销活动轮播 */}
-//       <section className="carousel-section">
-//         <h2>当前促销活动</h2>
-//         {promotions.length === 0 || !currentPromo ? (
-//           <div className="empty-state">暂无促销活动</div>
-//         ) : (
-//           <div
-//             className="carousel"
-//             onMouseEnter={() => clearInterval(promoTimerRef.current)}
-//             onMouseLeave={() => {
-//               promoTimerRef.current = setInterval(() => {
-//                 setPromoIndex(prev => (prev + 1) % promotions.length);
-//               }, 3000);
-//             }}
-//           >
-//             <button className="arrow left" onClick={prevPromo}>&lt;</button>
-//             <div className="slide">
-//               <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
-//               <div><strong>促销时间:</strong>
-//                 {currentPromo?.pStartTime && currentPromo?.pEndTime
-//                   ? `${new Date(currentPromo.pStartTime).toLocaleString()} ~ ${new Date(currentPromo.pEndTime).toLocaleString()}`
-//                   : '未知'}
-//               </div>
-//               <div><strong>上传奖励系数:</strong>{currentPromo?.uploadCoeff ?? '无'}</div>
-//               <div><strong>下载折扣系数:</strong>{currentPromo?.downloadCoeff ?? '无'}</div>
-//               {currentPromo?.description && (
-//                 <div><strong>描述:</strong>{currentPromo.description}</div>
-//               )}
-//             </div>
-//             <button className="arrow right" onClick={nextPromo}>&gt;</button>
-//           </div>
-//         )}
-//       </section>
-
-//       {/* 冷门资源轮播 */}
-//       <section className="carousel-section">
-//         <h2>冷门资源推荐</h2>
-//         {coldResources.length === 0 || !currentCold ? (
-//           <div className="empty-state">暂无冷门资源推荐</div>
-//         ) : (
-//           <div
-//             className="carousel"
-//             onMouseEnter={() => clearInterval(coldTimerRef.current)}
-//             onMouseLeave={() => {
-//               coldTimerRef.current = setInterval(() => {
-//                 setColdIndex(prev => (prev + 1) % coldResources.length);
-//               }, 3000);
-//             }}
-//           >
-//             <button className="arrow left" onClick={prevCold}>&lt;</button>
-//             <div className="slide cold-slide">
-//               <img src={currentCold.poster} alt={currentCold.title} className="resource-poster" />
-//               <div className="resource-info">
-//                 <div><strong>标题:</strong>{currentCold.title}</div>
-//                 <div><strong>下载量:</strong>{currentCold.download_count ?? 0} | <strong>种子数:</strong>{currentCold.seed_count ?? 0}</div>
-//                 <div><strong>激励:</strong>
-//                   {currentCold.incentives?.download_exempt && <span className="incentive-badge">免下载量</span>}
-//                   {currentCold.incentives?.extra_seed_bonus && <span className="incentive-badge">做种加成</span>}
-//                   {!currentCold.incentives?.download_exempt && !currentCold.incentives?.extra_seed_bonus && '无'}
-//                 </div>
-//               </div>
-//             </div>
-//             <button className="arrow right" onClick={nextCold}>&gt;</button>
-//           </div>
-//         )}
-//       </section>
-//     </div>
-//   );
-// };
-
 // export default Promotion;
 
 import React, { useEffect, useState, useRef } from 'react';
diff --git a/src/pages/PublishSeed/PublishSeed.css b/src/pages/PublishSeed/PublishSeed.css
index 3c53352..9b33ad4 100644
--- a/src/pages/PublishSeed/PublishSeed.css
+++ b/src/pages/PublishSeed/PublishSeed.css
@@ -1,10 +1,4 @@
 .pub-card {
-  /* background-color: #e9ded2;
-  border-radius: 16px;
-  margin-top: 10%;
-  margin-left: 10%;
-  margin-right: 10%;
-  padding: 5% 10%; */
   background-color: #e9ded2;
   border-radius: 16px;
   max-width: 70%;
@@ -48,11 +42,7 @@
   margin-bottom: 2%;
 }
 .seed-file input {
-  /* padding: 1%; */
   width: 100%;
-  /* border-radius: 6px; */
-  /* border: 1px solid #e0c4a1;  */
-  /* background-color: #fff5f5; */
   color: #5F4437;
   font-size: 1rem;
   margin-bottom: 2%;
diff --git a/src/pages/PublishSeed/PublishSeed.jsx b/src/pages/PublishSeed/PublishSeed.jsx
index f27ebc2..7287e82 100644
--- a/src/pages/PublishSeed/PublishSeed.jsx
+++ b/src/pages/PublishSeed/PublishSeed.jsx
@@ -1,54 +1,81 @@
-import React, { useState, useRef } from 'react';
+// export default PublishSeed;
+import React, { useState, useRef, useEffect } from 'react';
 import axios from 'axios';
 import Header from '../../components/Header';
 import './PublishSeed.css';
 import { useUser } from '../../context/UserContext';
 
 const PublishSeed = () => {
+  console.log('[DEBUG] PublishSeed 组件渲染');
+
   const [title, setTitle] = useState('');
   const [description, setDescription] = useState('');
   const [tags, setTags] = useState([]);
   const [category, setCategory] = useState('movie');
-  const [imageUrl, setImageUrl] = useState('');
   const [message, setMessage] = useState('');
   const [isLoading, setIsLoading] = useState(false);
   const [fileName, setFileName] = useState('');
+  const [imageFile, setImageFile] = useState(null);
+  const [previewUrl, setPreviewUrl] = useState('');
 
-  const fileInputRef = useRef(null); // ✅ 获取 input file 引用
+  const fileInputRef = useRef(null);
+  const imageInputRef = useRef(null);
   const { user } = useUser();
 
+  useEffect(() => {
+    console.log('[DEBUG] 当前用户:', user);
+  }, [user]);
+
   const handleTagsChange = (e) => {
-    setTags(e.target.value.split(',').map(tag => tag.trim()));
+    console.log('[DEBUG] 标签输入变化:', e.target.value);
+    setTags(e.target.value.split(',').map(tag => tag.trim()).filter(Boolean));
   };
 
   const handleFileButtonClick = () => {
-    fileInputRef.current?.click(); // 点击隐藏的 input
+    console.log('[DEBUG] 触发文件选择按钮点击');
+    fileInputRef.current?.click();
+  };
+
+  const handleImageButtonClick = () => {
+    console.log('[DEBUG] 触发封面图片选择按钮点击');
+    imageInputRef.current?.click();
   };
 
   const handleFileChange = (e) => {
+    console.log('[DEBUG] 种子文件选择变化:', e.target.files);
     const selectedFile = e.target.files[0];
     if (selectedFile) {
-      setFileName(selectedFile.name); // 仅展示文件名
+      setFileName(selectedFile.name);
     }
   };
 
-  const handleSubmit = async (e) => {
-    e.preventDefault();
-    console.log('[handleSubmit] 表单提交开始');
+  const handleImageChange = (e) => {
+    console.log('[DEBUG] 封面图片选择变化:', e.target.files);
+    const img = e.target.files[0];
+    if (!img) return;
+    setImageFile(img);
+    setPreviewUrl(URL.createObjectURL(img));
+  };
 
-    const currentFile = fileInputRef.current?.files[0]; // ✅ 获取文件
-    console.log('[handleSubmit] currentFile:', currentFile);
+  const handleSubmit = async (e) => {
+    console.log('[DEBUG] handleSubmit 被触发', e);
+    e.preventDefault();
 
     setIsLoading(true);
     setMessage('');
 
-    if (!user || !user.id) {
+    const currentFile = fileInputRef.current?.files[0];
+    console.log('[DEBUG] 当前选择文件:', currentFile);
+
+    if (!user || !user.userId) {
+      console.log('[DEBUG] 用户未登录,阻止上传');
       setMessage('请先登录');
       setIsLoading(false);
       return;
     }
 
     if (!currentFile || !currentFile.name.toLowerCase().endsWith('.torrent')) {
+      console.log('[DEBUG] 文件校验失败');
       setMessage('请上传一个 .torrent 文件');
       setIsLoading(false);
       return;
@@ -59,21 +86,25 @@
     formData.append('title', title);
     formData.append('description', description);
     formData.append('category', category);
-    formData.append('imageUrl', imageUrl);
-    formData.append('tags', tags.join(','));
-    formData.append('uploader', user.id);
+    formData.append('tags', tags.join(',')); // 逗号分隔字符串
+    formData.append('uploader', user.userId);
+
+    if (imageFile) {
+      formData.append('image_url', imageFile);
+    }
 
     try {
+      console.log('[DEBUG] 发送上传请求...');
       const response = await axios.post('/seeds/upload', formData, {
-        headers: {
-          'Content-Type': 'multipart/form-data',
-        },
+        // axios 会自动处理 multipart/form-data Content-Type 边界,不用手动设置
+        // headers: { 'Content-Type': 'multipart/form-data' },
       });
+      console.log('[DEBUG] 请求成功,响应:', response.data);
 
-      if (response.data.code === 0) {
+      if (response.data.status === 'success') {
         setMessage('种子上传成功');
       } else {
-        setMessage(response.data.msg || '上传失败,请稍后再试');
+        setMessage(response.data.message || '上传失败,请稍后再试');
       }
     } catch (error) {
       console.error('[handleSubmit] 上传失败:', error);
@@ -88,13 +119,22 @@
       <Header />
       <div className="pub-card">
         {message && <div className="message">{message}</div>}
-        <form onSubmit={handleSubmit} encType="multipart/form-data">
+        <form
+          onSubmit={(e) => {
+            console.log('[DEBUG] form onSubmit 触发');
+            handleSubmit(e);
+          }}
+          encType="multipart/form-data"
+        >
           <div className="title-tag">
             <label>标题</label>
             <input
               type="text"
               value={title}
-              onChange={(e) => setTitle(e.target.value)}
+              onChange={(e) => {
+                console.log('[DEBUG] 标题输入变化:', e.target.value);
+                setTitle(e.target.value);
+              }}
               required
             />
           </div>
@@ -103,7 +143,10 @@
             <label>描述</label>
             <textarea
               value={description}
-              onChange={(e) => setDescription(e.target.value)}
+              onChange={(e) => {
+                console.log('[DEBUG] 描述输入变化:', e.target.value);
+                setDescription(e.target.value);
+              }}
               required
             />
           </div>
@@ -123,7 +166,10 @@
             <label>分类</label>
             <select
               value={category}
-              onChange={(e) => setCategory(e.target.value)}
+              onChange={(e) => {
+                console.log('[DEBUG] 分类选择变化:', e.target.value);
+                setCategory(e.target.value);
+              }}
               required
             >
               <option value="movie">电影</option>
@@ -134,7 +180,11 @@
 
           <div className="seed-file">
             <label>种子文件</label>
-            <div className="seed-file-label" onClick={handleFileButtonClick}>
+            <div
+              className="seed-file-label"
+              onClick={handleFileButtonClick}
+              style={{ cursor: 'pointer' }}
+            >
               点击选择文件
             </div>
             <input
@@ -148,28 +198,36 @@
           </div>
 
           <div className="form-group">
-            <label>封面图URL</label>
-            <input
-              type="url"
-              value={imageUrl}
-              onChange={(e) => setImageUrl(e.target.value)}
-              placeholder="例如:http://example.com/images/cover.jpg"
-              required
-              style={{
-                padding: '1%',
-                width: '100%',
-                borderRadius: '6px',
-                border: '1px solid #e0c4a1',
-                backgroundColor: '#fff5f5',
-                color: '#5F4437',
-                fontSize: '1rem',
-                marginBottom: '2%',
-              }}
-            />
+            <label>封面图</label>
+            <div className="cover-upload">
+              <button type="button" onClick={handleImageButtonClick}>
+                上传图片
+              </button>
+              <input
+                type="file"
+                accept="image/*"
+                ref={imageInputRef}
+                onChange={handleImageChange}
+                style={{ display: 'none' }}
+              />
+            </div>
+            {previewUrl && (
+              <div style={{ marginTop: '10px' }}>
+                <img
+                  src={previewUrl}
+                  alt="封面预览"
+                  style={{ maxWidth: '100%', maxHeight: '200px' }}
+                />
+              </div>
+            )}
           </div>
 
           <div className="upload-button">
-            <button type="submit" disabled={isLoading}>
+            <button
+              type="submit"
+              disabled={isLoading}
+              onClick={() => console.log('[DEBUG] 上传按钮 onClick 触发')}
+            >
               {isLoading ? '正在上传...' : '上传种子'}
             </button>
           </div>
diff --git a/src/pages/PublishSeed/SimpleUploader.jsx b/src/pages/PublishSeed/SimpleUploader.jsx
deleted file mode 100644
index 0f44b6a..0000000
--- a/src/pages/PublishSeed/SimpleUploader.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import React, { useRef, useState } from 'react';
-import axios from 'axios';
-
-const SimpleUploader = () => {
-  const fileInputRef = useRef(null);
-  const [message, setMessage] = useState('');
-
-  const handleUpload = async () => {
-    const file = fileInputRef.current?.files[0];
-    console.log('[handleUpload] file:', file);
-
-    if (!file) {
-      setMessage('请先选择文件');
-      return;
-    }
-
-    const formData = new FormData();
-    formData.append('file', file);
-
-    try {
-      const response = await axios.post('/seeds/upload', formData, {
-        headers: { 'Content-Type': 'multipart/form-data' },
-      });
-
-      console.log('[handleUpload] response:', response);
-      setMessage(response.data?.msg || '上传成功');
-    } catch (err) {
-      console.error('[handleUpload] 上传失败:', err);
-      setMessage('上传失败');
-    }
-  };
-
-  return (
-    <div style={{ padding: '2rem' }}>
-      <h2>种子上传测试</h2>
-      <input type="file" accept=".torrent" ref={fileInputRef} />
-      <button onClick={handleUpload} style={{ marginLeft: '1rem' }}>
-        上传
-      </button>
-      <div style={{ marginTop: '1rem' }}>{message}</div>
-    </div>
-  );
-};
-
-export default SimpleUploader;
diff --git a/src/pages/SeedList/SeedDetail/SeedDetail.jsx b/src/pages/SeedList/SeedDetail/SeedDetail.jsx
index 0a3ba4d..10cf65d 100644
--- a/src/pages/SeedList/SeedDetail/SeedDetail.jsx
+++ b/src/pages/SeedList/SeedDetail/SeedDetail.jsx
@@ -1,167 +1,219 @@
 import React, { useEffect, useState } from 'react';
 import axios from 'axios';
-import { useParams } from 'react-router-dom';
+import { useParams } from 'wouter';
 import Header from '../../../components/Header';
 import './SeedDetail.css';
-
+import { useUser } from '../../../context/UserContext';
 
 
 const SeedDetail = () => {
-    const { seed_id } = useParams();
-    const [seed, setSeed] = useState(null);
-    const [error, setError] = useState(null);
-    const [comments, setComments] = useState([]);
-    const [newComment, setNewComment] = useState('');
+  const params = useParams();
+  const seed_id = params.id;
 
-    useEffect(() => {
-        axios
-           .get(`/echo/seeds/${seed_id}`)
-           .then((res) => {
-                if (res.data.status === 'success') {
-                    setSeed(res.data.seed);
-                } else {
-                    setError('未能获取种子信息');
-                }
-            })
-           .catch(() => {
-                setError('获取种子详情失败');
+  const [seed, setSeed] = useState(null);
+  const [coverImage, setCoverImage] = useState(null);
+  const [error, setError] = useState(null);
+  const [comments, setComments] = useState([]);
+  const [newComment, setNewComment] = useState('');
+
+  const { user } = useUser();
+
+
+  // 格式化图片 URL
+  const formatImageUrl = (url) => {
+    if (!url) return '';
+    const filename = url.split('/').pop();
+    return `http://localhost:8080/uploads/torrents/${filename}`;
+  };
+
+  useEffect(() => {
+    if (!seed_id) {
+      setError('无效的种子ID');
+      return;
+    }
+
+    const fetchSeedDetail = async () => {
+      try {
+        const res = await axios.post(`/seeds/info/${seed_id}`);
+        if (res.data.code === 0) {
+          const seedData = res.data.data;
+
+          // 处理封面图
+          let cover = seedData.imageUrl;
+          if (!cover && seedData.imgUrl) {
+            const imgs = seedData.imgUrl
+              .split(',')
+              .map((i) => i.trim())
+              .filter(Boolean);
+            cover = imgs.length > 0 ? formatImageUrl(imgs[0]) : null;
+          }
+          setCoverImage(cover);
+          setSeed(seedData);
+          setError(null);
+        } else {
+          setError('未能获取种子信息');
+        }
+      } catch (err) {
+        console.error('请求种子详情出错:', err);
+        setError('获取种子详情失败');
+      }
+    };
+
+    const fetchComments = async () => {
+      try {
+        const res = await axios.get(`/seeds/${seed_id}/comments`);
+        if (res.data.code === 0) {
+          setComments(res.data.data || []);
+        } else {
+          setComments([]);
+        }
+      } catch {
+        setComments([]);
+      }
+    };
+
+    fetchSeedDetail();
+    fetchComments();
+  }, [seed_id]);
+
+ const handleDownload = async (seedId) => {
+        if (!user || !user.userId) {
+            alert('请先登录再下载种子文件');
+            return;
+        }
+
+        try {
+            const response = await axios.get(`/seeds/${seedId}/download`, {
+                params: {
+                    passkey: user.userId,
+                },
+                responseType: 'blob'
             });
 
-        // 模拟获取评论数据,实际需要调用 API
-        axios.get(`/echo/seeds/${seed_id}/comments`)
-           .then((res) => {
-                if (res.data.status === 'success') {
-                    setComments(res.data.comments);
-                }
-            })
-           .catch(() => {
-                console.log('获取评论失败');
-            });
-    }, [seed_id]);
-
-    const handleDownload = async () => {
-        if (seed) {
-            const peer_id = 'echo-' + Math.random().toString(36).substring(2, 10);
-            const ip = '127.0.0.1';
-            const port = 6881;
-            const uploaded = 0;
-            const downloaded = 0;
-            const left = 0;
-
-            try {
-                const response = await axios.get(`/echo/seeds/${seed.seed_id}/download`, {
-                    params: { peer_id, ip, port, uploaded, downloaded, left },
-                    responseType: 'blob'
-                });
-
-                const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
-                const downloadUrl = URL.createObjectURL(blob);
-                const a = document.createElement('a');
-                a.href = downloadUrl;
-                a.download = `${seed.seed_id}.torrent`;
-                a.click();
-                URL.revokeObjectURL(downloadUrl);
-            } catch (error) {
-                console.error('下载失败:', error);
-                alert('下载失败,请稍后再试。');
-            }
+            const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
+            const downloadUrl = URL.createObjectURL(blob);
+            const a = document.createElement('a');
+            a.href = downloadUrl;
+            a.download = `${seedId}.torrent`;
+            a.click();
+            URL.revokeObjectURL(downloadUrl);
+        } catch (error) {
+            console.error('下载失败:', error);
+            alert('下载失败,请稍后再试。');
         }
     };
 
-    const handleCollect = () => {
-        // 这里可以添加收藏逻辑,调用相应 API
-        alert('已收藏');
-    };
 
-    const handleAddComment = () => {
-        if (newComment.trim()) {
-            // 这里可以添加评论逻辑,调用相应 API
-            setComments([...comments, { content: newComment, user: '用户' }]);
-            setNewComment('');
-        }
-    };
+  const handleCollect = () => {
+    alert('已收藏');
+  };
 
-    if (error) {
-        return (
-            <div className="seed-detail-page">
-                <Header />
-                <div className="seed-detail">
-                    <p className="error-text">{error}</p>
-                </div>
-            </div>
-        );
+  const handleAddComment = () => {
+    if (newComment.trim()) {
+      setComments([...comments, { content: newComment, user: '用户' }]);
+      setNewComment('');
     }
+  };
 
-    if (!seed) {
-        return (
-            <div className="seed-detail-page">
-                <Header />
-                <div className="seed-detail">
-                    <p>加载中...</p>
-                </div>
-            </div>
-        );
-    }
-
+  if (error) {
     return (
-        <div className="seed-detail-page">
-            <Header />
-            <div className="seed-detail">
-                <h1>{seed.title}</h1>
-                <div className="seed-header-container">
-                    <div className="seed-info">
-                        <div className="seed-basic-info">
-                            <p><strong>分类:</strong>{seed.category}</p>
-                            <p><strong>发布时间:</strong>{new Date(seed.upload_time).toLocaleString()}</p>
-                            <p><strong>标签:</strong>{seed.tags.join(' / ')}</p>
-                            <p><strong>简介:</strong>{seed.description}</p>
-                            <p><strong>大小:</strong>{seed.size} GB</p>
-                            <p><strong>分辨率:</strong>{seed.resolution}</p>
-                            <p><strong>片长:</strong>{seed.duration}</p>
-                            <p><strong>地区:</strong>{seed.region}</p>
-                            <p><strong>下载次数:</strong>{seed.downloads}</p>
-                        </div>
-                        {(seed.category === '电影' || seed.category === '电视剧') && (
-                            <div className="seed-media-info">
-                                <p><strong>导演:</strong>{seed.director}</p>
-                                <p><strong>编剧:</strong>{seed.writer}</p>
-                                <p><strong>主演:</strong>{seed.actors.join(' / ')}</p>
-                            </div>
-                        )}
-                    </div>
-                    <img src={seed.cover_url} alt={seed.title} className="cover-image" />
-                </div>
-                <div className="action-buttons">
-                    <button className="btn" onClick={handleDownload}>下载</button>
-                    <button className="btn" onClick={handleCollect}>收藏</button>             
-                </div>
-                <hr className="divider" />
-                {/* 评论部分 */}
-                <h3>评论区</h3>
-                <div className="comments-section">
-                    <div className="comments-list">
-                        {comments.map((comment, index) => (
-                            <div key={index} className="comment">
-                                <p className="comment-user">{comment.user}</p>
-                                <p className="comment-content">{comment.content}</p>
-                            </div>
-                        ))}
-                    </div>
-                    <div className="add-comment-form">
-                        <textarea
-                            placeholder="输入你的评论..."
-                            value={newComment}
-                            onChange={(e) => setNewComment(e.target.value)}
-                        />
-                        <div className="comment-options">
-                        <button className="btn" onClick={handleAddComment}>发布评论</button>
-                        </div>
-                    </div>
-                </div>
-            </div>
+      <div className="seed-detail-page">
+        <Header />
+        <div className="seed-detail">
+          <p className="error-text">{error}</p>
         </div>
+      </div>
     );
+  }
+
+  if (!seed) {
+    return (
+      <div className="seed-detail-page">
+        <Header />
+        <div className="seed-detail">
+          <p>加载中...</p>
+        </div>
+      </div>
+    );
+  }
+
+  const tags = seed.tags
+    ? Array.isArray(seed.tags)
+      ? seed.tags
+      : typeof seed.tags === 'string'
+      ? seed.tags.split(',').map((t) => t.trim())
+      : []
+    : [];
+
+  const actors = seed.actors
+    ? Array.isArray(seed.actors)
+      ? seed.actors
+      : typeof seed.actors === 'string'
+      ? seed.actors.split(',').map((a) => a.trim())
+      : []
+    : [];
+
+  return (
+    <div className="seed-detail-page">
+      <Header />
+      <div className="seed-detail">
+        <h1>{seed.title}</h1>
+        <div className="seed-header-container">
+          <div className="seed-info">
+            <div className="seed-basic-info">
+              <p><strong>分类:</strong>{seed.category || '未知'}</p>
+              <p><strong>发布时间:</strong>{seed.upload_time ? new Date(seed.upload_time).toLocaleString() : '未知'}</p>
+              <p><strong>标签:</strong>{tags.join(' / ')}</p>
+              <p><strong>简介:</strong>{seed.description || '无'}</p>
+              <p><strong>大小:</strong>{seed.size || '未知'}</p>
+              <p><strong>分辨率:</strong>{seed.resolution || '未知'}</p>
+              <p><strong>片长:</strong>{seed.duration || '未知'}</p>
+              <p><strong>地区:</strong>{seed.region || '未知'}</p>
+              <p><strong>下载次数:</strong>{seed.downloads ?? 0}</p>
+            </div>
+            {(seed.category === '电影' || seed.category === '电视剧') && (
+              <div className="seed-media-info">
+                <p><strong>导演:</strong>{seed.director || '未知'}</p>
+                <p><strong>编剧:</strong>{seed.writer || '未知'}</p>
+                <p><strong>主演:</strong>{actors.join(' / ')}</p>
+              </div>
+            )}
+          </div>
+          <img
+            src={coverImage || '/default-cover.png'}
+            alt={seed.title}
+            className="cover-image"
+          />
+        </div>
+        <div className="action-buttons">
+          <button className="btn" onClick={() => handleDownload(seed.id)}>下载</button>
+          <button className="btn" onClick={handleCollect}>收藏</button>
+        </div>
+        <hr className="divider" />
+        <h3>评论区</h3>
+        <div className="comments-section">
+          <div className="comments-list">
+            {comments.map((comment, index) => (
+              <div key={index} className="comment">
+                <p className="comment-user">{comment.user}</p>
+                <p className="comment-content">{comment.content}</p>
+              </div>
+            ))}
+          </div>
+          <div className="add-comment-form">
+            <textarea
+              placeholder="输入你的评论..."
+              value={newComment}
+              onChange={(e) => setNewComment(e.target.value)}
+            />
+            <div className="comment-options">
+              <button className="btn" onClick={handleAddComment}>发布评论</button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
 };
 
 export default SeedDetail;
-    
\ No newline at end of file
diff --git a/src/pages/SeedList/SeedList.jsx b/src/pages/SeedList/SeedList.jsx
index eade11d..be8d13f 100644
--- a/src/pages/SeedList/SeedList.jsx
+++ b/src/pages/SeedList/SeedList.jsx
@@ -1,93 +1,99 @@
 import React, { useState, useEffect } from 'react';
 import { Link } from 'wouter';
 import axios from 'axios';
-import logo from '../../assets/logo.png';
 import Recommend from './Recommend/Recommend';
 import Header from '../../components/Header';  // 引入 Header 组件
 import './SeedList.css';
-
-
+import { useUser } from '../../context/UserContext';
 
 const SeedList = () => {
     const [seeds, setSeeds] = useState([]);
-    const [filteredSeeds, setFilteredSeeds] = useState([]);
     const [loading, setLoading] = useState(true);
     const [searchTerm, setSearchTerm] = useState('');
     const [sortOption, setSortOption] = useState('最新');
     const [activeTab, setActiveTab] = useState('种子列表');
     const [filters, setFilters] = useState({});
     const [selectedFilters, setSelectedFilters] = useState({});
-    const [tagMode, setTagMode] = useState('all');
+    const [tagMode, setTagMode] = useState('any'); // 与接口对应,any / all
     const [errorMsg, setErrorMsg] = useState('');
+    const { user } = useUser();
+
 
     const TAGS = ['猜你喜欢', '电影', '电视剧', '动漫', '音乐', '游戏', '综艺', '软件', '体育', '学习', '纪录片', '其他'];
 
     const CATEGORY_MAP = {
-        '电影': 'movie',
-        '电视剧': 'tv',
-        '动漫': 'anime',
-        '音乐': 'music',
-        '游戏': 'game',
-        '综艺': 'variety',
-        '软件': 'software',
-        '体育': 'sports',
-        '学习': 'education',
-        '纪录片': 'documentary',
-        '其他': 'other'
+        '电影': '电影',
+        '电视剧': '电视剧',
+        '动漫': '动漫',
+        '音乐': '音乐',
+        '游戏': '游戏',
+        '综艺': '综艺',
+        '软件': '软件',
+        '体育': '体育',
+        '学习': '学习',
+        '纪录片': '纪录片',
+        '其他': '其他',
+        '猜你喜欢': '',        
+        '种子列表': '',        
     };
 
     const buildQueryParams = () => {
-        const category = CATEGORY_MAP[activeTab];
+        const category = CATEGORY_MAP[activeTab] || '';
+        const orderKey = sortOption === '最新' ? ['upload_time'] : (sortOption === '最热' ? ['downloads'] : ['upload_time']);
         const params = {
-            category,
-            sort_by: sortOption === '最新' ? 'newest' : sortOption === '最热' ? 'downloads' : undefined,
             page: 1,
-            limit: 20,
-            include_fields: 'seed_id,title,category,tags,size,upload_time,downloads,image_url',
+            size: 20,
+            orderKey,
+            orderDesc: true,
         };
 
-        if (searchTerm.trim()) params.search = searchTerm.trim();
+        if (searchTerm.trim()) {
+            params.title = searchTerm.trim();
+        }
+        if (category) {
+            params.category = category;
+        }
 
         const tags = Object.entries(selectedFilters)
-           .filter(([_, value]) => value!== '不限')
-           .map(([_, value]) => value);
+            .filter(([_, value]) => value !== '不限')
+            .map(([_, value]) => value);
 
         if (tags.length > 0) {
-            params.tags = tags.join(',');
-            params.tag_mode = tagMode;
+            params.tags = tags;
+            params.tagMode = tagMode; // any 或 all
         }
 
         return params;
     };
 
     const fetchSeeds = async () => {
+        if (activeTab === '猜你喜欢') return; 
         setLoading(true);
         setErrorMsg('');
         try {
             const params = buildQueryParams();
-            const queryString = new URLSearchParams(params).toString();
-            const response = await fetch(`/echo/seeds?${queryString}`);
-            const data = await response.json();
+            const response = await axios.post('/seeds/list', params);
+            const data = response.data;
 
-            if (data.status!== 'success') throw new Error(data.message || '获取失败');
-            const seeds = data.seeds || [];
-            setSeeds(seeds);
-            setFilteredSeeds(seeds);
+            if (data.code !== 0) {
+                throw new Error(data.msg || '获取失败');
+            }
+
+            setSeeds(data.data || []);
         } catch (error) {
             console.error('获取种子列表失败:', error);
             setErrorMsg(error.message || '获取失败,请稍后再试。');
             setSeeds([]);
-            setFilteredSeeds([]);
         } finally {
             setLoading(false);
         }
     };
 
     const fetchFilterOptions = async () => {
-        if (activeTab === '猜你喜欢') return;
+        if (activeTab === '猜你喜欢' || !CATEGORY_MAP[activeTab]) return;
         const category = CATEGORY_MAP[activeTab];
         try {
-            const res = await axios.get(`/echo/seed-filters?category=${category}`);
+            const res = await axios.get(`/seed-filters?category=${category}`);
             const filterData = res.data || {};
             setFilters(filterData);
 
@@ -104,28 +110,25 @@
     };
 
     useEffect(() => {
-        if (activeTab!== '猜你喜欢') {
-            fetchFilterOptions();
-        }
+        fetchFilterOptions();
     }, [activeTab]);
 
     useEffect(() => {
-        if (activeTab!== '猜你喜欢') {
-            fetchSeeds();
-        }
+        fetchSeeds();
     }, [activeTab, sortOption, selectedFilters, tagMode, searchTerm]);
 
+    // ✅ 修改后的下载函数
     const handleDownload = async (seedId) => {
-        const peer_id = 'echo-' + Math.random().toString(36).substring(2, 10);
-        const ip = '127.0.0.1';
-        const port = 6881;
-        const uploaded = 0;
-        const downloaded = 0;
-        const left = 0;
+        if (!user || !user.userId) {
+            alert('请先登录再下载种子文件');
+            return;
+        }
 
         try {
-            const response = await axios.get(`/echo/seeds/${seedId}/download`, {
-                params: { peer_id, ip, port, uploaded, downloaded, left },
+            const response = await axios.get(`/seeds/${seedId}/download`, {
+                params: {
+                    passkey: user.userId,
+                },
                 responseType: 'blob'
             });
 
@@ -143,22 +146,22 @@
     };
 
     const handleFilterChange = (key, value) => {
-        setSelectedFilters((prev) => ({
-           ...prev,
+        setSelectedFilters(prev => ({
+            ...prev,
             [key]: value
         }));
     };
 
     const clearFilter = (key) => {
-        setSelectedFilters((prev) => ({
-           ...prev,
+        setSelectedFilters(prev => ({
+            ...prev,
             [key]: '不限'
         }));
     };
 
     return (
         <div className="seed-list-container">
-            <Header /> {/* 引用 Header 组件 */}
+            <Header />
 
             <div className="controls">
                 <input
@@ -168,21 +171,29 @@
                     onChange={(e) => setSearchTerm(e.target.value)}
                     className="search-input"
                 />
-                <select value={sortOption} onChange={(e) => setSortOption(e.target.value)} className="sort-select">
+                <select
+                    value={sortOption}
+                    onChange={(e) => setSortOption(e.target.value)}
+                    className="sort-select"
+                >
                     <option value="最新">最新</option>
                     <option value="最热">最热</option>
                 </select>
-                <select value={tagMode} onChange={(e) => setTagMode(e.target.value)} className="tag-mode-select">
+                <select
+                    value={tagMode}
+                    onChange={(e) => setTagMode(e.target.value)}
+                    className="tag-mode-select"
+                >
                     <option value="any">包含任意标签</option>
                     <option value="all">包含所有标签</option>
                 </select>
             </div>
 
             <div className="tag-filters">
-                {TAGS.map((tag) => (
+                {TAGS.map(tag => (
                     <button
                         key={tag}
-                        className={`tag-button ${activeTab === tag? 'active-tag' : ''}`}
+                        className={`tag-button ${activeTab === tag ? 'active-tag' : ''}`}
                         onClick={() => {
                             setActiveTab(tag);
                             setFilters({});
@@ -194,7 +205,7 @@
                 ))}
             </div>
 
-            {activeTab!== '猜你喜欢' && Object.keys(filters).length > 0 && (
+            {activeTab !== '猜你喜欢' && Object.keys(filters).length > 0 && (
                 <div className="filter-bar">
                     {Object.entries(filters).map(([key, options]) => (
                         <div className="filter-group" key={key}>
@@ -203,12 +214,17 @@
                                 value={selectedFilters[key]}
                                 onChange={(e) => handleFilterChange(key, e.target.value)}
                             >
-                                {options.map((opt) => (
+                                {options.map(opt => (
                                     <option key={opt} value={opt}>{opt}</option>
                                 ))}
                             </select>
-                            {selectedFilters[key]!== '不限' && (
-                                <button className="clear-filter-btn" onClick={() => clearFilter(key)}>✕</button>
+                            {selectedFilters[key] !== '不限' && (
+                                <button
+                                    className="clear-filter-btn"
+                                    onClick={() => clearFilter(key)}
+                                >
+                                    ✕
+                                </button>
                             )}
                         </div>
                     ))}
@@ -216,13 +232,13 @@
             )}
 
             <div className="seed-list-content">
-                {activeTab === '猜你喜欢'? (
+                {activeTab === '猜你喜欢' ? (
                     <Recommend />
-                ) : loading? (
+                ) : loading ? (
                     <p>加载中...</p>
-                ) : errorMsg? (
+                ) : errorMsg ? (
                     <p className="error-text">{errorMsg}</p>
-                ) : filteredSeeds.length === 0? (
+                ) : seeds.length === 0 ? (
                     <p>未找到符合条件的种子。</p>
                 ) : (
                     <div className="seed-list-card">
@@ -235,53 +251,99 @@
                             <div className="seed-header-actions">操作</div>
                         </div>
                         <div className="seed-list-body">
-                            {filteredSeeds.map((seed, index) => (
-                                <Link href={`/seed/${seed.seed_id}`} key={index} className="seed-item-link">
-                                    <div className="seed-item">
-                                        {seed.image_url && (
-                                            <img src={seed.image_url} alt={seed.title} className="seed-item-cover" />
-                                        )}
-                                        <div className="seed-item-title">
-                                            <div className="seed-title-row">
-                                                <h3 className="seed-title">{seed.title}</h3>
-                                                <div className="seed-tags">
-                                                    {seed.tags && seed.tags.map((tag, i) => (
-                                                        <span key={i} className="tag-label">{tag}</span>
-                                                    ))}
+                            {seeds.map((seed, index) => {
+                                // 处理 tags 字段,兼容字符串和数组
+                                let tagsArray = [];
+                                if (seed.tags) {
+                                    if (Array.isArray(seed.tags)) {
+                                        tagsArray = seed.tags;
+                                    } else if (typeof seed.tags === 'string') {
+                                        try {
+                                            tagsArray = JSON.parse(seed.tags);
+                                            if (!Array.isArray(tagsArray)) {
+                                                // 解析后不是数组,按逗号分割
+                                                tagsArray = seed.tags.split(',').map(t => t.trim()).filter(t => t);
+                                            }
+                                        } catch {
+                                            // 解析失败,按逗号分割
+                                            tagsArray = seed.tags.split(',').map(t => t.trim()).filter(t => t);
+                                        }
+                                    }
+                                }
+
+                                return (
+                                    
+                                   <Link to={`/seed/${seed.id}`} key={index} className="seed-item-link">
+                                        <div className="seed-item">
+                                            {seed.image_url && (
+                                                <img
+                                                    src={seed.image_url}
+                                                    alt={seed.title}
+                                                    className="seed-item-cover"
+                                                />
+                                            )}
+                                            <div className="seed-item-title">
+                                                <div className="seed-title-row">
+                                                    <h3 className="seed-title">{seed.title}</h3>
+                                                    <div className="seed-tags">
+                                                        {tagsArray.map((tag, i) => (
+                                                            <span key={i} className="tag-label">{tag}</span>
+                                                        ))}
+                                                    </div>
                                                 </div>
                                             </div>
-                                        </div>
-                                        <div className="seed-item-size">{seed.size || '未知'}</div>
-                                        <div className="seed-item-upload-time">{seed.upload_time?.split('T')[0] || '未知'}</div>
-                                        <div className="seed-item-downloads">{seed.downloads?? 0} 次下载</div>
-                                        <div
-                                            className="seed-item-actions"
-                                            onClick={(e) => e.stopPropagation()} // 阻止事件冒泡,避免跳转
-                                        >
-                                            <button
-                                                className="btn-primary"
-                                                onClick={(e) => {
-                                                    e.preventDefault(); // 阻止跳转
-                                                    e.stopPropagation();
-                                                    handleDownload(seed.seed_id);
-                                                }}
+                                            <div className="seed-item-size">{seed.size || '未知'}</div>
+                                            <div className="seed-item-upload-time">{seed.upload_time?.split('T')[0] || '未知'}</div>
+                                            <div className="seed-item-downloads">{seed.downloads ?? 0} 次下载</div>
+                                            <div
+                                                className="seed-item-actions"
+                                                onClick={e => e.stopPropagation()} // 阻止事件冒泡,避免跳转
                                             >
-                                                下载
-                                            </button>
-                                            <button
-                                                className="btn-outline"
-                                                onClick={(e) => {
-                                                    e.preventDefault();
-                                                    e.stopPropagation();
-                                                    // TODO: 收藏操作
-                                                }}
-                                            >
-                                                收藏
-                                            </button>
+                                                <button
+                                                    className="btn-primary"
+                                                    onClick={e => {
+                                                        e.preventDefault();
+                                                        e.stopPropagation();
+                                                        handleDownload(seed.id);
+                                                    }}
+                                                >
+                                                    下载
+                                                </button>
+                                                <button
+    className="btn-outline"
+    onClick={async (e) => {
+        e.preventDefault();
+        e.stopPropagation();
+
+        if (!user || !user.userId) {
+            alert('请先登录再收藏');
+            return;
+        }
+
+        try {
+            const res = await axios.post(`/seeds/${seed.id}/favorite-toggle`, null, {
+                params: { user_id: user.userId },
+            });
+
+            if (res.data.code === 0) {
+                alert('操作成功'); // 你可以改成 toast 或 icon 状态提示
+            } else {
+                alert(res.data.msg || '操作失败');
+            }
+        } catch (err) {
+            console.error('收藏失败:', err);
+            alert('收藏失败,请稍后再试。');
+        }
+    }}
+>
+    收藏
+</button>
+
+                                            </div>
                                         </div>
-                                    </div>
-                                </Link>
-                            ))}
+                                    </Link>
+                                );
+                            })}
                         </div>
                     </div>
                 )}
@@ -291,4 +353,3 @@
 };
 
 export default SeedList;
-