美化性能dashboard
Change-Id: I1b844ced6c2387f6e9f0806175d5c1111c3e6935
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/src/components/PerformanceLogs.js b/Merge/front/src/components/PerformanceLogs.js
index bb1c244..be9eb99 100644
--- a/Merge/front/src/components/PerformanceLogs.js
+++ b/Merge/front/src/components/PerformanceLogs.js
@@ -10,33 +10,83 @@
useEffect(() => {
fetchSysCost(userId)
- .then(list => setData(list))
+ .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">
- <ResponsiveContainer width="100%" height={400}>
- <LineChart
- data={data}
- margin={{ top: 20, right: 30, left: 20, bottom: 5 }}
- >
- <CartesianGrid strokeDasharray="3 3" />
- <XAxis dataKey="record_time" />
- <YAxis
- domain={[0, 'auto']}
- label={{ value: '时间 (s)', angle: -90, position: 'insideLeft' }}
- tickFormatter={val => (val / 1000).toFixed(2)}
- />
- <Tooltip formatter={val => (typeof val === 'number' ? (val/1000).toFixed(2) + 's' : val)} />
- <Legend />
- <Line type="monotone" dataKey="elapsed_time" stroke="#8884d8" name="响应时间" />
- <Line type="monotone" dataKey="cpu_user" stroke="#82ca9d" name="CPU 用户时间" />
- <Line type="monotone" dataKey="cpu_system" stroke="#ffc658" name="CPU 系统时间" />
- <Line type="monotone" dataKey="memory_rss" stroke="#ff7300" name="内存 RSS" />
- {/* 如需更多指标,可再添其他 <Line /> */}
- </LineChart>
- </ResponsiveContainer>
+ {/* 响应时间图表 */}
+ <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>
);
}