Merge "身份令牌验证与推荐接口"
diff --git a/LJC/personalpage/src/App.js b/LJC/personalpage/src/App.js
index ed241f4..f11e487 100644
--- a/LJC/personalpage/src/App.js
+++ b/LJC/personalpage/src/App.js
@@ -31,7 +31,7 @@
         <main className="py-5">
           <div className="max-w-6xl mx-auto">
             <Routes>
-              <Route path="/" element={<Navigate to="/user/11" replace />} />
+              {/* <Route path="/" element={<Navigate to="/user/11" replace />} /> */}
               <Route path="/user/:userId" element={<UserProfileRoute />} />
             </Routes>
           </div>
diff --git a/LJC/personalpage/src/components/EditProfileForm.jsx b/LJC/personalpage/src/components/EditProfileForm.jsx
deleted file mode 100644
index d33cbcb..0000000
--- a/LJC/personalpage/src/components/EditProfileForm.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import React, { useState } from 'react';
-import { FaCamera, FaTimes } from 'react-icons/fa';
-
-const EditProfileForm = ({ user, onSave, onCancel }) => {
-  const [avatar, setAvatar] = useState(user.avatar || '');
-  const [bio, setBio] = useState(user.bio || '');
-  const [gender, setGender] = useState('secret');
-  const [birthday, setBirthday] = useState('');
-  const [location, setLocation] = useState('');
-
-  const handleSubmit = (e) => {
-    e.preventDefault();
-    onSave({ avatar, bio });
-  };
-
-  return (
-    <form onSubmit={handleSubmit}>
-      <div className="mb-6">
-        <label className="block text-sm font-medium text-gray-700 mb-2">头像</label>
-        <div className="flex items-center">
-          <div className="relative">
-            <div className="w-20 h-20 rounded-full bg-gradient-to-r from-pink-300 to-orange-300 flex items-center justify-center">
-              {avatar ? (
-                <img src={avatar} alt="Avatar" className="w-full h-full rounded-full" />
-              ) : (
-                <div className="text-white text-2xl">{user.username.charAt(0)}</div>
-              )}
-            </div>
-            <button 
-              type="button"
-              className="absolute bottom-0 right-0 bg-white rounded-full p-1 shadow-md"
-            >
-              <FaCamera className="text-gray-700 text-sm" />
-            </button>
-          </div>
-          <div className="ml-4">
-            <input
-              type="text"
-              value={avatar}
-              onChange={(e) => setAvatar(e.target.value)}
-              placeholder="输入头像URL"
-              className="w-full rounded-md border-gray-300 shadow-sm text-sm"
-            />
-          </div>
-        </div>
-      </div>
-      
-      <div className="mb-4">
-        <label className="block text-sm font-medium text-gray-700 mb-2">个人简介</label>
-        <textarea
-          value={bio}
-          onChange={(e) => setBio(e.target.value)}
-          rows="3"
-          maxLength="100"
-          className="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm"
-          placeholder="介绍一下自己吧~"
-        />
-        <div className="text-right text-xs text-gray-500 mt-1">{bio.length}/100</div>
-      </div>
-      
-      <div className="grid grid-cols-2 gap-4 mb-4">
-        <div>
-          <label className="block text-sm font-medium text-gray-700 mb-2">性别</label>
-          <select
-            value={gender}
-            onChange={(e) => setGender(e.target.value)}
-            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm"
-          >
-            <option value="secret">保密</option>
-            <option value="male">男</option>
-            <option value="female">女</option>
-          </select>
-        </div>
-        
-        <div>
-          <label className="block text-sm font-medium text-gray-700 mb-2">生日</label>
-          <input
-            type="date"
-            value={birthday}
-            onChange={(e) => setBirthday(e.target.value)}
-            className="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm"
-          />
-        </div>
-      </div>
-      
-      <div className="mb-6">
-        <label className="block text-sm font-medium text-gray-700 mb-2">地区</label>
-        <input
-          type="text"
-          value={location}
-          onChange={(e) => setLocation(e.target.value)}
-          placeholder="填写你所在的城市"
-          className="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm"
-        />
-      </div>
-      
-      <div className="flex justify-end space-x-3">
-        <button 
-          type="button"
-          onClick={onCancel}
-          className="px-5 py-2 bg-gray-100 text-gray-700 rounded-full text-sm hover:bg-gray-200"
-        >
-          取消
-        </button>
-        <button 
-          type="submit"
-          className="px-5 py-2 bg-red-500 text-white rounded-full text-sm hover:bg-red-600"
-        >
-          保存
-        </button>
-      </div>
-    </form>
-  );
-};
-
-export default EditProfileForm;
\ No newline at end of file
diff --git a/LJC/personalpage/src/components/FavoritePosts.jsx b/LJC/personalpage/src/components/FavoritePosts.jsx
deleted file mode 100644
index 4033d7b..0000000
--- a/LJC/personalpage/src/components/FavoritePosts.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { getFavorites } from '../services/api';
-import { FaHeart } from 'react-icons/fa';
-
-const FavoritePosts = ({ userId }) => {
-  const [favorites, setFavorites] = useState([]);
-  const [loading, setLoading] = useState(true);
-
-  useEffect(() => {
-    const fetchFavorites = async () => {
-      try {
-        setLoading(true);
-        const response = await getFavorites(userId);
-        setFavorites(response.data);
-      } catch (error) {
-        console.error('Failed to fetch favorites:', error);
-      } finally {
-        setLoading(false);
-      }
-    };
-    
-    if (userId) {
-      fetchFavorites();
-    }
-  }, [userId]);
-
-  if (loading) {
-    return (
-      <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
-        {[1, 2, 3, 4, 5, 6].map(item => (
-          <div key={item} className="bg-gray-100 rounded-xl aspect-square animate-pulse"></div>
-        ))}
-      </div>
-    );
-  }
-
-  if (favorites.length === 0) {
-    return (
-      <div className="text-center py-16">
-        <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-red-100 text-red-500 mb-4">
-          <FaHeart className="text-2xl" />
-        </div>
-        <h3 className="text-lg font-medium text-gray-900">暂无收藏内容</h3>
-        <p className="mt-1 text-gray-500">你还没有收藏任何笔记</p>
-      </div>
-    );
-  }
-
-  // 模拟瀑布流布局数据
-  const waterfallData = favorites.map(post => ({
-    ...post,
-    height: Math.floor(Math.random() * 100) + 200 // 随机高度
-  }));
-
-  return (
-    <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
-      {waterfallData.map(post => (
-        <div 
-          key={post.id} 
-          className="bg-white rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-shadow"
-        >
-          <div 
-            className="relative bg-gray-200" 
-            style={{ height: `${post.height}px` }}
-          >
-            {/* 占位图片 */}
-            <div className="absolute inset-0 bg-gradient-to-br from-pink-100 to-orange-100"></div>
-            
-            {/* 类型标签 */}
-            <div className="absolute top-2 right-2 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded-full">
-              {post.type === 'image' ? '图文' : 
-               post.type === 'video' ? '视频' : '文档'}
-            </div>
-            
-            {/* 收藏标记 */}
-            <div className="absolute bottom-2 right-2 bg-red-500 rounded-full p-1">
-              <FaHeart className="text-white text-xs" />
-            </div>
-          </div>
-          
-          <div className="p-3">
-            <h3 className="font-medium line-clamp-2">{post.title}</h3>
-            <div className="flex items-center mt-2 text-xs text-gray-500">
-              <span>❤️ 2.5k</span>
-              <span className="mx-2">•</span>
-              <span>⭐ 156</span>
-            </div>
-          </div>
-        </div>
-      ))}
-    </div>
-  );
-};
-
-export default FavoritePosts;
\ No newline at end of file
diff --git a/LJC/personalpage/src/components/FollowButton.jsx b/LJC/personalpage/src/components/FollowButton.jsx
deleted file mode 100644
index 9e738d4..0000000
--- a/LJC/personalpage/src/components/FollowButton.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import { followUser, unfollowUser } from '../services/api';
-
-const FollowButton = ({ userId, isFollowing, onFollowChange }) => {
-  const handleFollow = async () => {
-    try {
-      if (isFollowing) {
-        await unfollowUser(userId);
-        onFollowChange(false);
-      } else {
-        await followUser(userId);
-        onFollowChange(true);
-      }
-    } catch (error) {
-      console.error('关注操作失败:', error);
-    }
-  };
-
-  return (
-    <button 
-      onClick={handleFollow}
-      className={`px-6 py-2 rounded-full text-sm font-medium transition-all ${
-        isFollowing 
-          ? 'bg-gray-100 text-gray-800 hover:bg-gray-200' 
-          : 'bg-red-500 text-white hover:bg-red-600'
-      }`}
-    >
-      {isFollowing ? '已关注' : '关注'}
-    </button>
-  );
-};
-
-export default FollowButton;
\ No newline at end of file
diff --git a/Merge/back_trm/app/__pycache__/__init__.cpython-310.pyc b/Merge/back_trm/app/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..9368fce
--- /dev/null
+++ b/Merge/back_trm/app/__pycache__/__init__.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc b/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc
new file mode 100644
index 0000000..e22e52b
--- /dev/null
+++ b/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/routes.py b/Merge/back_trm/app/routes.py
index 625ea6d..20cf99c 100644
--- a/Merge/back_trm/app/routes.py
+++ b/Merge/back_trm/app/routes.py
@@ -287,7 +287,7 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'admin')
+    checres=f.checkid(data['userid'],'superadmin')
     if(not checres):
         f.recordlog(data['userid'],
                      'error', 
diff --git a/Merge/back_wzy/__init__.py b/Merge/back_wzy/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Merge/back_wzy/__init__.py
diff --git a/Merge/back_wzy/__pycache__/app.cpython-310.pyc b/Merge/back_wzy/__pycache__/app.cpython-310.pyc
new file mode 100644
index 0000000..b733167
--- /dev/null
+++ b/Merge/back_wzy/__pycache__/app.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_wzy/__pycache__/config.cpython-310.pyc b/Merge/back_wzy/__pycache__/config.cpython-310.pyc
index 0d915b2..325d84e 100644
--- a/Merge/back_wzy/__pycache__/config.cpython-310.pyc
+++ b/Merge/back_wzy/__pycache__/config.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_wzy/app.py b/Merge/back_wzy/app.py
index a90f62f..0c371fd 100644
--- a/Merge/back_wzy/app.py
+++ b/Merge/back_wzy/app.py
@@ -1,13 +1,19 @@
 # app.py
 
-from flask import Flask
+from flask import Flask,g,request
+import psutil
+import time
+import os
 from flask_cors import CORS
 from config import Config
 from extensions import db, migrate
-
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from utils.Fpost import Fpost;
+app = Flask(__name__)
+app.config.from_object(Config)
 def create_app():
-    app = Flask(__name__)
-    app.config.from_object(Config)
+    
 
     # 启用 CORS:允许前端 http://localhost:5173 发起跨域请求
     # 生产环境请根据实际域名调整 origins
@@ -24,6 +30,38 @@
 
     return app
 
+proc=psutil.Process(os.getpid())
+@app.before_request
+def before_request():
+    g.start_time=time.time()
+    g.start_cpu=proc.cpu_times()
+    g.start_mem=proc.memory_info()
+
+@app.after_request
+def after_request(response):
+    end_time = time.time()
+    end_cpu = proc.cpu_times()
+    end_mem = proc.memory_info()
+
+    elapsed = end_time - g.start_time
+    cpu_user = end_cpu.user - g.start_cpu.user
+    cpu_sys  = end_cpu.system - g.start_cpu.system
+    mem_rss  = end_mem.rss - g.start_mem.rss
+
+    #写入性能消耗
+    engine=create_engine(Config.SQLURL)
+    SessionLocal = sessionmaker(bind=engine)
+    session = SessionLocal()
+    f=Fpost(session)
+    f.recordsyscost(
+        request.path,
+        elapsed,
+        cpu_user,
+        cpu_sys,
+        mem_rss
+    )
+    return response
+
 # 只有直接用 python app.py 时,这段才会执行
 if __name__ == '__main__':
     app = create_app()
diff --git a/Merge/back_wzy/config.py b/Merge/back_wzy/config.py
index e5bdb32..205b03d 100644
--- a/Merge/back_wzy/config.py
+++ b/Merge/back_wzy/config.py
@@ -9,3 +9,4 @@
         'SQLURL'
     )
     SQLALCHEMY_TRACK_MODIFICATIONS = False
