用户资料接口添加

Change-Id: Id55e7fef307ec1663bb7a05cfbf7f81e097ac767
diff --git a/front/src/UserProfile.js b/front/src/UserProfile.js
index 0269723..4737033 100644
--- a/front/src/UserProfile.js
+++ b/front/src/UserProfile.js
@@ -1,4 +1,4 @@
-import React, { useState } from "react";

+import React, { useState, useEffect } from "react";

 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

 import { useNavigate } from "react-router-dom";

 import "./App.css";

@@ -6,21 +6,109 @@
 export default function UserProfile() {

   const navigate = useNavigate();

   const [userInfo, setUserInfo] = useState({

+    avatar_url: "",

     username: "示例用户",

     email: "user@example.com",

-    company: "",

+    invite_left: "",

     school: "",

-    birthday: "",

+    account_status: "",

+    gender: "",

   });

   const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });

+  const [userSeeds, setUserSeeds] = useState([]);

+  const [userStats, setUserStats] = useState({

+    magic: 0,

+    upload: 0,

+    download: 0,

+    ratio: 0,

+  });

+

+  // 新增:根据userid从后端获取用户信息

+  useEffect(() => {

+    const fetchUserInfo = async () => {

+      // 假设userid存储在localStorage或其他地方

+      // const userid = localStorage.getItem("userid");

+      const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

+      if (!userid) return;

+      try {

+        const res = await fetch(`/api/user-profile?userid=${userid}`);

+        if (res.ok) {

+          const data = await res.json();

+          setUserInfo(data);

+          setTempUserInfo(data);

+        }

+      } catch (err) {

+        // 可以根据需要处理错误

+        console.error("获取用户信息失败", err);

+      }

+    };

+    fetchUserInfo();

+  }, []);

+

+  // 动态加载用户上传种子列表

+  useEffect(() => {

+    const fetchUserSeeds = async () => {

+      // const userid = localStorage.getItem("userid");

+      const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

+      if (!userid) return;

+      try {

+        const res = await fetch(`/api/user-seeds?userid=${userid}`);

+        if (res.ok) {

+          const data = await res.json();

+          setUserSeeds(data);

+        }

+      } catch (err) {

+        console.error("获取种子列表失败", err);

+      }

+    };

+    fetchUserSeeds();

+  }, []);

+

+  // 动态加载用户活跃度信息

+  useEffect(() => {

+    const fetchUserStats = async () => {

+      // const userid = localStorage.getItem("userid");

+      const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

+      if (!userid) return;

+      try {

+        const res = await fetch(`/api/user-stats?userid=${userid}`);

+        if (res.ok) {

+          const data = await res.json();

+          setUserStats(data);

+        }

+      } catch (err) {

+        console.error("获取活跃度信息失败", err);

+      }

+    };

+    fetchUserStats();

+  }, []);

 

   const handleInputChange = (field, value) => {

     setTempUserInfo({ ...tempUserInfo, [field]: value });

   };

 

