合并login

Change-Id: Ie06ed019cbb00d52e0b9e1f3c7a56c947b57a42c
diff --git a/Merge/back_trm/app.py b/Merge/back_trm/app.py
index 3c7fb86..af7fefc 100644
--- a/Merge/back_trm/app.py
+++ b/Merge/back_trm/app.py
@@ -1,8 +1,47 @@
 from app import create_app
 from flask_cors import CORS
+import os
+import psutil
+from flask import Flask,g,request
+import time
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from config import Config
+from app.functions.Fpost import Fpost;
 
 app = create_app()
 CORS(app, resources={r"/*": {"origins": "*"}})
 
+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
 if __name__ == "__main__":
     app.run(debug=True,port=5713,host='0.0.0.0')
\ 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
deleted file mode 100644
index f713fad..0000000
--- a/Merge/back_trm/app/__pycache__/__init__.cpython-310.pyc
+++ /dev/null
Binary files differ
diff --git a/Merge/back_trm/app/__pycache__/__init__.cpython-312.pyc b/Merge/back_trm/app/__pycache__/__init__.cpython-312.pyc
deleted file mode 100644
index ec28c7e..0000000
--- a/Merge/back_trm/app/__pycache__/__init__.cpython-312.pyc
+++ /dev/null
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
deleted file mode 100644
index 5166bf4..0000000
--- a/Merge/back_trm/app/__pycache__/routes.cpython-310.pyc
+++ /dev/null
Binary files differ
diff --git a/Merge/back_trm/app/functions/Fpost.py b/Merge/back_trm/app/functions/Fpost.py
index 7d6ccd2..2237815 100644
--- a/Merge/back_trm/app/functions/Fpost.py
+++ b/Merge/back_trm/app/functions/Fpost.py
@@ -4,6 +4,8 @@
 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
@@ -99,4 +101,56 @@
             
         except Exception as e:
             self.session.rollback()
-            raise Exception(f"创建token失败: {str(e)}")
\ No newline at end of file
+            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_trm/app/functions/__init__.py b/Merge/back_trm/app/functions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Merge/back_trm/app/functions/__init__.py
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 f9b1bc6..3a49c6c 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/functions/__pycache__/__init__.cpython-310.pyc b/Merge/back_trm/app/functions/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..ed5a973
--- /dev/null
+++ b/Merge/back_trm/app/functions/__pycache__/__init__.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/models/__pycache__/logs.cpython-310.pyc b/Merge/back_trm/app/models/__pycache__/logs.cpython-310.pyc
new file mode 100644
index 0000000..f1e86e3
--- /dev/null
+++ b/Merge/back_trm/app/models/__pycache__/logs.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/models/__pycache__/syscost.cpython-310.pyc b/Merge/back_trm/app/models/__pycache__/syscost.cpython-310.pyc
new file mode 100644
index 0000000..9831ffd
--- /dev/null
+++ b/Merge/back_trm/app/models/__pycache__/syscost.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_trm/app/models/logs.py b/Merge/back_trm/app/models/logs.py
new file mode 100644
index 0000000..ac8cf1c
--- /dev/null
+++ b/Merge/back_trm/app/models/logs.py
@@ -0,0 +1,19 @@
+from sqlalchemy import Column, BigInteger, Integer, Enum, Text, String, TIMESTAMP, ForeignKey, Index
+from sqlalchemy.sql import func
+from . import Base  # adjust if Base lives elsewhere
+
+class Log(Base):
+    __tablename__ = 'logs'
+    __table_args__ = (
+        Index('user_id', 'user_id'),
+        Index('idx_logs_created', 'created_at'),
+    )
+
+    id = Column(BigInteger, primary_key=True, autoincrement=True, comment='日志ID')
+    user_id = Column(Integer, ForeignKey('users.id', ondelete='SET NULL'), comment='用户ID')
+    type = Column(Enum('access', 'error', 'behavior', 'system',
+                       name='logs_type_enum'), nullable=False, comment='日志类型')
+    content = Column(Text, nullable=False, comment='日志内容')
+    ip = Column(String(45), nullable=True, comment='IP地址')
+    created_at = Column(TIMESTAMP, server_default=func.current_timestamp(),
+                        nullable=True, comment='记录时间')
diff --git a/Merge/back_trm/app/models/syscost.py b/Merge/back_trm/app/models/syscost.py
new file mode 100644
index 0000000..bbde029
--- /dev/null
+++ b/Merge/back_trm/app/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_trm/app/routes.py b/Merge/back_trm/app/routes.py
index 41b022b..625ea6d 100644
--- a/Merge/back_trm/app/routes.py
+++ b/Merge/back_trm/app/routes.py
@@ -5,8 +5,10 @@
 from config import Config
 from flask import jsonify,request
 
