feat(database): 添加用户角色权限系统及相关监控功能

- 创建用户表(users)包含基本信息和认证字段
- 创建角色表(roles)用于权限控制
- 创建权限表(permissions)定义系统权限
- 创建用户角色关联表(user_roles)建立用户与角色关系
- 创建角色权限关联表(role_permissions)建立角色与权限关系
- 创建迁移记录表(migrations)追踪数据库变更
- 添加AdminController提供管理员面板功能
- 实现系统监控、配置管理、缓存清理等功能
- 添加AOP切面编程支持的各种通知类型
- 实现告警管理AlertManager支持多渠道告警
- 添加文档注解接口规范
This commit is contained in:
Lawson
2026-04-08 17:00:28 +08:00
commit 2782d765fb
270 changed files with 107192 additions and 0 deletions

363
public/monitor/index.html Normal file
View File

@@ -0,0 +1,363 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FendxPHP 运维管理面板</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/apexcharts@3.35.0/dist/apexcharts.css" rel="stylesheet">
<style>
.status-healthy { color: #10b981; }
.status-warning { color: #f59e0b; }
.status-critical { color: #ef4444; }
.chart-container { position: relative; height: 300px; }
.metric-card { transition: all 0.3s ease; }
.metric-card:hover { transform: translateY(-2px); box-shadow: 0 10px 25px rgba(0,0,0,0.1); }
.sidebar-item { transition: all 0.2s ease; }
.sidebar-item:hover { background-color: #f3f4f6; }
.sidebar-item.active { background-color: #3b82f6; color: white; }
.refresh-btn { animation: spin 2s linear infinite; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.alert-item { animation: slideIn 0.3s ease; }
@keyframes slideIn { from { transform: translateX(-100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
</style>
</head>
<body class="bg-gray-50">
<div class="flex h-screen">
<!-- 侧边栏 -->
<div class="w-64 bg-white shadow-lg">
<div class="p-6 border-b">
<h1 class="text-xl font-bold text-gray-800">FendxPHP 监控</h1>
<p class="text-sm text-gray-600">运维管理面板</p>
</div>
<nav class="p-4">
<div class="sidebar-item active rounded-lg p-3 mb-2 cursor-pointer" onclick="showSection('dashboard')">
<div class="flex items-center">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
</svg>
<span>仪表盘</span>
</div>
</div>
<div class="sidebar-item rounded-lg p-3 mb-2 cursor-pointer" onclick="showSection('health')">
<div class="flex items-center">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>健康检查</span>
</div>
</div>
<div class="sidebar-item rounded-lg p-3 mb-2 cursor-pointer" onclick="showSection('metrics')">
<div class="flex items-center">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
<span>性能指标</span>
</div>
</div>
<div class="sidebar-item rounded-lg p-3 mb-2 cursor-pointer" onclick="showSection('errors')">
<div class="flex items-center">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>错误追踪</span>
</div>
</div>
<div class="sidebar-item rounded-lg p-3 mb-2 cursor-pointer" onclick="showSection('logs')">
<div class="flex items-center">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<span>日志分析</span>
</div>
</div>
<div class="sidebar-item rounded-lg p-3 mb-2 cursor-pointer" onclick="showSection('alerts')">
<div class="flex items-center">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path>
</svg>
<span>告警管理</span>
</div>
</div>
<div class="sidebar-item rounded-lg p-3 mb-2 cursor-pointer" onclick="showSection('settings')">
<div class="flex items-center">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
<span>系统设置</span>
</div>
</div>
</nav>
</div>
<!-- 主内容区 -->
<div class="flex-1 overflow-auto">
<!-- 顶部栏 -->
<div class="bg-white shadow-sm border-b px-6 py-4">
<div class="flex justify-between items-center">
<div class="flex items-center">
<h2 id="section-title" class="text-xl font-semibold text-gray-800">仪表盘</h2>
<span id="last-update" class="ml-4 text-sm text-gray-500">最后更新: --</span>
</div>
<div class="flex items-center space-x-4">
<button onclick="refreshData()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition">
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
刷新
</button>
<div class="flex items-center">
<span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
<span class="text-sm text-gray-600">系统正常</span>
</div>
</div>
</div>
</div>
<!-- 内容区域 -->
<div class="p-6">
<!-- 仪表盘 -->
<div id="dashboard-section" class="section">
<!-- 概览卡片 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="metric-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">系统状态</p>
<p id="system-status" class="text-2xl font-bold status-healthy">健康</p>
</div>
<div class="p-3 bg-green-100 rounded-full">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
</div>
<div class="metric-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">活跃告警</p>
<p id="active-alerts" class="text-2xl font-bold text-yellow-600">0</p>
</div>
<div class="p-3 bg-yellow-100 rounded-full">
<svg class="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path>
</svg>
</div>
</div>
</div>
<div class="metric-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">内存使用</p>
<p id="memory-usage" class="text-2xl font-bold text-blue-600">0%</p>
</div>
<div class="p-3 bg-blue-100 rounded-full">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"></path>
</svg>
</div>
</div>
</div>
<div class="metric-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">响应时间</p>
<p id="response-time" class="text-2xl font-bold text-green-600">0ms</p>
</div>
<div class="p-3 bg-green-100 rounded-full">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">请求趋势</h3>
<div class="chart-container">
<div id="requests-chart"></div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">错误分布</h3>
<div class="chart-container">
<div id="errors-chart"></div>
</div>
</div>
</div>
<!-- 最近告警 -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">最近告警</h3>
<div id="recent-alerts" class="space-y-3">
<p class="text-gray-500 text-center py-4">暂无告警</p>
</div>
</div>
</div>
<!-- 健康检查 -->
<div id="health-section" class="section hidden">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">系统健康检查</h3>
<div id="health-checks" class="space-y-4">
<div class="text-center py-8">
<svg class="w-12 h-12 text-gray-400 mx-auto mb-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
<p class="text-gray-500">正在加载健康检查数据...</p>
</div>
</div>
</div>
</div>
<!-- 性能指标 -->
<div id="metrics-section" class="section hidden">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">系统指标</h3>
<div id="system-metrics" class="space-y-4">
<div class="text-center py-8">
<p class="text-gray-500">正在加载系统指标...</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">应用指标</h3>
<div id="application-metrics" class="space-y-4">
<div class="text-center py-8">
<p class="text-gray-500">正在加载应用指标...</p>
</div>
</div>
</div>
</div>
</div>
<!-- 错误追踪 -->
<div id="errors-section" class="section hidden">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">错误追踪</h3>
<div class="flex space-x-2">
<select id="error-filter" class="px-3 py-2 border rounded-lg">
<option value="">所有级别</option>
<option value="critical">严重</option>
<option value="warning">警告</option>
<option value="info">信息</option>
</select>
<button onclick="refreshErrors()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
刷新
</button>
</div>
</div>
<div id="errors-list" class="space-y-3">
<div class="text-center py-8">
<p class="text-gray-500">正在加载错误数据...</p>
</div>
</div>
</div>
</div>
<!-- 日志分析 -->
<div id="logs-section" class="section hidden">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">日志分析</h3>
<div class="flex space-x-2">
<select id="log-level" class="px-3 py-2 border rounded-lg">
<option value="">所有级别</option>
<option value="ERROR">错误</option>
<option value="WARNING">警告</option>
<option value="INFO">信息</option>
<option value="DEBUG">调试</option>
</select>
<input type="text" id="log-search" placeholder="搜索日志..." class="px-3 py-2 border rounded-lg">
<button onclick="searchLogs()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
搜索
</button>
</div>
</div>
<div id="logs-container" class="space-y-2">
<div class="text-center py-8">
<p class="text-gray-500">请输入搜索条件</p>
</div>
</div>
</div>
</div>
<!-- 告警管理 -->
<div id="alerts-section" class="section hidden">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">告警管理</h3>
<div class="flex space-x-2">
<select id="alert-status" class="px-3 py-2 border rounded-lg">
<option value="">所有状态</option>
<option value="active">活跃</option>
<option value="acknowledged">已确认</option>
<option value="resolved">已解决</option>
</select>
<button onclick="refreshAlerts()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
刷新
</button>
</div>
</div>
<div id="alerts-list" class="space-y-3">
<div class="text-center py-8">
<p class="text-gray-500">正在加载告警数据...</p>
</div>
</div>
</div>
</div>
<!-- 系统设置 -->
<div id="settings-section" class="section hidden">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">系统设置</h3>
<div class="space-y-6">
<div>
<h4 class="text-md font-medium mb-3">监控配置</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">采样率</label>
<input type="number" id="sample-rate" class="w-full px-3 py-2 border rounded-lg" value="1.0" step="0.1" min="0" max="1">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">数据保留时间(秒)</label>
<input type="number" id="retention" class="w-full px-3 py-2 border rounded-lg" value="3600" step="60">
</div>
</div>
</div>
<div>
<h4 class="text-md font-medium mb-3">告警阈值</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">错误率阈值</label>
<input type="number" id="error-threshold" class="w-full px-3 py-2 border rounded-lg" value="0.05" step="0.01" min="0" max="1">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">内存使用阈值</label>
<input type="number" id="memory-threshold" class="w-full px-3 py-2 border rounded-lg" value="0.8" step="0.1" min="0" max="1">
</div>
</div>
</div>
<div class="flex justify-end">
<button onclick="saveSettings()" class="px-6 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600">
保存设置
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/apexcharts@3.35.0/dist/apexcharts.min.js"></script>
<script src="dashboard.js"></script>
</body>
</html>