修复令牌验证逻辑,修改管理员dashboard,增加退出登录功能

Change-Id: I6a832763126dffd28733269044a1b1956c5b1106
diff --git a/Merge/back_rhj/__pycache__/config.cpython-310.pyc b/Merge/back_rhj/__pycache__/config.cpython-310.pyc
new file mode 100644
index 0000000..0d95d54
--- /dev/null
+++ b/Merge/back_rhj/__pycache__/config.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/__pycache__/__init__.cpython-310.pyc b/Merge/back_rhj/app/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..bc775fd
--- /dev/null
+++ b/Merge/back_rhj/app/__pycache__/__init__.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/__pycache__/routes.cpython-310.pyc b/Merge/back_rhj/app/__pycache__/routes.cpython-310.pyc
new file mode 100644
index 0000000..f5aa9b3
--- /dev/null
+++ b/Merge/back_rhj/app/__pycache__/routes.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/blueprints/__pycache__/recommend.cpython-310.pyc b/Merge/back_rhj/app/blueprints/__pycache__/recommend.cpython-310.pyc
new file mode 100644
index 0000000..caf8ad5
--- /dev/null
+++ b/Merge/back_rhj/app/blueprints/__pycache__/recommend.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/blueprints/__pycache__/scheduler.cpython-310.pyc b/Merge/back_rhj/app/blueprints/__pycache__/scheduler.cpython-310.pyc
new file mode 100644
index 0000000..44681a7
--- /dev/null
+++ b/Merge/back_rhj/app/blueprints/__pycache__/scheduler.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/functions/__pycache__/FAuth.cpython-310.pyc b/Merge/back_rhj/app/functions/__pycache__/FAuth.cpython-310.pyc
new file mode 100644
index 0000000..f4aa5b2
--- /dev/null
+++ b/Merge/back_rhj/app/functions/__pycache__/FAuth.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/__pycache__/__init__.cpython-310.pyc b/Merge/back_rhj/app/models/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..2434dcb
--- /dev/null
+++ b/Merge/back_rhj/app/models/__pycache__/__init__.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/__pycache__/email_verification.cpython-310.pyc b/Merge/back_rhj/app/models/__pycache__/email_verification.cpython-310.pyc
new file mode 100644
index 0000000..39d1bfa
--- /dev/null
+++ b/Merge/back_rhj/app/models/__pycache__/email_verification.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/__pycache__/users.cpython-310.pyc b/Merge/back_rhj/app/models/__pycache__/users.cpython-310.pyc
new file mode 100644
index 0000000..33b799e
--- /dev/null
+++ b/Merge/back_rhj/app/models/__pycache__/users.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recall/__pycache__/__init__.cpython-310.pyc b/Merge/back_rhj/app/models/recall/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..d6c32d0
--- /dev/null
+++ b/Merge/back_rhj/app/models/recall/__pycache__/__init__.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recall/__pycache__/ad_recall.cpython-310.pyc b/Merge/back_rhj/app/models/recall/__pycache__/ad_recall.cpython-310.pyc
new file mode 100644
index 0000000..92342d0
--- /dev/null
+++ b/Merge/back_rhj/app/models/recall/__pycache__/ad_recall.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recall/__pycache__/hot_recall.cpython-310.pyc b/Merge/back_rhj/app/models/recall/__pycache__/hot_recall.cpython-310.pyc
new file mode 100644
index 0000000..af23bff
--- /dev/null
+++ b/Merge/back_rhj/app/models/recall/__pycache__/hot_recall.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recall/__pycache__/multi_recall_manager.cpython-310.pyc b/Merge/back_rhj/app/models/recall/__pycache__/multi_recall_manager.cpython-310.pyc
new file mode 100644
index 0000000..59158dc
--- /dev/null
+++ b/Merge/back_rhj/app/models/recall/__pycache__/multi_recall_manager.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recall/__pycache__/swing_recall.cpython-310.pyc b/Merge/back_rhj/app/models/recall/__pycache__/swing_recall.cpython-310.pyc
new file mode 100644
index 0000000..5cd2aee
--- /dev/null
+++ b/Merge/back_rhj/app/models/recall/__pycache__/swing_recall.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recall/__pycache__/usercf_recall.cpython-310.pyc b/Merge/back_rhj/app/models/recall/__pycache__/usercf_recall.cpython-310.pyc
new file mode 100644
index 0000000..25df6ea
--- /dev/null
+++ b/Merge/back_rhj/app/models/recall/__pycache__/usercf_recall.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recall/ad_recall.py b/Merge/back_rhj/app/models/recall/ad_recall.py
new file mode 100644
index 0000000..cc2c602
--- /dev/null
+++ b/Merge/back_rhj/app/models/recall/ad_recall.py
@@ -0,0 +1,206 @@
+import pymysql
+from typing import List, Tuple, Dict
+import random
+class AdRecall:
+    """
+    广告召回算法实现
+    专门用于召回广告类型的内容
+    """
+    
+    def __init__(self, db_config: dict):
+        """
+        初始化广告召回模型
+        
+        Args:
+            db_config: 数据库配置
+        """
+        self.db_config = db_config
+        self.ad_items = []
+        
+    def _get_ad_items(self):
+        """获取广告物品列表"""
+        conn = pymysql.connect(**self.db_config)
+        try:
+            cursor = conn.cursor()
+            
+            # 获取所有广告帖子,按热度和发布时间排序
+            cursor.execute("""
+                SELECT 
+                    p.id,
+                    p.heat,
+                    p.created_at,
+                    COUNT(DISTINCT b.user_id) as interaction_count,
+                    DATEDIFF(NOW(), p.created_at) as days_since_created
+                FROM posts p
+                LEFT JOIN behaviors b ON p.id = b.post_id
+                WHERE p.is_advertisement = 1 AND p.status = 'published'
+                GROUP BY p.id, p.heat, p.created_at
+                ORDER BY p.heat DESC, p.created_at DESC
+            """)
+            
+            results = cursor.fetchall()
+            
+            # 计算广告分数
+            items_with_scores = []
+            for row in results:
+                post_id, heat, created_at, interaction_count, days_since_created = row
+                
+                # 处理 None 值
+                heat = heat or 0
+                interaction_count = interaction_count or 0
+                days_since_created = days_since_created or 0
+                
+                # 广告分数计算:热度 + 交互数 - 时间惩罚
+                # 新发布的广告给予更高权重
+                freshness_bonus = max(0, 30 - days_since_created) / 30.0  # 30 天内的新鲜度奖励
+                
+                ad_score = (
+                    heat * 0.6 +
+                    interaction_count * 0.3 +
+                    freshness_bonus * 100  # 新鲜度奖励
+                )
+                
+                items_with_scores.append((post_id, ad_score))
+            
+            # 按广告分数排序
+            self.ad_items = sorted(items_with_scores, key=lambda x: x[1], reverse=True)
+            
+        finally:
+            cursor.close()
+            conn.close()
+    
+    def train(self):
+        """训练广告召回模型"""
+        print("开始获取广告物品...")
+        self._get_ad_items()
+        print(f"广告召回模型训练完成,共 {len (self.ad_items)} 个广告物品")
+    
+    def recall(self, user_id: int, num_items: int = 10) -> List[Tuple[int, float]]:
+        """
+        为用户召回广告物品
+        
+        Args:
+            user_id: 用户 ID
+            num_items: 召回物品数量
+            
+        Returns:
+            List of (item_id, score) tuples
+        """
+        # 如果尚未训练,先进行训练
+        if not hasattr(self, 'ad_items') or not self.ad_items:
+            self.train()
+        
+        # 获取用户已交互的广告,避免重复推荐
+        conn = pymysql.connect(**self.db_config)
+        try:
+            cursor = conn.cursor()
+            cursor.execute("""
+                SELECT DISTINCT b.post_id 
+                FROM behaviors b
+                JOIN posts p ON b.post_id = p.id
+                WHERE b.user_id = %s AND p.is_advertisement = 1
+                AND b.type IN ('like', 'favorite', 'comment', 'view')
+            """, (user_id,))
+            
+            user_interacted_ads = set(row[0] for row in cursor.fetchall())
+            
+            # 获取用户的兴趣标签(基于历史行为)
+            cursor.execute("""
+                SELECT t.name, COUNT(*) as count
+                FROM behaviors b
+                JOIN posts p ON b.post_id = p.id
+                JOIN post_tags pt ON p.id = pt.post_id
+                JOIN tags t ON pt.tag_id = t.id
+                WHERE b.user_id = %s AND b.type IN ('like', 'favorite', 'comment')
+                GROUP BY t.name
+                ORDER BY count DESC
+                LIMIT 10
+            """, (user_id,))
+            
+            user_interest_tags = set(row[0] for row in cursor.fetchall())
+            
+        finally:
+            cursor.close()
+            conn.close()
+        
+        # 过滤掉用户已交互的广告
+        filtered_ads = [
+            (item_id, score) for item_id, score in self.ad_items
+            if item_id not in user_interacted_ads
+        ]
+        
+        # 如果没有未交互的广告,但有广告数据,返回评分最高的广告(可能用户会再次感兴趣)
+        if not filtered_ads and self.ad_items:
+            print(f"用户 {user_id} 已与所有广告交互,返回评分最高的广告")
+            filtered_ads = self.ad_items[:num_items]
+        
+        # 如果用户有兴趣标签,可以进一步个性化广告推荐
+        if user_interest_tags and filtered_ads:
+            filtered_ads = self._personalize_ads(filtered_ads, user_interest_tags)
+        
+        return filtered_ads[:num_items]
+    
+    def _personalize_ads(self, ad_list: List[Tuple[int, float]], user_interest_tags: set) -> List[Tuple[int, float]]:
+        """
+        根据用户兴趣标签个性化广告推荐
+        
+        Args:
+            ad_list: 广告列表
+            user_interest_tags: 用户兴趣标签
+            
+        Returns:
+            个性化后的广告列表
+        """
+        conn = pymysql.connect(**self.db_config)
+        try:
+            cursor = conn.cursor()
+            
+            personalized_ads = []
+            for ad_id, ad_score in ad_list:
+                # 获取广告的标签
+                cursor.execute("""
+                    SELECT t.name
+                    FROM post_tags pt
+                    JOIN tags t ON pt.tag_id = t.id
+                    WHERE pt.post_id = %s
+                """, (ad_id,))
+                
+                ad_tags = set(row[0] for row in cursor.fetchall())
+                
+                # 计算标签匹配度
+                tag_match_score = len(ad_tags & user_interest_tags) / max(len(user_interest_tags), 1)
+                
+                # 调整广告分数
+                final_score = ad_score * (1 + tag_match_score)
+                personalized_ads.append((ad_id, final_score))
+            
+            # 重新排序
+            personalized_ads.sort(key=lambda x: x[1], reverse=True)
+            return personalized_ads
+            
+        finally:
+            cursor.close()
+            conn.close()
+    
+    def get_random_ads(self, num_items: int = 5) -> List[Tuple[int, float]]:
+        """
+        获取随机广告(用于多样性)
+        
+        Args:
+            num_items: 返回物品数量
+            
+        Returns:
+            List of (item_id, score) tuples
+        """
+        if len(self.ad_items) <= num_items:
+            return self.ad_items
+        
+        # 随机选择但倾向于高分广告
+        weights = [score for _, score in self.ad_items]
+        selected_indices = random.choices(
+            range(len(self.ad_items)), 
+            weights=weights, 
+            k=num_items
+        )
+        
+        return [self.ad_items[i] for i in selected_indices]
diff --git a/Merge/back_rhj/app/models/recommend/__pycache__/LightGCN.cpython-310.pyc b/Merge/back_rhj/app/models/recommend/__pycache__/LightGCN.cpython-310.pyc
new file mode 100644
index 0000000..cf9b64f
--- /dev/null
+++ b/Merge/back_rhj/app/models/recommend/__pycache__/LightGCN.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recommend/__pycache__/base_model.cpython-310.pyc b/Merge/back_rhj/app/models/recommend/__pycache__/base_model.cpython-310.pyc
new file mode 100644
index 0000000..19f10bf
--- /dev/null
+++ b/Merge/back_rhj/app/models/recommend/__pycache__/base_model.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/models/recommend/__pycache__/operators.cpython-310.pyc b/Merge/back_rhj/app/models/recommend/__pycache__/operators.cpython-310.pyc
new file mode 100644
index 0000000..fa721af
--- /dev/null
+++ b/Merge/back_rhj/app/models/recommend/__pycache__/operators.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/services/__pycache__/lightgcn_scorer.cpython-310.pyc b/Merge/back_rhj/app/services/__pycache__/lightgcn_scorer.cpython-310.pyc
new file mode 100644
index 0000000..495528d
--- /dev/null
+++ b/Merge/back_rhj/app/services/__pycache__/lightgcn_scorer.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/services/__pycache__/recommendation_service.cpython-310.pyc b/Merge/back_rhj/app/services/__pycache__/recommendation_service.cpython-310.pyc
new file mode 100644
index 0000000..88ab960
--- /dev/null
+++ b/Merge/back_rhj/app/services/__pycache__/recommendation_service.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/utils/__pycache__/data_loader.cpython-310.pyc b/Merge/back_rhj/app/utils/__pycache__/data_loader.cpython-310.pyc
new file mode 100644
index 0000000..a145deb
--- /dev/null
+++ b/Merge/back_rhj/app/utils/__pycache__/data_loader.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/utils/__pycache__/graph_build.cpython-310.pyc b/Merge/back_rhj/app/utils/__pycache__/graph_build.cpython-310.pyc
new file mode 100644
index 0000000..b802281
--- /dev/null
+++ b/Merge/back_rhj/app/utils/__pycache__/graph_build.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/utils/__pycache__/parse_args.cpython-310.pyc b/Merge/back_rhj/app/utils/__pycache__/parse_args.cpython-310.pyc
new file mode 100644
index 0000000..548f131
--- /dev/null
+++ b/Merge/back_rhj/app/utils/__pycache__/parse_args.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_rhj/app/utils/__pycache__/scheduler_manager.cpython-310.pyc b/Merge/back_rhj/app/utils/__pycache__/scheduler_manager.cpython-310.pyc
new file mode 100644
index 0000000..a280927
--- /dev/null
+++ b/Merge/back_rhj/app/utils/__pycache__/scheduler_manager.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/__pycache__/config.cpython-310.pyc b/Merge/back_trm/__pycache__/config.cpython-310.pyc
index 47d55f3..0255d9e 100644
--- a/Merge/back_trm/__pycache__/config.cpython-310.pyc
+++ b/Merge/back_trm/__pycache__/config.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app.py b/Merge/back_trm/app.py
index af7fefc..f1a9ec1 100644
--- a/Merge/back_trm/app.py
+++ b/Merge/back_trm/app.py
@@ -10,7 +10,11 @@
 from app.functions.Fpost import Fpost;
 
 app = create_app()