-  const handleSave = () => {

+  const handleSave = async () => {

     setUserInfo({ ...tempUserInfo });

-    alert("信息已保存!");

+    // 获取userid

+    // const userid = localStorage.getItem("userid");

+    const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

+    try {

+      const res = await fetch('/api/change-profile', {

+        method: 'POST',

+        headers: {

+          'Content-Type': 'application/json',

+        },

+        body: JSON.stringify({ userid, ...tempUserInfo }),

+      });

+      if (res.ok) {

+        alert("信息已保存!");

+      } else {

+        alert("保存失败,请重试。");

+      }

+    } catch (err) {

+      alert("保存失败,请检查网络连接。");

+      console.error("保存用户信息失败", err);

+    }

   };

 

   const handleAvatarClick = () => {

@@ -39,7 +127,7 @@
             <AccountCircleIcon style={{ fontSize: 90, color: '#1a237e', marginBottom: 12 }} />

             {tempUserInfo.avatar && (

               <img

-                src={tempUserInfo.avatar}

+                src={tempUserInfo.avatar_url}

                 alt="用户头像"

                 style={{

                   position: 'absolute',

@@ -67,21 +155,19 @@
           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

             <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>

-            <input

-              type="email"

-              value={tempUserInfo.email}

-              onChange={(e) => handleInputChange("email", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

+            <span

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}

+            >

+              {tempUserInfo.email}

+            </span>

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

-            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>公司:</b>

-            <input

-              type="text"

-              value={tempUserInfo.company}

-              onChange={(e) => handleInputChange("company", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>

+            <span

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}

+            >

+              {tempUserInfo.invite_left}

+            </span>

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

             <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>

@@ -93,22 +179,21 @@
             />

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

-            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>生日:</b>

-            <input

-              type="date"

-              value={tempUserInfo.birthday}

-              onChange={(e) => handleInputChange("birthday", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

-          </div>

-          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

-            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>密码:</b>

-            <input

-              type="password"

-              value={tempUserInfo.password || ""}

-              onChange={(e) => handleInputChange("password", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>

+            <span

+              style={{ flex: 1, display: 'flex', alignItems: 'center', padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}

+            >

+              {tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}

+              <span style={{

+                display: 'inline-block',

+                width: 12,

+                height: 12,

+                borderRadius: '50%',

+                backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',

+                marginLeft: 10,

+                border: '1px solid #b2b2b2',

+              }} />

+            </span>

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

             <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>

@@ -187,15 +272,61 @@
       {/* 上传种子列表 */}

       <div style={{ gridColumn: '2 / 3', gridRow: '1 / 2', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>

         <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>个人上传种子列表</h3>

-        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>

-          (此处显示上传种子列表)

+        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 12 }}>

+          {userSeeds.length === 0 ? (

+            <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无上传种子)</div>

+          ) : (

+            <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>

+              {userSeeds.map((seed, idx) => (

+                <li

+                  key={seed.seed_id || idx}

+                  style={{ display: 'flex', alignItems: 'center', padding: '10px 0', borderBottom: '1px solid #e0e7ff', cursor: 'pointer' }}

+                  onClick={e => {

+                    // 阻止点击删除按钮时跳转

+                    if (e.target.classList.contains('delete-btn')) return;

+                    navigate(`/torrent/${seed.seed_id}`);

+                  }}

+                >

+                  <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>

+                  <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>

+                  <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.popularity}</span>

+                  <button

+                    className="delete-btn"

+                    style={{ marginLeft: 18, background: '#e53935', color: '#fff', border: 'none', borderRadius: 6, padding: '4px 14px', cursor: 'pointer', fontSize: 14 }}

+                    onClick={async (e) => {

+                      e.stopPropagation();

+                      // const userid = localStorage.getItem("userid");

+                      const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

+                      try {

+                        const res = await fetch('/api/delete-seed', {

+                          method: 'POST',

+                          headers: { 'Content-Type': 'application/json' },

+                          body: JSON.stringify({ seed_id: seed.seed_id, userid }),

+                        });

+                        if (res.ok) {

+                          setUserSeeds(userSeeds.filter((s, i) => (s.seed_id || i) !== (seed.seed_id || idx)));

+                        } else {

+                          alert('删除失败,请重试');

+                        }

+                      } catch (err) {

+                        alert('删除失败,请检查网络');

+                      }

+                    }}

+                  >删除</button>

+                </li>

+              ))}

+            </ul>

+          )}

         </div>

       </div>

       {/* 活跃度模块 */}

       <div style={{ gridColumn: '2 / 3', gridRow: '2 / 3', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>

         <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>活跃度</h3>

-        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>

-          (此处显示活跃度信息)

+        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 18, display: 'flex', flexDirection: 'column', gap: 12, fontSize: 18 }}>

+          <div>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></div>

+          <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload} GB</b></div>

+          <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download} GB</b></div>

+          <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.ratio}</b></div>

         </div>

       </div>

     </div>