+    SQLURL=os.getenv('SQLURL')
diff --git a/Merge/back_wzy/models/__pycache__/logs.cpython-310.pyc b/Merge/back_wzy/models/__pycache__/logs.cpython-310.pyc
new file mode 100644
index 0000000..3ce0df5
--- /dev/null
+++ b/Merge/back_wzy/models/__pycache__/logs.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_wzy/models/__pycache__/syscost.cpython-310.pyc b/Merge/back_wzy/models/__pycache__/syscost.cpython-310.pyc
new file mode 100644
index 0000000..13585ba
--- /dev/null
+++ b/Merge/back_wzy/models/__pycache__/syscost.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_wzy/models/syscost.py b/Merge/back_wzy/models/syscost.py
new file mode 100644
index 0000000..bbde029
--- /dev/null
+++ b/Merge/back_wzy/models/syscost.py
@@ -0,0 +1,15 @@
+from sqlalchemy import Column, BigInteger, DateTime, String, Float, func
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+class PerformanceData(Base):
+    __tablename__ = 'performance_data'
+
+    id = Column(BigInteger, primary_key=True, autoincrement=True)
+    record_time = Column(DateTime, nullable=False, server_default=func.now(), comment='记录时间')
+    endpoint = Column(String(255), nullable=True, comment='请求接口路径')
+    elapsed_time = Column(Float, nullable=False, comment='总耗时(秒)')
+    cpu_user = Column(Float, nullable=False, comment='用户态 CPU 时间差(秒)')
+    cpu_system = Column(Float, nullable=False, comment='系统态 CPU 时间差(秒)')
+    memory_rss = Column(BigInteger, nullable=False, comment='RSS 内存增量(字节)')
\ No newline at end of file
diff --git a/Merge/back_wzy/utils/Fpost.py b/Merge/back_wzy/utils/Fpost.py
new file mode 100644
index 0000000..c8b5f48
--- /dev/null
+++ b/Merge/back_wzy/utils/Fpost.py
@@ -0,0 +1,156 @@
+from models.user import User as users
+from models.post import Post as post
+import secrets
+import hashlib
+from datetime import datetime, timedelta
+from sqlalchemy.orm import Session
+from models.logs import Log
+from models.syscost import PerformanceData
+class Fpost:
+    def __init__(self,session:Session):
+        self.session=session
+        return
+    
+
+    def getlist(self):
+        results = self.session.query(post.id, post.title,post.status)
+        return results
+    
+    def getuserlist(self):
+        results= self.session.query(users.id, users.username, users.role)
+        return results
+
+    def giveadmin(self,userid):
+        res=self.session.query(users).filter(users.id==userid).first()
+        if not res:
+            return False
+        res.role='admin'
+        self.session.commit()
+        return True
+    
+    def giveuser(self,userid):
+        res=self.session.query(users).filter(users.id==userid).first()
+        if not res:
+            return False
+        res.role='user'
+        self.session.commit()
+        return True
+    
+    def givesuperadmin(self,userid):
+        res=self.session.query(users).filter(users.id==userid).first()
+        if not res:
+            return False
+        res.role='superadmin'
+        self.session.commit()
+        return True
+
+
+    def getpost(self,postid):
+        res=self.session.query(post).filter(post.id==postid).first()
+        return res
+    def checkid(self,userid,status=''):
+        res=self.session.query(users).filter(users.id==userid).first()
+        if(not res):
+            return False
+        if res.role !=status:
+            return False
+        return True
+    
+    def review(self,postid,status):
+        print(status)
+        res=self.session.query(post).filter(post.id==postid).first()
+        if not res:
+            return False
+        res.status=status
+        self.session.commit()
+        return True
+    
+    def createtoken(self, userid):
+        """
+        根据userid创建token并插入到数据库
+        :param userid: 用户ID
+        :return: 生成的token字符串
+        """
+        # 生成随机盐值
+        salt = secrets.token_hex(16)
+        
+        # 创建哈希值:userid + 当前时间戳 + 随机盐值
+        current_time = str(datetime.now().timestamp())
+        hash_input = f"{userid}_{current_time}_{salt}"
+        
+        # 生成SHA256哈希值作为token
+        token = hashlib.sha256(hash_input.encode()).hexdigest()
+        
+        # 设置时间
+        created_time = datetime.now()
+        expires_time = created_time + timedelta(days=1)  # 一天后过期
+        
+        try:
+            # 创建新的token记录
+            new_token = Token(
+                token=token,
+                expires_at=expires_time,
+                created_at=created_time
+            )
+            
+            # 假设self.session是数据库会话对象
+            self.session.add(new_token)
+            self.session.commit()
+            
+            return token
+            
+        except Exception as e:
+            self.session.rollback()
+            raise Exception(f"创建token失败: {str(e)}")
+        
+    def recordlog(self,user_id,log_type,content,ip):
+        """
+        记录日志
+        :param user_id: 用户ID
+        :param log_type: 日志类型,'access','error','behavior','system'
+        :param content: 日志内容
+        :param ip: IP地址
+        """
+        try:
+            new_log = Log(
+                user_id=user_id,
+                type=log_type,
+                content=content,
+                ip=ip
+            )
+            self.session.add(new_log)
+            self.session.commit()
+        except Exception as e:
+            self.session.rollback()
+            raise Exception(f"记录日志失败: {str(e)}")
+    
+    def getrecordlog(self):
+       res= self.session.query(Log).all()
+       return res 
+    
+    def recordsyscost(self, endpoint: str, elapsed_time: float, cpu_user: float, cpu_system: float, memory_rss: int):
+        """
+        记录系统性能消耗到 performance_data 表
+        :param endpoint: 请求接口路径
+        :param elapsed_time: 总耗时(秒)
+        :param cpu_user: 用户态 CPU 时间差(秒)
+        :param cpu_system: 系统态 CPU 时间差(秒)
+        :param memory_rss: RSS 内存增量(字节)
+        """
+        try:
+            new_record = PerformanceData(
+                endpoint=endpoint,
+                elapsed_time=elapsed_time,
+                cpu_user=cpu_user,
+                cpu_system=cpu_system,
+                memory_rss=memory_rss
+            )
+            self.session.add(new_record)
+            self.session.commit()
+        except Exception as e:
+            self.session.rollback()
+            raise Exception(f"记录系统性能消耗失败: {e}")
+    
+    def getsyscost(self):
+        res= self.session.query(PerformanceData).all()
+        return res
\ No newline at end of file
diff --git a/Merge/back_wzy/utils/__init__.py b/Merge/back_wzy/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Merge/back_wzy/utils/__init__.py
diff --git a/Merge/back_wzy/utils/__pycache__/Fpost.cpython-310.pyc b/Merge/back_wzy/utils/__pycache__/Fpost.cpython-310.pyc
new file mode 100644
index 0000000..92bc6b9
--- /dev/null
+++ b/Merge/back_wzy/utils/__pycache__/Fpost.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_wzy/utils/__pycache__/__init__.cpython-310.pyc b/Merge/back_wzy/utils/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..2aacbf6
--- /dev/null
+++ b/Merge/back_wzy/utils/__pycache__/__init__.cpython-310.pyc
Binary files differ
diff --git a/Merge/front/package.json b/Merge/front/package.json
index 31c1d3e..54a8783 100644
--- a/Merge/front/package.json
+++ b/Merge/front/package.json
@@ -14,7 +14,8 @@
     "web-vitals": "^2.1.4",
     "lucide-react": "^0.468.0",
     "antd": "^4.24.0",