-CORS(app, resources={r"/*": {"origins": "*"}})
+CORS(app,
+     resources={r"/*": {"origins": "*"}},
+     supports_credentials=True,
+     allow_headers=["Content-Type"]
+)
 
 proc=psutil.Process(os.getpid())
 @app.before_request
diff --git a/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc b/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc
index e22e52b..8eadf0e 100644
--- a/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc
+++ b/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/functions/Fpost.py b/Merge/back_trm/app/functions/Fpost.py
index 2237815..248ed13 100644
--- a/Merge/back_trm/app/functions/Fpost.py
+++ b/Merge/back_trm/app/functions/Fpost.py
@@ -6,6 +6,10 @@
 from sqlalchemy.orm import Session
 from ..models.logs import Log
 from ..models.syscost import PerformanceData
+# from ..models.token import Token
+from config import Config
+import requests
+
 class Fpost:
     def __init__(self,session:Session):
         self.session=session
@@ -48,13 +52,30 @@
     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):
+    def checkid(self, token, status=''):
+        """
+        使用前端传来的 token 调用 /verify_user 接口校验,
+        如果接口返回 success=False 或者角色不匹配则返回 False,否则 True。
+        """
+        print("---------------------------------------------------------")
+        try:
+            url = f"{Config.API_BASE_URL}/verify_user"
+            headers = {
+                'Authorization': f'Bearer {token}',
+                'Content-Type': 'application/json'
+            }
+            payload = {'token': token}
+            resp = requests.post(url, headers=headers, json=payload)
+        
+            print(resp.json())
+            if resp.status_code != 200:
+                return False
+            data = resp.json()
+            if not data.get('success'):
+                return False
+            return data.get('role') == status,data.get('user_id')
+        except Exception:
             return False
