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

View File

@@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
namespace Fendx\Web\Scanner;
use ReflectionClass;
use ReflectionMethod;
use ReflectionException;
use Fendx\Core\Annotation\Controller;
use Fendx\Web\Annotation\GetRoute;
use Fendx\Web\Annotation\PostRoute;
use Fendx\Web\Route\Router;
final class RouteScanner
{
private Router $router;
private array $controllers = [];
public function __construct(Router $router)
{
$this->router = $router;
}
public function scan(string $scanPath): void
{
$this->scanControllers($scanPath);
$this->registerRoutes();
}
private function scanControllers(string $path): void
{
if (!is_dir($path)) {
return;
}
$files = glob($path . '/**/*.php');
foreach ($files as $file) {
$this->scanControllerFile($file);
}
}
private function scanControllerFile(string $file): void
{
$className = $this->getClassNameFromFile($file);
if ($className === null || class_exists($className) === false) {
return;
}
try {
$reflection = new ReflectionClass($className);
// 检查是否有Controller注解
$controllerAttributes = $reflection->getAttributes(Controller::class);
if (empty($controllerAttributes)) {
return;
}
$controllerAttribute = $controllerAttributes[0]->newInstance();
$prefix = $controllerAttribute->prefix;
// 扫描方法上的路由注解
$this->scanControllerMethods($className, $reflection, $prefix);
} catch (ReflectionException $e) {
error_log("Failed to scan controller $className: " . $e->getMessage());
}
}
private function scanControllerMethods(string $className, ReflectionClass $reflection, string $prefix): void
{
$methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
if ($method->getName() === '__construct') {
continue;
}
$this->scanMethodRoutes($className, $method, $prefix);
}
}
private function scanMethodRoutes(string $className, ReflectionMethod $method, string $prefix): void
{
// 扫描GetRoute注解
$getRouteAttributes = $method->getAttributes(GetRoute::class);
foreach ($getRouteAttributes as $attribute) {
$routeAttribute = $attribute->newInstance();
$path = $prefix . $routeAttribute->path;
$this->controllers[] = [
'method' => 'GET',
'path' => $path,
'controller' => $className,
'action' => $method->getName()
];
}
// 扫描PostRoute注解
$postRouteAttributes = $method->getAttributes(PostRoute::class);
foreach ($postRouteAttributes as $attribute) {
$routeAttribute = $attribute->newInstance();
$path = $prefix . $routeAttribute->path;
$this->controllers[] = [
'method' => 'POST',
'path' => $path,
'controller' => $className,
'action' => $method->getName()
];
}
}
private function registerRoutes(): void
{
foreach ($this->controllers as $route) {
$handler = [$route['controller'], $route['action']];
switch ($route['method']) {
case 'GET':
$this->router->get($route['path'], $handler);
break;
case 'POST':
$this->router->post($route['path'], $handler);
break;
case 'PUT':
$this->router->put($route['path'], $handler);
break;
case 'DELETE':
$this->router->delete($route['path'], $handler);
break;
}
}
}
private function getClassNameFromFile(string $file): ?string
{
$content = file_get_contents($file);
if (preg_match('/namespace\s+([^;]+);/', $content, $matches)) {
$namespace = trim($matches[1]);
$className = basename($file, '.php');
return $namespace . '\\' . $className;
}
return null;
}
public function getRoutes(): array
{
return $this->controllers;
}
}