-    "crypto-js": "^4.2.0"
+    "crypto-js": "^4.2.0",
+    "recharts": "^2.1.9"
   },
   "scripts": {
     "start": "react-scripts start",
diff --git a/Merge/front/src/App.jsx b/Merge/front/src/App.jsx
index 770bc0a..3ab9fca 100644
--- a/Merge/front/src/App.jsx
+++ b/Merge/front/src/App.jsx
@@ -1,22 +1,19 @@
-import React from 'react'
-import { BrowserRouter as Router } from 'react-router-dom'
-import Header from './components/Header'
-import Sidebar from './components/Sidebar'
-import AppRoutes from './router/App'
-import './App.css'
+import React from 'react';
+import Header from './components/Header';
+import Sidebar from './components/Sidebar';
+import AppRoutes from './router/App';
+import './App.css';
 
 export default function App() {
   return (
-    <Router>
-      <div className="app">
-        <Header />
-        <Sidebar />
-        <main className="main-content">
-          <div className="content-wrapper">
-            <AppRoutes />
-          </div>
-        </main>
-      </div>
-    </Router>
-  )
-}
+    <div className="app">
+      <Header />
+      <Sidebar />
+      <main className="main-content">
+        <div className="content-wrapper">
+          <AppRoutes />
+        </div>
+      </main>
+    </div>
+  );
+}
\ No newline at end of file
diff --git a/Merge/front/src/api/posts.js b/Merge/front/src/api/posts_trm.js
similarity index 68%
rename from Merge/front/src/api/posts.js
rename to Merge/front/src/api/posts_trm.js
index 37acf43..f28ec19 100644
--- a/Merge/front/src/api/posts.js
+++ b/Merge/front/src/api/posts_trm.js
@@ -128,4 +128,64 @@
   })
   if (!res.ok) throw new Error(`giveUser: ${res.status}`)
   return res.json()