-        if res.role !=status:
-            return False
-        return True
     
     def review(self,postid,status):
         print(status)
@@ -65,43 +86,43 @@
         self.session.commit()
         return True
     
-    def createtoken(self, userid):
-        """
-        根据userid创建token并插入到数据库
-        :param userid: 用户ID
-        :return: 生成的token字符串
-        """
-        # 生成随机盐值
-        salt = secrets.token_hex(16)
+    # 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}"
+    #     # 创建哈希值:userid + 当前时间戳 + 随机盐值
+    #     current_time = str(datetime.now().timestamp())
+    #     hash_input = f"{userid}_{current_time}_{salt}"
         
-        # 生成SHA256哈希值作为token
-        token = hashlib.sha256(hash_input.encode()).hexdigest()
+    #     # 生成SHA256哈希值作为token
+    #     token = hashlib.sha256(hash_input.encode()).hexdigest()
         
-        # 设置时间
-        created_time = datetime.now()
-        expires_time = created_time + timedelta(days=1)  # 一天后过期
+    #     # 设置时间
+    #     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
-            )
+    #     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()
+    #         # 假设self.session是数据库会话对象
+    #         self.session.add(new_token)
+    #         self.session.commit()
             
-            return token
+    #         return token
             
-        except Exception as e:
-            self.session.rollback()
-            raise Exception(f"创建token失败: {str(e)}")
+    #     except Exception as e:
+    #         self.session.rollback()
+    #         raise Exception(f"创建token失败: {str(e)}")
         
     def recordlog(self,user_id,log_type,content,ip):
         """
@@ -152,5 +173,5 @@
             raise Exception(f"记录系统性能消耗失败: {e}")
     
     def getsyscost(self):
-        res= self.session.query(PerformanceData).all()
+        res = self.session.query(PerformanceData).order_by(PerformanceData.record_time.desc()).limit(200).all()
         return res
\ No newline at end of file
diff --git a/Merge/back_trm/app/functions/__pycache__/Fpost.cpython-310.pyc b/Merge/back_trm/app/functions/__pycache__/Fpost.cpython-310.pyc
index 3a49c6c..88279b4 100644
--- a/Merge/back_trm/app/functions/__pycache__/Fpost.cpython-310.pyc
+++ b/Merge/back_trm/app/functions/__pycache__/Fpost.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/models/__pycache__/token.cpython-310.pyc b/Merge/back_trm/app/models/__pycache__/token.cpython-310.pyc
new file mode 100644
index 0000000..ab59118
--- /dev/null
+++ b/Merge/back_trm/app/models/__pycache__/token.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/routes.py b/Merge/back_trm/app/routes.py
index 20cf99c..b1d1fd6 100644
--- a/Merge/back_trm/app/routes.py
+++ b/Merge/back_trm/app/routes.py
@@ -17,9 +17,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'superadmin')
+    checres,userid=f.checkid(data['userid'],'superadmin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要超级管理员才能执行修改用户角色的操作,但是当前用户不是超级管理员',
                      request.remote_addr)
@@ -27,12 +27,12 @@
     
     res=f.giveadmin(data['targetid'])
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      f"尝试修改用户{data['targetid']}角色为admin失败,用户不存在",
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'User not found'})
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                     'behavior', 
                     f'用户角色为admin修改成功,用户ID: {data["targetid"]} 被修改为管理员',
                     request.remote_addr)
@@ -46,9 +46,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'superadmin')
+    checres,userid=f.checkid(data['userid'],'superadmin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要超级管理员才能执行修改用户角色的操作,但是当前用户不是超级管理员',
                      request.remote_addr)
@@ -56,12 +56,12 @@
     
     res=f.giveuser(data['targetid'])
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      f"尝试修改用户{data['targetid']}为user失败,用户不存在",
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'User not found'})
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                     'behavior', 
                     f'用户角色修改成功,用户ID: {data["targetid"]} 被修改为普通用户',
                     request.remote_addr)
@@ -76,9 +76,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'superadmin')
+    checres,userid=f.checkid(data['userid'],'superadmin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要超级管理员才能执行修改用户角色的操作,但是当前用户不是超级管理员',
                      request.remote_addr)
@@ -86,12 +86,12 @@
     
     res=f.givesuperadmin(data['targetid'])
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      f'尝试修改用户{data["targetid"]}角色为superadmin失败,用户不存在',
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'User not found'})
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                     'behavior', 
                     f'用户角色修改成功,用户ID: {data["targetid"]} 被修改为超级管理员',
                     request.remote_addr)
@@ -105,9 +105,11 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'superadmin')
+    checres,userid=f.checkid(data['userid'],'superadmin')
+    print("+++++++++++++++++++++++++++++++++++++++++++++++++")
+    print(checres)
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要超级管理员才能执行获取用户列表的操作,但是当前用户不是超级管理员',
                      request.remote_addr)
@@ -121,7 +123,7 @@
             'role': datai[2]
         })
 
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                     'access', 
                     '获取用户列表成功',
                     request.remote_addr)
@@ -135,9 +137,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'admin')
+    checres,userid=f.checkid(data['userid'],'admin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要管理员才能执行获取帖子列表的操作,但是当前用户不是管理员',
                      request.remote_addr)
@@ -150,7 +152,7 @@
             'title': datai[1],
             'status': datai[2]
         })
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                  'access', 
                  '获取帖子列表成功',
                  request.remote_addr)
@@ -163,21 +165,21 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'admin')
+    checres,userid=f.checkid(data['userid'],'admin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要管理员才能执行获取帖子详情的操作,但是当前用户不是管理员',
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     res=f.getpost(data['postid'])
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      f'尝试获取帖子{data["postid"]}失败,帖子不存在',
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Post not found'})
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                  'access', 
                  f'获取帖子详情成功,帖子ID: {data["postid"]}',
                  request.remote_addr)
@@ -190,9 +192,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'admin')
+    checres,userid=f.checkid(data['userid'],'admin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要管理员才能执行帖子审核的操作,但是当前用户不是管理员',
                      request.remote_addr)
@@ -200,12 +202,12 @@
     
     res=f.review(data['postid'],data['status'])
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      f'尝试审核帖子{data["postid"]}失败,帖子不存在',
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Post not found'})
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                  'behavior', 
                  f'帖子审核成功,帖子ID: {data["postid"]} 状态更新为 {data["status"]}',
                  request.remote_addr)
@@ -220,9 +222,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'admin')
+    checres,userid=f.checkid(data['userid'],'admin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要管理员才能执行Nginx认证的操作,但是当前用户不是管理员',
                      request.remote_addr)
@@ -230,12 +232,12 @@
     
     res=f.nginxauth(data['postid'],data['status'])
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      f'尝试更新Nginx认证状态失败,帖子{data["postid"]}不存在',
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Post not found'})
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                  'behavior', 
                  f'Nginx认证状态更新成功,帖子ID: {data["postid"]} 状态更新为 {data["status"]}',
                  request.remote_addr)
@@ -248,9 +250,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'superadmin')
+    checres,userid=f.checkid(data['userid'],'superadmin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要管理员才能执行获取系统性能消耗的操作,但是当前用户不是管理员',
                      request.remote_addr)
@@ -258,13 +260,13 @@
     
     res=f.getsyscost()
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '尝试获取系统性能消耗数据失败,数据不存在',
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'No performance data found'})
     
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                  'access', 
                  '获取系统性能消耗数据成功',
                  request.remote_addr)
@@ -287,9 +289,9 @@
     SessionLocal = sessionmaker(bind=engine)
     session = SessionLocal()
     f=Fpost(session)
-    checres=f.checkid(data['userid'],'superadmin')
+    checres,userid=f.checkid(data['userid'],'superadmin')
     if(not checres):
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '系统需要管理员才能执行获取日志的操作,但是当前用户不是管理员',
                      request.remote_addr)
@@ -297,13 +299,13 @@
     
     res=f.getrecordlog()
     if not res:
-        f.recordlog(data['userid'],
+        f.recordlog(userid,
                      'error', 
                      '尝试获取日志失败,日志不存在',
                      request.remote_addr)
         return jsonify({'status': 'error', 'message': 'No logs found'})
     
-    f.recordlog(data['userid'],
+    f.recordlog(userid,
                  'access', 
                  '获取日志成功',
                  request.remote_addr)
diff --git a/Merge/back_trm/config.py b/Merge/back_trm/config.py
index d4a2e88..424b53e 100644
--- a/Merge/back_trm/config.py
+++ b/Merge/back_trm/config.py
@@ -9,4 +9,5 @@
     SQLPORT=os.getenv('SQLPORT')
     SQLNAME=os.getenv('SQLNAME')
     SQLUSER=os.getenv('SQLUSER')
-    SQLPWD=os.getenv('SQLPWD')
\ No newline at end of file
+    SQLPWD=os.getenv('SQLPWD')
+    API_BASE_URL = 'http://10.126.59.25:8082'
\ No newline at end of file
diff --git a/Merge/back_wzy/__pycache__/config.cpython-310.pyc b/Merge/back_wzy/__pycache__/config.cpython-310.pyc
index 325d84e..bd938d3 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/config.py b/Merge/back_wzy/config.py
index 205b03d..6a9bf8c 100644
--- a/Merge/back_wzy/config.py
+++ b/Merge/back_wzy/config.py
@@ -1,12 +1,12 @@
 # config.py
 import os
-
+from dotenv import load_dotenv
+load_dotenv()
 basedir = os.path.abspath(os.path.dirname(__file__))
-
 class Config:
     SECRET_KEY = os.environ.get('SECRET_KEY', 'you-will-never-guess')
     SQLALCHEMY_DATABASE_URI = os.environ.get(
         'SQLURL'
     )
     SQLALCHEMY_TRACK_MODIFICATIONS = False
-    SQLURL=os.getenv('SQLURL')
+    SQLURL = os.getenv('SQLURL')
\ No newline at end of file
diff --git a/Merge/front/src/api/posts_trm.js b/Merge/front/src/api/posts_trm.js
index f28ec19..43d7105 100644
--- a/Merge/front/src/api/posts_trm.js
+++ b/Merge/front/src/api/posts_trm.js
@@ -1,16 +1,20 @@
+import { getAuthToken } from '../utils/auth'
 const BASE = 'http://10.126.59.25:5713'  // 后端地址
 
+
+
 /**
  * 获取待审核的帖子列表
  * POST /apostlist
  * @param {number|string} userId 平台管理员的用户 ID
  * @returns Promise<[ {id, title, status}, … ]>
  */
-export async function fetchPosts(userId) {
+export async function fetchPosts() {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/apostlist`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId })
+    body: JSON.stringify({ userid })
   })
   if (!res.ok) throw new Error(`fetchPosts: ${res.status}`)
   
@@ -43,11 +47,12 @@
  * 审核通过
  * POST /areview
  */
-export async function approvePost(postId, userId) {
+export async function approvePost(postId) {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/areview`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId, postid: postId, status: 'published' })
+    body: JSON.stringify({ userid, postid: postId, status: 'published' })
   })
   if (!res.ok) throw new Error(`approvePost: ${res.status}`)
   return res.json()
@@ -57,11 +62,12 @@
  * 驳回
  * POST /areview
  */
-export async function rejectPost(postId, userId) {
+export async function rejectPost(postId) {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/areview`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId, postid: postId, status: 'rejected' })
+    body: JSON.stringify({ userid, postid: postId, status: 'rejected' })
   })
   if (!res.ok) throw new Error(`rejectPost: ${res.status}`)
   return res.json()
@@ -74,11 +80,12 @@
  * @param {number|string} userId 平台管理员的用户 ID
  * @returns Promise<{id, title, content, status}>
  */
-export async function fetchPost(postId, userId) {
+export async function fetchPost(postId) {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/agetpost`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId, postid: postId })
+    body: JSON.stringify({ userid, postid: postId })
   })
   if (!res.ok) throw new Error(`fetchPost: ${res.status}`)
   return res.json()
@@ -90,41 +97,45 @@
  * @param {number|string} userId 平台管理员的用户 ID
  * @returns Promise<[ {id, name, role}, … ]>
  */
-export async function fetchUserList(userId) {
+export async function fetchUserList() {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/sgetuserlist`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId })
+    body: JSON.stringify({ userid })
   })
   if (!res.ok) throw new Error(`fetchUserList: ${res.status}`)
   return res.json()
 }
 
-export async function giveAdmin(userId, targetId) {
+export async function giveAdmin(targetId) {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/sgiveadmin`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId, targetid: targetId })
+    body: JSON.stringify({ userid, targetid: targetId })
   })
   if (!res.ok) throw new Error(`giveAdmin: ${res.status}`)
   return res.json()
 }
 
-export async function giveSuperAdmin(userId, targetId) {
+export async function giveSuperAdmin(targetId) {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/sgivesuperadmin`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId, targetid: targetId })
+    body: JSON.stringify({ userid, targetid: targetId })
   })
   if (!res.ok) throw new Error(`giveSuperAdmin: ${res.status}`)
   return res.json()
 }
 
-export async function giveUser(userId, targetId) {
+export async function giveUser(targetId) {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/sgiveuser`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId, targetid: targetId })
+    body: JSON.stringify({ userid, targetid: targetId })
   })
   if (!res.ok) throw new Error(`giveUser: ${res.status}`)
   return res.json()
@@ -133,14 +144,14 @@
 /**
  * 获取事务日志
  * POST /getrecordlog
- * @param {number|string} userId 平台管理员的用户 ID
  * @returns Promise<[ {id, user_id, type, content, ip, created_at}, … ]>
  */
-export async function fetchRecordLog(userId) {
+export async function fetchRecordLog() {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/getrecordlog`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId })
+    body: JSON.stringify({ userid })
   })
   if (!res.ok) throw new Error(`fetchRecordLog: ${res.status}`)
   const json = await res.json()
@@ -166,11 +177,12 @@
  * @param {number|string} userId 平台管理员的用户 ID
  * @returns Promise<[ {id, record_time, endpoint, elapsed_time, cpu_user, cpu_system, memory_rss}, … ]>
  */
-export async function fetchSysCost(userId) {
+export async function fetchSysCost() {
+  const userid = getAuthToken()
   const res = await fetch(`${BASE}/getsyscost`, {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ userid: userId })
+    body: JSON.stringify({ userid })
   })
   if (!res.ok) throw new Error(`fetchSysCost: ${res.status}`)
   const json = await res.json()
diff --git a/Merge/front/src/components/PerformanceLogs.js b/Merge/front/src/components/PerformanceLogs.js
index be9eb99..00f7e7d 100644
--- a/Merge/front/src/components/PerformanceLogs.js
+++ b/Merge/front/src/components/PerformanceLogs.js
@@ -7,8 +7,10 @@
 
 function PerformanceLogs({ userId }) {
   const [data, setData] = useState([]);
+  const [loading, setLoading] = useState(true);
 
   useEffect(() => {
+    setLoading(true);
     fetchSysCost(userId)
       .then(list => {
         const msList = list.map(item => ({
@@ -22,9 +24,40 @@
         console.log('Converted data:', msList[0]); // debug first item
         setData(msList);
       })
-      .catch(err => console.error('fetchSysCost error:', err));
+      .catch(err => console.error('fetchSysCost error:', err))
+      .finally(() => setLoading(false));
   }, [userId]);
 
+  if (loading) {
+    return (
+      <section className="dashboard-performance">
+        <div style={{ 
+          display: 'flex', 
+          justifyContent: 'center', 
+          alignItems: 'center', 
+          height: '400px',
+          flexDirection: 'column'
+        }}>
+          <div style={{
+            border: '4px solid #f3f3f3',
+            borderTop: '4px solid #3498db',
+            borderRadius: '50%',
+            width: '50px',
+            height: '50px',
+            animation: 'spin 1s linear infinite'
+          }}></div>
+          <p style={{ marginTop: '20px', color: '#666' }}>加载中...</p>
+          <style>{`
+            @keyframes spin {
+              0% { transform: rotate(0deg); }
+              100% { transform: rotate(360deg); }
+            }
+          `}</style>
+        </div>
+      </section>
+    );
+  }
+
   return (
     <section className="dashboard-performance">
       {/* 响应时间图表 */}
diff --git a/Merge/front/src/components/TransactionLogs.js b/Merge/front/src/components/TransactionLogs.js
index 9df8f67..24a31cb 100644
--- a/Merge/front/src/components/TransactionLogs.js
+++ b/Merge/front/src/components/TransactionLogs.js
@@ -2,14 +2,76 @@
 import { fetchRecordLog } from '../api/posts_trm';
 
 function TransactionLogs({ userId }) {
-  const [records, setRecords] = useState([]);
+  const [allRecords, setAllRecords] = useState([]);
+  const [currentPage, setCurrentPage] = useState(1);
+  const [pageSize] = useState(10);
+  const [loading, setLoading] = useState(false);
+
+  const loadRecords = async () => {
+    setLoading(true);
+    try {
+      const data = await fetchRecordLog();
+      setAllRecords(data);
+    } catch (err) {
+      console.error('fetchRecordLog error:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
 
   useEffect(() => {
-    fetchRecordLog(userId)
-      .then(data => setRecords(data))
-      .catch(err => console.error('fetchRecordLog error:', err));
+    loadRecords();
   }, [userId]);
 
+  const total = allRecords.length;
+  const totalPages = Math.ceil(total / pageSize);
+  
+  // 计算当前页显示的数据
+  const startIndex = (currentPage - 1) * pageSize;
+  const endIndex = startIndex + pageSize;
+  const currentRecords = allRecords.slice(startIndex, endIndex);
+
+  const handlePrevPage = () => {
+    if (currentPage > 1) {
+      setCurrentPage(currentPage - 1);
+    }
+  };
+
+  const handleNextPage = () => {
+    if (currentPage < totalPages) {
+      setCurrentPage(currentPage + 1);
+    }
+  };
+
+  const handlePageClick = (page) => {
+    setCurrentPage(page);
+  };
+
+  const renderPageNumbers = () => {
+    const pages = [];
+    const maxVisiblePages = 5;
+    
+    let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
+    let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
+    
+    if (endPage - startPage < maxVisiblePages - 1) {
+      startPage = Math.max(1, endPage - maxVisiblePages + 1);
+    }
+
+    for (let i = startPage; i <= endPage; i++) {
+      pages.push(
+        <button
+          key={i}
+          onClick={() => handlePageClick(i)}
+          className={`page-btn ${currentPage === i ? 'active' : ''}`}
+        >
+          {i}
+        </button>
+      );
+    }
+    return pages;
+  };
+
   return (
     <section className="dashboard-logs">
       <table className="admin-table">
@@ -24,25 +86,96 @@
           </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>
+          {loading ? (
+            <tr>
+              <td colSpan="6" style={{ textAlign: 'center' }}>加载中...</td>
+            </tr>
+          ) : currentRecords.length > 0 ? (
+            currentRecords.map((r, i) => (
+              <tr key={r.id || 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>
+      
+      {totalPages > 1 && (
+        <div className="pagination">
+          <button 
+            onClick={handlePrevPage} 
+            disabled={currentPage === 1}
+            className="page-btn"
+          >
+            上一页
+          </button>
+          
+          {renderPageNumbers()}
+          
+          <button 
+            onClick={handleNextPage} 
+            disabled={currentPage === totalPages}
+            className="page-btn"
+          >
+            下一页
+          </button>
+          
+          <span className="page-info">
+            第 {currentPage} 页,共 {totalPages} 页,总计 {total} 条记录
+          </span>
+        </div>
+      )}
+      
+      <style jsx>{`
+        .pagination {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          gap: 8px;
+          margin-top: 16px;
+          flex-wrap: wrap;
+        }
+        
+        .page-btn {
+          padding: 6px 12px;
+          border: 1px solid #ddd;
+          background: white;
+          cursor: pointer;
+          border-radius: 4px;
+          transition: all 0.2s;
+        }
+        
+        .page-btn:hover:not(:disabled) {
+          background: #f5f5f5;
+          border-color: #999;
+        }
+        
+        .page-btn:disabled {
+          opacity: 0.5;
+          cursor: not-allowed;
+        }
+        
+        .page-btn.active {
+          background: #007bff;
+          color: white;
+          border-color: #007bff;
+        }
+        
+        .page-info {
+          margin-left: 16px;
+          font-size: 14px;
+          color: #666;
+        }
+      `}</style>
     </section>
   );
 }
diff --git a/Merge/front/src/components/UserProfile.jsx b/Merge/front/src/components/UserProfile.jsx
index 8421997..53618db 100644
--- a/Merge/front/src/components/UserProfile.jsx
+++ b/Merge/front/src/components/UserProfile.jsx
@@ -29,7 +29,8 @@
   useTheme,
   CircularProgress,
   Snackbar,
-  Alert
+  Alert,
+  Menu
 } from '@mui/material';
 import { useParams } from 'react-router-dom';
 import { 
@@ -123,6 +124,7 @@
   const [isEditing, setIsEditing] = useState(false);
   const [followers, setFollowers] = useState([]);
   const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+  const [anchorEl, setAnchorEl] = useState(null);
   
   // 用户数据状态
   const [currentUser, setCurrentUser] = useState(null);
@@ -149,6 +151,8 @@
     location: ''
   });
 
+  const menuOpen = Boolean(anchorEl);
+
   // 显示提示信息
   const showSnackbar = (message, severity = 'success') => {
     setSnackbar({ open: true, message, severity });
@@ -390,6 +394,16 @@
     navigate(`/user/${userId}`);
   };
 
+  const handleMenuOpen = (e) => setAnchorEl(e.currentTarget);
+  const handleMenuClose = () => setAnchorEl(null);
+  const handleLogout = () => {
+    handleMenuClose();
+    // 清理本地存储
+    localStorage.clear();
+    // 在此处添加退出登录逻辑,比如清理本地存储并跳转
+    navigate('/login');
+  };
+
   if (loading) {
     return (
       <Box sx={{ 
@@ -531,9 +545,18 @@
                         >
                           {profileUser.is_following ? '已关注' : '关注'}
                         </Button>
-                        <IconButton sx={{ ml: 1 }}>
+                        <IconButton sx={{ ml: 1 }} onClick={handleMenuOpen}>
                           <MoreVert />
                         </IconButton>
+                        <Menu
+                          anchorEl={anchorEl}
+                          open={menuOpen}
+                          onClose={handleMenuClose}
+                          anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
+                          transformOrigin={{ vertical: 'top', horizontal: 'right' }}
+                        >
+                          <MenuItem onClick={handleLogout}>退出登录</MenuItem>
+                        </Menu>
                       </>
                     )}
                   </Box>