新增后台接口

Change-Id: Ibd8183079a77aad05b2c81658c2d6fe9b648e042
diff --git a/front/src/AdminPage.js b/front/src/AdminPage.js
index 4862449..dac05cb 100644
--- a/front/src/AdminPage.js
+++ b/front/src/AdminPage.js
@@ -1,5 +1,6 @@
-import React, { useState } from "react";
+import React, { useState, useEffect } from "react";
 import { useNavigate } from "react-router-dom";
+import { API_BASE_URL } from "./config";
 
 // 示例数据
 const initialConfig = {
@@ -9,19 +10,46 @@
     CheatTime: 5,
 };
 
-const cheatUsers = [
-    { user_id: "u001", email: "cheat1@example.com", username: "cheater1", account_status: 1 },
-    { user_id: "u002", email: "cheat2@example.com", username: "cheater2", account_status: 0 },
-];
-
-const suspiciousUsers = [
-    { user_id: "u101", email: "suspect1@example.com", username: "suspect1", account_status: 0 },
-    { user_id: "u102", email: "suspect2@example.com", username: "suspect2", account_status: 0 },
-];
-
 export default function AdminPage() {
     const navigate = useNavigate();
     const [config, setConfig] = useState(initialConfig);
+    // state for users fetched from backend
+    const [cheatUsers, setCheatUsers] = useState([]);
+    const [suspiciousUsers, setSuspiciousUsers] = useState([]);
+
+    // helper to get admin userId from cookie
+    const getUserIdFromCookie = () => {
+        const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
+        return match ? match[2] : null;
+    };
+
+    // fetch cheat users list from backend
+    const fetchCheatUsers = async () => {
+        const adminId = getUserIdFromCookie();
+        if (!adminId) return;
+        try {
+            const res = await fetch(`${API_BASE_URL}/api/admin/cheat-users?userid=${adminId}`);
+            if (!res.ok) throw new Error('获取作弊用户失败');
+            const data = await res.json();
+            setCheatUsers(data);
+        } catch (err) {
+            console.error(err);
+        }
+    };
+
+    // fetch suspicious users list from backend
+    const fetchSuspiciousUsers = async () => {
+        const adminId = getUserIdFromCookie();
+        if (!adminId) return;
+        try {
+            const res = await fetch(`${API_BASE_URL}/api/admin/suspicious-users?userid=${adminId}`);
+            if (!res.ok) throw new Error('获取可疑用户失败');
+            const data = await res.json();
+            setSuspiciousUsers(data);
+        } catch (err) {
+            console.error(err);
+        }
+    };
 
     const handleConfigChange = (e) => {
         const { name, value } = e.target;
@@ -29,31 +57,129 @@
     };
 
     const handleBan = (user) => {
-        alert(`已封禁用户:${user.username}`);
+        const adminId = getUserIdFromCookie();
+        if (!adminId) {
+            alert('无法获取用户ID');
+            return;
+        }
+        fetch(`${API_BASE_URL}/api/admin/ban-user`, {
+            method: 'POST',
+            headers: { 'Content-Type': 'application/json' },
+            body: JSON.stringify({ userid: user.userid }),
+        })
+            .then((res) => { if (!res.ok) throw new Error('Network response was not ok'); return res.json(); })
+            .then(() => {
+                // 重新获取用户列表,触发页面重新渲染
+                fetchSuspiciousUsers();
+                fetchCheatUsers();
+            })
+            .catch((err) => console.error('封禁用户失败:', err));
+    }
+
+    // 解封作弊用户
+    const handleUnban = (user) => {
+        const adminId = getUserIdFromCookie();
+        if (!adminId) {
+            alert('无法获取用户ID');
+            return;
+        }
+        fetch(`${API_BASE_URL}/api/admin/unban-user`, {
+            method: 'POST',
+            headers: { 'Content-Type': 'application/json' },
+            body: JSON.stringify({ userid: user.userid }),
+        })
+            .then((res) => { if (!res.ok) throw new Error('Network response was not ok'); return res.json(); })
+            .then(() => {
+                // 重新获取用户列表,触发页面重新渲染
+                fetchCheatUsers();
+                fetchSuspiciousUsers();
+            })
+            .catch((err) => console.error('解封用户失败:', err));
     };
 
+    // 保存系统参数到后端
+    const handleSave = () => {
+        const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
+        const userId = match ? match[2] : null;
+        if (!userId) {
+            alert('无法获取用户ID');
+            return;
+        }
+        fetch(`${API_BASE_URL}/api/save-config`, {
+            method: 'POST',
+            // credentials: 'include',
+            headers: { 'Content-Type': 'application/json' },
+            body: JSON.stringify({ userid: userId, ...config }),
+        })
+            .then((res) => {
+                if (!res.ok) throw new Error('Network response was not ok');
+                return res.json();
+            })
+            .then(() => alert('系统参数已保存'))
+            .catch((err) => console.error('保存系统参数失败:', err));
+    };
+
+    // 初始化时向后端请求系统参数及用户列表
+    useEffect(() => {
+        const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
+        const userId = match ? match[2] : null;
+        // console.log("User ID from cookie:", userId);
+        if (userId) {
+            // fetch config
+            fetch(`${API_BASE_URL}/api/admin/config?userid=${userId}`)
+                .then((res) => {
+                    if (!res.ok) throw new Error('Network response was not ok');
+                    return res.json();
+                })
+                .then((data) => {
+                    // console.log("Fetched system config:", data);
+                    setConfig(data);
+                })
+                .catch((err) => console.error('获取系统参数失败:', err));
+
+            // 初始获取用户列表
+            fetchCheatUsers();
+            fetchSuspiciousUsers();
+        }
+    }, []);
+
     return (
         <div style={{ padding: 40, maxWidth: 900, margin: "0 auto" }}>
             <h1 style={{ textAlign: "center", marginBottom: 32 }}>管理员页面</h1>
             {/* 参数设置 */}
-            <div style={{ marginBottom: 32, padding: 18, background: "#f7faff", borderRadius: 12, display: "flex", gap: 24, alignItems: "center" }}>
+            <div style={{ marginBottom: 32, padding: 18, background: "#f7faff", borderRadius: 12, display: "flex", gap: 24, alignItems: "center", justifyContent: "space-between" }}>
                 <b>系统参数:</b>
                 <label>
                     FarmNumber:
-                    <input type="number" name="FarmNumber" value={config.FarmNumber} onChange={handleConfigChange} style={{ width: 60, margin: "0 12px" }} />
+                    <input type="number" name="FarmNumber" value={config.FarmNumber} onChange={handleConfigChange} disabled style={{ width: 60, margin: "0 12px" }} />
                 </label>
                 <label>
                     FakeTime:
-                    <input type="number" name="FakeTime" value={config.FakeTime} onChange={handleConfigChange} style={{ width: 60, margin: "0 12px" }} />
+                    <input type="number" name="FakeTime" value={config.FakeTime} onChange={handleConfigChange} disabled style={{ width: 60, margin: "0 12px" }} />
                 </label>
                 <label>
                     BegVote:
-                    <input type="number" name="BegVote" value={config.BegVote} onChange={handleConfigChange} style={{ width: 60, margin: "0 12px" }} />
+                    <input type="number" name="BegVote" value={config.BegVote} onChange={handleConfigChange} disabled style={{ width: 60, margin: "0 12px" }} />
                 </label>
                 <label>
                     CheatTime:
-                    <input type="number" name="CheatTime" value={config.CheatTime} onChange={handleConfigChange} style={{ width: 60, margin: "0 12px" }} />
+                    <input type="number" name="CheatTime" value={config.CheatTime} onChange={handleConfigChange} disabled style={{ width: 60, margin: "0 12px" }} />
                 </label>
+                {/* <button
+                    onClick={handleSave}
+                    style={{
+                        background: '#1976d2',
+                        color: '#fff',
+                        border: 'none',
+                        borderRadius: 6,
+                        padding: '6px 18px',
+                        cursor: 'pointer',
+                        writingMode: 'horizontal-tb',
+                        whiteSpace: 'nowrap'
+                    }}
+                >
+                    保存
+                </button> */}
             </div>
             {/* 作弊用户 */}
             <div style={{ marginBottom: 32 }}>
@@ -61,7 +187,7 @@
                 <table style={{ width: "100%", background: "#fff", borderRadius: 10, boxShadow: "0 2px 8px #e0e7ff", marginBottom: 18 }}>
                     <thead>
                         <tr style={{ background: "#f5f5f5" }}>
-                            <th>user_id</th>
+                            {/* <th>user_id</th> */}
                             <th>email</th>
                             <th>username</th>
                             <th>account_status</th>
@@ -70,19 +196,19 @@
                     </thead>
                     <tbody>
                         {cheatUsers.map((u) => (
-                            <tr key={u.user_id}>
-                                <td>{u.user_id}</td>
+                            <tr key={u.userid}>
+                                {/* <td>{u.userid}</td> */}
                                 <td>{u.email}</td>
                                 <td>{u.username}</td>
-                                <td style={{ color: u.account_status === 1 ? "#e53935" : "#43a047" }}>
-                                    {u.account_status === 1 ? "封禁" : "正常"}
+                                <td style={{ color: "#e53935" }}>
+                                    {"封禁" }
                                 </td>
                                 <td>
                                     <button
-                                        style={{ background: "#e53935", color: "#fff", border: "none", borderRadius: 6, padding: "4px 14px", cursor: "pointer" }}
-                                        onClick={() => handleBan(u)}
+                                        style={{ background: "#13F31E", color: "#fff", border: "none", borderRadius: 6, padding: "4px 14px", cursor: "pointer" }}
+                                        onClick={() => handleUnban(u)}
                                     >
-                                        封禁
+                                        解封
                                     </button>
                                 </td>
                             </tr>
@@ -96,7 +222,7 @@
                 <table style={{ width: "100%", background: "#fff", borderRadius: 10, boxShadow: "0 2px 8px #e0e7ff" }}>
                     <thead>
                         <tr style={{ background: "#f5f5f5" }}>
-                            <th>user_id</th>
+                            {/* <th>user_id</th> */}
                             <th>email</th>
                             <th>username</th>
                             <th>account_status</th>
@@ -106,7 +232,7 @@
                     <tbody>
                         {suspiciousUsers.map((u) => (
                             <tr key={u.user_id}>
-                                <td>{u.user_id}</td>
+                                {/* <td>{u.user_id}</td> */}
                                 <td>{u.email}</td>
                                 <td>{u.username}</td>
                                 <td style={{ color: u.account_status === 1 ? "#e53935" : "#43a047" }}>