+}
+
+/**
+ * 获取事务日志
+ * POST /getrecordlog
+ * @param {number|string} userId 平台管理员的用户 ID
+ * @returns Promise<[ {id, user_id, type, content, ip, created_at}, … ]>
+ */
+export async function fetchRecordLog(userId) {
+  const res = await fetch(`${BASE}/getrecordlog`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ userid: userId })
+  })
+  if (!res.ok) throw new Error(`fetchRecordLog: ${res.status}`)
+  const json = await res.json()
+  console.log('fetchRecordLog response:', json)
+  if (json.status === 'error' && json.message === 'Unauthorized') {
+    throw new Error('Unauthorized')
+  }
+  let list
+  if (Array.isArray(json)) {
+    list = json
+  } else if (Array.isArray(json.data)) {
+    list = json.data
+  } else {
+    list = []
+  }
+  console.log('Normalized record log list:', list)
+  return list
+}
+
+/**
+ * 获取系统性能消耗数据
+ * POST /getsyscost
+ * @param {number|string} userId 平台管理员的用户 ID
+ * @returns Promise<[ {id, record_time, endpoint, elapsed_time, cpu_user, cpu_system, memory_rss}, … ]>
+ */
+export async function fetchSysCost(userId) {
+  const res = await fetch(`${BASE}/getsyscost`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ userid: userId })
+  })
+  if (!res.ok) throw new Error(`fetchSysCost: ${res.status}`)
+  const json = await res.json()
+  console.log('fetchSysCost response:', json)
+  if (json.status === 'error' && json.message === 'Unauthorized') {
+    throw new Error('Unauthorized')
+  }
+  let list
+  if (Array.isArray(json)) {
+    list = json
+  } else if (Array.isArray(json.data)) {
+    list = json.data
+  } else {
+    list = []
+  }
+  console.log('Normalized sys cost list:', list)
+  return list
 }