+
 main = Blueprint('main', __name__)
 
+
 @main.route('/sgiveadmin',methods=['POST','GET'])
 def giveadmin():
     data=request.get_json()
@@ -17,12 +19,23 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'superadmin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要超级管理员才能执行修改用户角色的操作,但是当前用户不是超级管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     
     res=f.giveadmin(data['targetid'])
     if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     f"尝试修改用户{data['targetid']}角色为admin失败,用户不存在",
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'User not found'})
-    
+    f.recordlog(data['userid'],
+                    'behavior', 
+                    f'用户角色为admin修改成功,用户ID: {data["targetid"]} 被修改为管理员',
+                    request.remote_addr)
     return jsonify({'status': 'success', 'message': 'User role updated to admin'})
 
 @main.route('/sgiveuser',methods=['POST','GET'])
@@ -35,12 +48,23 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'superadmin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要超级管理员才能执行修改用户角色的操作,但是当前用户不是超级管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     
     res=f.giveuser(data['targetid'])
     if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     f"尝试修改用户{data['targetid']}为user失败,用户不存在",
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'User not found'})
-    
+    f.recordlog(data['userid'],
+                    'behavior', 
+                    f'用户角色修改成功,用户ID: {data["targetid"]} 被修改为普通用户',
+                    request.remote_addr)
     return jsonify({'status': 'success', 'message': 'User role updated to user'})
 
 
@@ -54,12 +78,23 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'superadmin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要超级管理员才能执行修改用户角色的操作,但是当前用户不是超级管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     
     res=f.givesuperadmin(data['targetid'])
     if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     f'尝试修改用户{data["targetid"]}角色为superadmin失败,用户不存在',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'User not found'})
-    
+    f.recordlog(data['userid'],
+                    'behavior', 
+                    f'用户角色修改成功,用户ID: {data["targetid"]} 被修改为超级管理员',
+                    request.remote_addr)
     return jsonify({'status': 'success', 'message': 'User role updated to superadmin'})
 
 @main.route('/sgetuserlist',methods=['POST','GET'])
@@ -72,6 +107,10 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'superadmin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要超级管理员才能执行获取用户列表的操作,但是当前用户不是超级管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     res=f.getuserlist()
     respons=[]
@@ -81,6 +120,11 @@
             'username': datai[1],
             'role': datai[2]
         })
+
+    f.recordlog(data['userid'],
+                    'access', 
+                    '获取用户列表成功',
+                    request.remote_addr)
     return jsonify(respons)
 
 @main.route('/apostlist',methods=['POST','GET'])
@@ -93,6 +137,10 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'admin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要管理员才能执行获取帖子列表的操作,但是当前用户不是管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     res=f.getlist()
     respons=[]
@@ -102,6 +150,10 @@
             'title': datai[1],
             'status': datai[2]
         })
+    f.recordlog(data['userid'],
+                 'access', 
+                 '获取帖子列表成功',
+                 request.remote_addr)
     return jsonify(respons)
 
 @main.route('/agetpost',methods=['POST','GET'])
@@ -113,9 +165,22 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'admin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要管理员才能执行获取帖子详情的操作,但是当前用户不是管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     res=f.getpost(data['postid'])
-
+    if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     f'尝试获取帖子{data["postid"]}失败,帖子不存在',
+                     request.remote_addr)
+        return jsonify({'status': 'error', 'message': 'Post not found'})
+    f.recordlog(data['userid'],
+                 'access', 
+                 f'获取帖子详情成功,帖子ID: {data["postid"]}',
+                 request.remote_addr)
     return jsonify(res.to_dict() if res else {})
 
 @main.route('/areview',methods=['POST','GET'])
