history.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import streamlit as st
  2. import pandas as pd
  3. from datetime import datetime, timedelta
  4. import random
  5. import plotly.express as px
  6. import plotly.graph_objects as go
  7. st.title("📚 History")
  8. st.markdown("操作历史和活动记录")
  9. # 模拟历史数据
  10. @st.cache_data
  11. def load_history_data():
  12. actions = ["登录", "登出", "创建", "编辑", "删除", "查看", "下载", "上传"]
  13. objects = ["用户账户", "订单记录", "产品信息", "系统设置", "报告文件", "配置文件"]
  14. users = ["张三", "李四", "王五", "赵六", "钱七", "孙八"]
  15. data = []
  16. for i in range(500):
  17. timestamp = datetime.now() - timedelta(
  18. days=random.randint(0, 90),
  19. hours=random.randint(0, 23),
  20. minutes=random.randint(0, 59)
  21. )
  22. data.append({
  23. 'id': f"HIST-{4000 + i}",
  24. 'timestamp': timestamp,
  25. 'user': random.choice(users),
  26. 'action': random.choice(actions),
  27. 'object': random.choice(objects),
  28. 'ip_address': f"192.168.1.{random.randint(1, 254)}",
  29. 'status': random.choice(['成功', '失败', '警告']),
  30. 'details': f"操作详情 {i+1}: 用户执行了相关操作",
  31. 'session_id': f"SES-{random.randint(10000, 99999)}"
  32. })
  33. return pd.DataFrame(data)
  34. # 加载数据
  35. history_df = load_history_data()
  36. # 时间范围选择
  37. st.subheader("📅 时间范围")
  38. col1, col2, col3 = st.columns(3)
  39. with col1:
  40. time_filter = st.selectbox(
  41. "快速选择",
  42. ["最近1小时", "最近24小时", "最近7天", "最近30天", "最近90天", "自定义范围"]
  43. )
  44. with col2:
  45. if time_filter == "自定义范围":
  46. start_date = st.date_input("开始日期", value=datetime.now() - timedelta(days=7))
  47. else:
  48. start_date = None
  49. with col3:
  50. if time_filter == "自定义范围":
  51. end_date = st.date_input("结束日期", value=datetime.now())
  52. else:
  53. end_date = None
  54. # 应用时间筛选
  55. if time_filter == "最近1小时":
  56. cutoff = datetime.now() - timedelta(hours=1)
  57. filtered_df = history_df[history_df['timestamp'] >= cutoff]
  58. elif time_filter == "最近24小时":
  59. cutoff = datetime.now() - timedelta(days=1)
  60. filtered_df = history_df[history_df['timestamp'] >= cutoff]
  61. elif time_filter == "最近7天":
  62. cutoff = datetime.now() - timedelta(days=7)
  63. filtered_df = history_df[history_df['timestamp'] >= cutoff]
  64. elif time_filter == "最近30天":
  65. cutoff = datetime.now() - timedelta(days=30)
  66. filtered_df = history_df[history_df['timestamp'] >= cutoff]
  67. elif time_filter == "最近90天":
  68. cutoff = datetime.now() - timedelta(days=90)
  69. filtered_df = history_df[history_df['timestamp'] >= cutoff]
  70. else: # 自定义范围
  71. if start_date and end_date:
  72. filtered_df = history_df[
  73. (history_df['timestamp'].dt.date >= start_date) &
  74. (history_df['timestamp'].dt.date <= end_date)
  75. ]
  76. else:
  77. filtered_df = history_df
  78. # 统计概览
  79. st.subheader("📊 活动概览")
  80. col1, col2, col3, col4 = st.columns(4)
  81. with col1:
  82. st.metric("总操作数", len(filtered_df))
  83. with col2:
  84. unique_users = filtered_df['user'].nunique()
  85. st.metric("活跃用户", unique_users)
  86. with col3:
  87. success_rate = len(filtered_df[filtered_df['status'] == '成功']) / len(filtered_df) * 100 if len(filtered_df) > 0 else 0
  88. st.metric("成功率", f"{success_rate:.1f}%")
  89. with col4:
  90. failed_ops = len(filtered_df[filtered_df['status'] == '失败'])
  91. st.metric("失败操作", failed_ops)
  92. # 可视化图表
  93. st.subheader("📈 活动趋势")
  94. tab1, tab2, tab3 = st.tabs(["时间趋势", "用户活动", "操作分布"])
  95. with tab1:
  96. # 按小时统计活动
  97. if len(filtered_df) > 0:
  98. hourly_activity = filtered_df.groupby(filtered_df['timestamp'].dt.hour).size().reset_index()
  99. hourly_activity.columns = ['hour', 'count']
  100. fig_hourly = px.bar(hourly_activity, x='hour', y='count',
  101. title='按小时统计的活动分布')
  102. st.plotly_chart(fig_hourly, use_container_width=True)
  103. with tab2:
  104. # 用户活动统计
  105. if len(filtered_df) > 0:
  106. user_activity = filtered_df['user'].value_counts().reset_index()
  107. user_activity.columns = ['user', 'count']
  108. fig_users = px.pie(user_activity, values='count', names='user',
  109. title='用户活动分布')
  110. st.plotly_chart(fig_users, use_container_width=True)
  111. with tab3:
  112. # 操作类型分布
  113. if len(filtered_df) > 0:
  114. action_counts = filtered_df['action'].value_counts().reset_index()
  115. action_counts.columns = ['action', 'count']
  116. fig_actions = px.bar(action_counts, x='action', y='count',
  117. title='操作类型分布')
  118. st.plotly_chart(fig_actions, use_container_width=True)
  119. # 筛选器
  120. st.subheader("🔍 详细筛选")
  121. col1, col2, col3, col4 = st.columns(4)
  122. with col1:
  123. selected_users = st.multiselect(
  124. "用户",
  125. options=filtered_df['user'].unique(),
  126. default=filtered_df['user'].unique()
  127. )
  128. with col2:
  129. selected_actions = st.multiselect(
  130. "操作类型",
  131. options=filtered_df['action'].unique(),
  132. default=filtered_df['action'].unique()
  133. )
  134. with col3:
  135. selected_status = st.multiselect(
  136. "状态",
  137. options=filtered_df['status'].unique(),
  138. default=filtered_df['status'].unique()
  139. )
  140. with col4:
  141. search_term = st.text_input("搜索", placeholder="搜索详情...")
  142. # 应用筛选
  143. final_df = filtered_df[
  144. (filtered_df['user'].isin(selected_users)) &
  145. (filtered_df['action'].isin(selected_actions)) &
  146. (filtered_df['status'].isin(selected_status))
  147. ]
  148. if search_term:
  149. final_df = final_df[
  150. final_df['details'].str.contains(search_term, case=False, na=False) |
  151. final_df['object'].str.contains(search_term, case=False, na=False)
  152. ]
  153. # 历史记录表格
  154. st.subheader("📋 历史记录")
  155. # 排序选项
  156. sort_order = st.radio("排序", ["最新在前", "最旧在前"], horizontal=True)
  157. final_df = final_df.sort_values('timestamp', ascending=(sort_order == "最旧在前"))
  158. # 分页
  159. items_per_page = st.selectbox("每页显示", [25, 50, 100], index=1)
  160. total_pages = (len(final_df) - 1) // items_per_page + 1 if len(final_df) > 0 else 1
  161. if total_pages > 1:
  162. page = st.selectbox("页面", range(1, total_pages + 1))
  163. start_idx = (page - 1) * items_per_page
  164. end_idx = start_idx + items_per_page
  165. display_df = final_df.iloc[start_idx:end_idx]
  166. else:
  167. display_df = final_df
  168. # 显示数据
  169. if len(display_df) > 0:
  170. st.dataframe(
  171. display_df[['timestamp', 'user', 'action', 'object', 'status', 'ip_address', 'details']],
  172. use_container_width=True,
  173. column_config={
  174. 'timestamp': st.column_config.DatetimeColumn(
  175. "时间",
  176. format="MM-DD HH:mm:ss"
  177. ),
  178. 'status': st.column_config.SelectboxColumn(
  179. "状态",
  180. options=['成功', '失败', '警告']
  181. )
  182. }
  183. )
  184. # 导出功能
  185. if st.button("📥 导出当前数据"):
  186. csv = display_df.to_csv(index=False)
  187. st.download_button(
  188. label="下载CSV文件",
  189. data=csv,
  190. file_name=f"history_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
  191. mime="text/csv"
  192. )
  193. else:
  194. st.warning("没有找到匹配的历史记录")
  195. # 实时更新选项
  196. st.subheader("🔄 实时更新")
  197. auto_refresh = st.checkbox("启用自动刷新 (30秒)")
  198. if auto_refresh:
  199. st.info("⏱️ 页面将每30秒自动刷新")
  200. time.sleep(30)
  201. st.rerun()