\ No newline at end of file
diff --git a/Merge/front/src/components/Admin.js b/Merge/front/src/components/Admin.js
index da11100..2d97495 100644
--- a/Merge/front/src/components/Admin.js
+++ b/Merge/front/src/components/Admin.js
@@ -2,7 +2,7 @@
 import React, { useState, useEffect, useMemo, useCallback } from 'react';
 import { Layout, Tabs, Input, List, Card, Button, Tag, Spin, Typography, Divider } from 'antd';
 import '../style/Admin.css';
-import { fetchPosts, approvePost, rejectPost } from '../api/posts';
+import { fetchPosts, approvePost, rejectPost } from '../api/posts_trm';
 
 export default function Admin() {
   const ADMIN_USER_ID = 2;
diff --git a/Merge/front/src/components/LogsDashboard.js b/Merge/front/src/components/LogsDashboard.js
index 1bd6cb7..22047e2 100644
--- a/Merge/front/src/components/LogsDashboard.js
+++ b/Merge/front/src/components/LogsDashboard.js
@@ -1,4 +1,5 @@
 import React, { useEffect, useState } from 'react';
+import { NavLink, Outlet } from 'react-router-dom';
 import '../style/Admin.css';
 
 function LogsDashboard() {
@@ -19,25 +20,17 @@
   return (
     <div className="admin-container">
       <h2>运行日志 & 性能 Dashboard</h2>
-      <section className="dashboard-stats">
-        <pre>{JSON.stringify(stats, null, 2)}</pre>
-      </section>
-      <section className="dashboard-logs">
-        <table className="admin-table">
-          <thead>
-            <tr><th>时间</th><th>级别</th><th>消息</th></tr>
-          </thead>
-          <tbody>
-            {logs.map((log, i) => (
-              <tr key={i}>
-                <td>{new Date(log.time).toLocaleString()}</td>
-                <td>{log.level}</td>
-                <td>{log.message}</td>
-              </tr>
-            ))}
-          </tbody>
-        </table>
-      </section>
+      <nav className="dashboard-nav">
+        <NavLink to="transactions" className={({ isActive }) => isActive ? 'active' : ''}>
+          事务日志
+        </NavLink>
+        <NavLink to="performance" className={({ isActive }) => isActive ? 'active' : ''}>
+          性能日志
+        </NavLink>
+      </nav>
+
+      {/* nested routes will render here */}
+      <Outlet />
     </div>
   );
 }
diff --git a/Merge/front/src/components/PerformanceLogs.js b/Merge/front/src/components/PerformanceLogs.js
new file mode 100644
index 0000000..be9eb99
--- /dev/null
+++ b/Merge/front/src/components/PerformanceLogs.js
@@ -0,0 +1,94 @@
+import React, { useState, useEffect } from 'react';
+import {
+  ResponsiveContainer, LineChart, Line,
+  XAxis, YAxis, Tooltip, CartesianGrid, Legend
+} from 'recharts';
+import { fetchSysCost } from '../api/posts_trm';
+
+function PerformanceLogs({ userId }) {
+  const [data, setData] = useState([]);
+
+  useEffect(() => {
+    fetchSysCost(userId)
+      .then(list => {
+        const msList = list.map(item => ({
+          ...item,
+          elapsed_time: item.elapsed_time * 1000,
+          cpu_user: item.cpu_user * 1000,
+          cpu_system: item.cpu_system * 1000,
+          memory_rss: item.memory_rss / (1024 * 1024*8), // convert bytes to MB
+          record_time_ts: new Date(item.record_time).getTime()    // add numeric timestamp
+        }));
+        console.log('Converted data:', msList[0]); // debug first item
+        setData(msList);
+      })
+      .catch(err => console.error('fetchSysCost error:', err));
+  }, [userId]);
+
+  return (
+    <section className="dashboard-performance">
+      {/* 响应时间图表 */}
+      <div style={{ marginBottom: '30px' }}>
+        <h3>响应时间</h3>
+        <ResponsiveContainer width="100%" height={300}>
+          <LineChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
+            <CartesianGrid strokeDasharray="3 3" />
+            <XAxis
+              dataKey="record_time_ts"
+              type="number"
+              domain={['dataMin', 'dataMax']}
+              tickFormatter={ts => new Date(ts).toLocaleTimeString()}
+            />
+            <YAxis domain={[0, 'auto']} label={{ value: '时间 (ms)', angle: -90, position: 'insideLeft' }} />
+            <Tooltip formatter={(val) => typeof val === 'number' ? `${val.toFixed(2)} ms` : val} />
+            <Legend />
+            <Line type="monotone" dataKey="elapsed_time" stroke="#8884d8" name="响应时间 (ms)" />
+          </LineChart>
+        </ResponsiveContainer>
+      </div>
+
+      {/* CPU时间图表 */}
+      <div style={{ marginBottom: '30px' }}>
+        <h3>CPU 使用时间</h3>
+        <ResponsiveContainer width="100%" height={300}>
+          <LineChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
+            <CartesianGrid strokeDasharray="3 3" />
+            <XAxis
+              dataKey="record_time_ts"
+              type="number"
+              domain={['dataMin', 'dataMax']}
+              tickFormatter={ts => new Date(ts).toLocaleTimeString()}
+            />
+            <YAxis domain={[0, 'auto']} label={{ value: '时间 (ms)', angle: -90, position: 'insideLeft' }} />
+            <Tooltip formatter={(val) => typeof val === 'number' ? `${val.toFixed(2)} ms` : val} />
+            <Legend />
+            <Line type="monotone" dataKey="cpu_user" stroke="#82ca9d" name="CPU 用户时间 (ms)" />
+            <Line type="monotone" dataKey="cpu_system" stroke="#ffc658" name="CPU 系统时间 (ms)" />
+          </LineChart>
+        </ResponsiveContainer>
+      </div>
+
+      {/* 内存图表 */}
+      <div style={{ marginBottom: '30px' }}>
+        <h3>内存使用</h3>
+        <ResponsiveContainer width="100%" height={300}>
+          <LineChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
+            <CartesianGrid strokeDasharray="3 3" />
+            <XAxis
+              dataKey="record_time_ts"
+              type="number"
+              domain={['dataMin', 'dataMax']}
+              tickFormatter={ts => new Date(ts).toLocaleTimeString()}
+            />
+            <YAxis domain={[0, 'auto']} label={{ value: '内存 (MB)', angle: -90, position: 'insideLeft' }} />
+            <Tooltip formatter={(val) => typeof val === 'number' ? `${val.toFixed(2)} MB` : val} />
+            <Legend />
+            <Line type="monotone" dataKey="memory_rss" stroke="#ff7300" name="内存 RSS" />
+          </LineChart>
+        </ResponsiveContainer>
+      </div>
+    </section>
+  );
+}
+
+export default PerformanceLogs;
diff --git a/Merge/front/src/components/SuperAdmin.js b/Merge/front/src/components/SuperAdmin.js
index 817b708..f24e5d4 100644
--- a/Merge/front/src/components/SuperAdmin.js
+++ b/Merge/front/src/components/SuperAdmin.js
@@ -1,7 +1,7 @@
 import React, { useState, useEffect } from 'react';
 import { NavLink, Outlet } from 'react-router-dom';
 import { Spin } from 'antd';
-import { fetchUserList } from '../api/posts';
+import { fetchUserList } from '../api/posts_trm';
 import '../style/SuperAdmin.css';
 
 export default function SuperAdmin() {
diff --git a/Merge/front/src/components/TransactionLogs.js b/Merge/front/src/components/TransactionLogs.js
new file mode 100644
index 0000000..9df8f67
--- /dev/null
+++ b/Merge/front/src/components/TransactionLogs.js
@@ -0,0 +1,50 @@
+import React, { useState, useEffect } from 'react';
+import { fetchRecordLog } from '../api/posts_trm';
+
+function TransactionLogs({ userId }) {
+  const [records, setRecords] = useState([]);
+
+  useEffect(() => {
+    fetchRecordLog(userId)
+      .then(data => setRecords(data))
+      .catch(err => console.error('fetchRecordLog error:', err));
+  }, [userId]);
+
+  return (
+    <section className="dashboard-logs">
+      <table className="admin-table">
+        <thead>
+          <tr>
+            <th>ID</th>
+            <th>用户ID</th>
+            <th>类型</th>
+            <th>内容</th>
+            <th>IP</th>
+            <th>创建时间</th>
+          </tr>
+        </thead>
+        <tbody>
+          {records.length > 0
+            ? records.map((r, i) => (
+                <tr key={i}>
+                  <td>{r.id}</td>
+                  <td>{r.user_id}</td>
+                  <td>{r.type}</td>
+                  <td>{r.content}</td>
+                  <td>{r.ip}</td>
+                  <td>{new Date(r.created_at).toLocaleString()}</td>
+                </tr>
+              ))
+            : (
+              <tr>
+                <td colSpan="6" style={{ textAlign: 'center' }}>暂无数据</td>
+              </tr>
+            )
+          }
+        </tbody>
+      </table>
+    </section>
+  );
+}
+
+export default TransactionLogs;
diff --git a/Merge/front/src/components/UserManagement.js b/Merge/front/src/components/UserManagement.js
index 4bd05c5..a48f8cf 100644
--- a/Merge/front/src/components/UserManagement.js
+++ b/Merge/front/src/components/UserManagement.js
@@ -1,7 +1,7 @@
 import React, { useState, useEffect } from 'react';
 import '../style/Admin.css';
 import { Select, message, Table } from 'antd';
-import { fetchUserList, giveUser, giveAdmin, giveSuperAdmin } from '../api/posts';
+import { fetchUserList, giveUser, giveAdmin, giveSuperAdmin } from '../api/posts_trm';
 
 const { Option } = Select;
 const ROLE_LIST = ['用户', '管理员', '超级管理员'];
diff --git a/Merge/front/src/index.js b/Merge/front/src/index.js
index 1ce450d..56520f8 100644
--- a/Merge/front/src/index.js
+++ b/Merge/front/src/index.js
@@ -8,8 +8,8 @@
 const root = ReactDOM.createRoot(document.getElementById('root'));
 root.render(
   <React.StrictMode>
-   
-    <App />
-    
+    <BrowserRouter>
+      <App />
+    </BrowserRouter>
   </React.StrictMode>
 );
\ No newline at end of file
diff --git a/Merge/front/src/router/App.js b/Merge/front/src/router/App.js
index e1b9454..4d606f9 100644
--- a/Merge/front/src/router/App.js
+++ b/Merge/front/src/router/App.js
@@ -20,6 +20,9 @@
 import ForgotPasswordPage from '../pages/ForgotPasswordPage/ForgotPasswordPage';
 import TestDashboard from '../pages/TestDashboard/TestDashboard';
 
+import TransactionLogs from '../components/TransactionLogs';
+import PerformanceLogs from '../components/PerformanceLogs';
+
 export default function AppRoutes() {
   return (
     <Routes>
@@ -44,9 +47,6 @@
       <Route path="/forgot-password" element={<ForgotPasswordPage />} />
       <Route path="/test-dashboard" element={<TestDashboard />} />
 
-      {/* 最后一个兜底 */}
-      <Route path="*" element={<PlaceholderPage pageId="home" />} />
-
       {/* 普通管理员,无 header */}
       <Route path="admin" element={<AdminPage />} />
 
@@ -54,8 +54,17 @@
       <Route path="superadmin" element={<SuperAdmin />}>
         <Route index element={<Navigate to="users" replace />} />
         <Route path="users" element={<UserManagement superAdminId={3} />} />
-        <Route path="dashboard" element={<LogsDashboard />} />
+
+        {/* dashboard as layout */}
+        <Route path="dashboard" element={<LogsDashboard />}>
+          <Route index element={<Navigate to="transactions" replace />} />
+          <Route path="transactions" element={<TransactionLogs userId={1} />} />
+          <Route path="performance" element={<PerformanceLogs userId={1} />} />
+        </Route>
       </Route>
+
+      {/* 最后一个兜底,放在最末尾 */}
+      <Route path="*" element={<PlaceholderPage pageId="home" />} />
     </Routes>
   );
 }
\ No newline at end of file
diff --git a/Merge/front/src/style/Admin.css b/Merge/front/src/style/Admin.css
index 4a5bcb7..90e244d 100644
--- a/Merge/front/src/style/Admin.css
+++ b/Merge/front/src/style/Admin.css
@@ -386,4 +386,9 @@
 .ant-layout-sider .ant-menu-item:nth-child(1),
 .ant-layout-sider .ant-menu-item:nth-child(2) {
   color: var(--xiaohongshu-red) !important;
+}
+
+.dashboard-nav {
+  display: flex;
+  gap: 1rem;
 }
\ No newline at end of file