import streamlit as st import pandas as pd from datetime import datetime, timedelta import random import plotly.express as px import plotly.graph_objects as go st.title("📚 History") st.markdown("操作历史和活动记录") # 模拟历史数据 @st.cache_data def load_history_data(): actions = ["登录", "登出", "创建", "编辑", "删除", "查看", "下载", "上传"] objects = ["用户账户", "订单记录", "产品信息", "系统设置", "报告文件", "配置文件"] users = ["张三", "李四", "王五", "赵六", "钱七", "孙八"] data = [] for i in range(500): timestamp = datetime.now() - timedelta( days=random.randint(0, 90), hours=random.randint(0, 23), minutes=random.randint(0, 59) ) data.append({ 'id': f"HIST-{4000 + i}", 'timestamp': timestamp, 'user': random.choice(users), 'action': random.choice(actions), 'object': random.choice(objects), 'ip_address': f"192.168.1.{random.randint(1, 254)}", 'status': random.choice(['成功', '失败', '警告']), 'details': f"操作详情 {i+1}: 用户执行了相关操作", 'session_id': f"SES-{random.randint(10000, 99999)}" }) return pd.DataFrame(data) # 加载数据 history_df = load_history_data() # 时间范围选择 st.subheader("📅 时间范围") col1, col2, col3 = st.columns(3) with col1: time_filter = st.selectbox( "快速选择", ["最近1小时", "最近24小时", "最近7天", "最近30天", "最近90天", "自定义范围"] ) with col2: if time_filter == "自定义范围": start_date = st.date_input("开始日期", value=datetime.now() - timedelta(days=7)) else: start_date = None with col3: if time_filter == "自定义范围": end_date = st.date_input("结束日期", value=datetime.now()) else: end_date = None # 应用时间筛选 if time_filter == "最近1小时": cutoff = datetime.now() - timedelta(hours=1) filtered_df = history_df[history_df['timestamp'] >= cutoff] elif time_filter == "最近24小时": cutoff = datetime.now() - timedelta(days=1) filtered_df = history_df[history_df['timestamp'] >= cutoff] elif time_filter == "最近7天": cutoff = datetime.now() - timedelta(days=7) filtered_df = history_df[history_df['timestamp'] >= cutoff] elif time_filter == "最近30天": cutoff = datetime.now() - timedelta(days=30) filtered_df = history_df[history_df['timestamp'] >= cutoff] elif time_filter == "最近90天": cutoff = datetime.now() - timedelta(days=90) filtered_df = history_df[history_df['timestamp'] >= cutoff] else: # 自定义范围 if start_date and end_date: filtered_df = history_df[ (history_df['timestamp'].dt.date >= start_date) & (history_df['timestamp'].dt.date <= end_date) ] else: filtered_df = history_df # 统计概览 st.subheader("📊 活动概览") col1, col2, col3, col4 = st.columns(4) with col1: st.metric("总操作数", len(filtered_df)) with col2: unique_users = filtered_df['user'].nunique() st.metric("活跃用户", unique_users) with col3: success_rate = len(filtered_df[filtered_df['status'] == '成功']) / len(filtered_df) * 100 if len(filtered_df) > 0 else 0 st.metric("成功率", f"{success_rate:.1f}%") with col4: failed_ops = len(filtered_df[filtered_df['status'] == '失败']) st.metric("失败操作", failed_ops) # 可视化图表 st.subheader("📈 活动趋势") tab1, tab2, tab3 = st.tabs(["时间趋势", "用户活动", "操作分布"]) with tab1: # 按小时统计活动 if len(filtered_df) > 0: hourly_activity = filtered_df.groupby(filtered_df['timestamp'].dt.hour).size().reset_index() hourly_activity.columns = ['hour', 'count'] fig_hourly = px.bar(hourly_activity, x='hour', y='count', title='按小时统计的活动分布') st.plotly_chart(fig_hourly, use_container_width=True) with tab2: # 用户活动统计 if len(filtered_df) > 0: user_activity = filtered_df['user'].value_counts().reset_index() user_activity.columns = ['user', 'count'] fig_users = px.pie(user_activity, values='count', names='user', title='用户活动分布') st.plotly_chart(fig_users, use_container_width=True) with tab3: # 操作类型分布 if len(filtered_df) > 0: action_counts = filtered_df['action'].value_counts().reset_index() action_counts.columns = ['action', 'count'] fig_actions = px.bar(action_counts, x='action', y='count', title='操作类型分布') st.plotly_chart(fig_actions, use_container_width=True) # 筛选器 st.subheader("🔍 详细筛选") col1, col2, col3, col4 = st.columns(4) with col1: selected_users = st.multiselect( "用户", options=filtered_df['user'].unique(), default=filtered_df['user'].unique() ) with col2: selected_actions = st.multiselect( "操作类型", options=filtered_df['action'].unique(), default=filtered_df['action'].unique() ) with col3: selected_status = st.multiselect( "状态", options=filtered_df['status'].unique(), default=filtered_df['status'].unique() ) with col4: search_term = st.text_input("搜索", placeholder="搜索详情...") # 应用筛选 final_df = filtered_df[ (filtered_df['user'].isin(selected_users)) & (filtered_df['action'].isin(selected_actions)) & (filtered_df['status'].isin(selected_status)) ] if search_term: final_df = final_df[ final_df['details'].str.contains(search_term, case=False, na=False) | final_df['object'].str.contains(search_term, case=False, na=False) ] # 历史记录表格 st.subheader("📋 历史记录") # 排序选项 sort_order = st.radio("排序", ["最新在前", "最旧在前"], horizontal=True) final_df = final_df.sort_values('timestamp', ascending=(sort_order == "最旧在前")) # 分页 items_per_page = st.selectbox("每页显示", [25, 50, 100], index=1) total_pages = (len(final_df) - 1) // items_per_page + 1 if len(final_df) > 0 else 1 if total_pages > 1: page = st.selectbox("页面", range(1, total_pages + 1)) start_idx = (page - 1) * items_per_page end_idx = start_idx + items_per_page display_df = final_df.iloc[start_idx:end_idx] else: display_df = final_df # 显示数据 if len(display_df) > 0: st.dataframe( display_df[['timestamp', 'user', 'action', 'object', 'status', 'ip_address', 'details']], use_container_width=True, column_config={ 'timestamp': st.column_config.DatetimeColumn( "时间", format="MM-DD HH:mm:ss" ), 'status': st.column_config.SelectboxColumn( "状态", options=['成功', '失败', '警告'] ) } ) # 导出功能 if st.button("📥 导出当前数据"): csv = display_df.to_csv(index=False) st.download_button( label="下载CSV文件", data=csv, file_name=f"history_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", mime="text/csv" ) else: st.warning("没有找到匹配的历史记录") # 实时更新选项 st.subheader("🔄 实时更新") auto_refresh = st.checkbox("启用自动刷新 (30秒)") if auto_refresh: st.info("⏱️ 页面将每30秒自动刷新") time.sleep(30) st.rerun()