@@ -127,12 +192,23 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'admin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要管理员才能执行帖子审核的操作,但是当前用户不是管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     
     res=f.review(data['postid'],data['status'])
     if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     f'尝试审核帖子{data["postid"]}失败,帖子不存在',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Post not found'})
-    
+    f.recordlog(data['userid'],
+                 'behavior', 
+                 f'帖子审核成功,帖子ID: {data["postid"]} 状态更新为 {data["status"]}',
+                 request.remote_addr)
     return jsonify({'status': 'success', 'message': 'Post reviewed successfully'})
 
 
@@ -146,10 +222,102 @@
     f=Fpost(session)
     checres=f.checkid(data['userid'],'admin')
     if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要管理员才能执行Nginx认证的操作,但是当前用户不是管理员',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Unauthorized'})
     
     res=f.nginxauth(data['postid'],data['status'])
     if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     f'尝试更新Nginx认证状态失败,帖子{data["postid"]}不存在',
+                     request.remote_addr)
         return jsonify({'status': 'error', 'message': 'Post not found'})
+    f.recordlog(data['userid'],
+                 'behavior', 
+                 f'Nginx认证状态更新成功,帖子ID: {data["postid"]} 状态更新为 {data["status"]}',
+                 request.remote_addr)
+    return jsonify({'status': 'success', 'message': 'Nginx auth updated successfully'})
+
+@main.route('/getsyscost',methods=['POST','GET'])
+def getsyscost():
+    data=request.get_json()
+    engine=create_engine(Config.SQLURL)
+    SessionLocal = sessionmaker(bind=engine)
+    session = SessionLocal()
+    f=Fpost(session)
+    checres=f.checkid(data['userid'],'superadmin')
+    if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要管理员才能执行获取系统性能消耗的操作,但是当前用户不是管理员',
+                     request.remote_addr)
+        return jsonify({'status': 'error', 'message': 'Unauthorized'})
     
-    return jsonify({'status': 'success', 'message': 'Nginx auth updated successfully'})
\ No newline at end of file
+    res=f.getsyscost()
+    if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     '尝试获取系统性能消耗数据失败,数据不存在',
+                     request.remote_addr)
+        return jsonify({'status': 'error', 'message': 'No performance data found'})
+    
+    f.recordlog(data['userid'],
+                 'access', 
+                 '获取系统性能消耗数据成功',
+                 request.remote_addr)
+    resdata = []
+    for datai in res:
+        resdata.append({
+            'id': datai.id,
+            'record_time': datai.record_time.isoformat(),
+            'endpoint': datai.endpoint,
+            'elapsed_time': datai.elapsed_time,
+            'cpu_user': datai.cpu_user,
+            'cpu_system': datai.cpu_system,
+            'memory_rss': datai.memory_rss
+        })
+    return jsonify(resdata)
+@main.route('/getrecordlog',methods=['POST','GET'])
+def getrecordlog():
+    data=request.get_json()
+    engine=create_engine(Config.SQLURL)
+    SessionLocal = sessionmaker(bind=engine)
+    session = SessionLocal()
+    f=Fpost(session)
+    checres=f.checkid(data['userid'],'admin')
+    if(not checres):
+        f.recordlog(data['userid'],
+                     'error', 
+                     '系统需要管理员才能执行获取日志的操作,但是当前用户不是管理员',
+                     request.remote_addr)
+        return jsonify({'status': 'error', 'message': 'Unauthorized'})
+    
+    res=f.getrecordlog()
+    if not res:
+        f.recordlog(data['userid'],
+                     'error', 
+                     '尝试获取日志失败,日志不存在',
+                     request.remote_addr)
+        return jsonify({'status': 'error', 'message': 'No logs found'})
+    
+    f.recordlog(data['userid'],
+                 'access', 
+                 '获取日志成功',
+                 request.remote_addr)
+    
+    resdata = []
+    for datai in res:
+        resdata.append({
+            'id': datai.id,
+            'user_id': datai.user_id,
+            'type': datai.type,
+            'content': datai.content,
+            'ip': datai.ip,
+            'created_at': datai.created_at.isoformat()
+        })
+    
+    return jsonify(resdata)
+