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

453
scripts/check-database.php Normal file
View File

@@ -0,0 +1,453 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
/**
* 数据库状态检查脚本
* 用法: php scripts/check-database.php [options]
*/
use Fendx\Db\DB;
use Fendx\Core\Config\Config;
class DatabaseChecker
{
private array $config;
private ?PDO $pdo = null;
public function __construct()
{
$this->loadConfig();
$this->connectDatabase();
}
/**
* 加载配置
*/
private function loadConfig(): void
{
// 加载环境配置
$envFile = __DIR__ . '/../.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos($line, '=') !== false) {
list($key, $value) = explode('=', $line, 2);
$_ENV[trim($key)] = trim($value);
$_SERVER[trim($key)] = trim($value);
}
}
}
$this->config = [
'host' => $_ENV['DB_HOST'] ?? 'localhost',
'port' => $_ENV['DB_PORT'] ?? '3306',
'database' => $_ENV['DB_DATABASE'] ?? 'fendx_php',
'username' => $_ENV['DB_USERNAME'] ?? 'root',
'password' => $_ENV['DB_PASSWORD'] ?? '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
];
}
/**
* 连接数据库
*/
private function connectDatabase(): void
{
try {
$dsn = sprintf(
'mysql:host=%s;port=%s;charset=%s',
$this->config['host'],
$this->config['port'],
$this->config['charset']
);
$this->pdo = new PDO($dsn, $this->config['username'], $this->config['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
echo "✅ 数据库连接成功\n";
} catch (PDOException $e) {
echo "❌ 数据库连接失败: " . $e->getMessage() . "\n";
exit(1);
}
}
/**
* 检查数据库是否存在
*/
public function checkDatabaseExists(): bool
{
try {
$stmt = $this->pdo->query("SHOW DATABASES LIKE '{$this->config['database']}'");
$result = $stmt->fetch();
if ($result) {
echo "✅ 数据库 '{$this->config['database']}' 存在\n";
return true;
} else {
echo "❌ 数据库 '{$this->config['database']}' 不存在\n";
return false;
}
} catch (PDOException $e) {
echo "❌ 检查数据库失败: " . $e->getMessage() . "\n";
return false;
}
}
/**
* 创建数据库
*/
public function createDatabase(): bool
{
try {
$sql = sprintf(
"CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET %s COLLATE %s",
$this->config['database'],
$this->config['charset'],
$this->config['collation']
);
$this->pdo->exec($sql);
echo "✅ 数据库 '{$this->config['database']}' 创建成功\n";
return true;
} catch (PDOException $e) {
echo "❌ 创建数据库失败: " . $e->getMessage() . "\n";
return false;
}
}
/**
* 选择数据库
*/
private function selectDatabase(): bool
{
try {
$this->pdo->exec("USE `{$this->config['database']}`");
return true;
} catch (PDOException $e) {
echo "❌ 选择数据库失败: " . $e->getMessage() . "\n";
return false;
}
}
/**
* 检查表结构
*/
public function checkTables(): array
{
if (!$this->selectDatabase()) {
return [];
}
try {
$stmt = $this->pdo->query("SHOW TABLES");
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
if (empty($tables)) {
echo "⚠️ 数据库中没有表\n";
return [];
}
echo "📊 数据库表列表:\n";
foreach ($tables as $table) {
$count = $this->getTableRecordCount($table);
echo " - {$table} ({$count} 条记录)\n";
}
return $tables;
} catch (PDOException $e) {
echo "❌ 检查表失败: " . $e->getMessage() . "\n";
return [];
}
}
/**
* 获取表记录数
*/
private function getTableRecordCount(string $table): int
{
try {
$stmt = $this->pdo->query("SELECT COUNT(*) FROM `{$table}`");
return (int) $stmt->fetchColumn();
} catch (PDOException $e) {
return 0;
}
}
/**
* 检查迁移表
*/
public function checkMigrations(): void
{
if (!$this->selectDatabase()) {
return;
}
try {
// 检查迁移表是否存在
$stmt = $this->pdo->query("SHOW TABLES LIKE 'migrations'");
$hasMigrationsTable = $stmt->fetch() !== false;
if (!$hasMigrationsTable) {
echo "⚠️ 迁移表不存在,需要运行迁移\n";
return;
}
// 检查迁移记录
$stmt = $this->pdo->query("SELECT * FROM migrations ORDER BY id DESC LIMIT 10");
$migrations = $stmt->fetchAll();
if (empty($migrations)) {
echo "⚠️ 没有迁移记录\n";
return;
}
echo "📋 最近迁移记录:\n";
foreach ($migrations as $migration) {
echo " - {$migration['migration']} ({$migration['batch']})\n";
}
} catch (PDOException $e) {
echo "❌ 检查迁移失败: " . $e->getMessage() . "\n";
}
}
/**
* 检查用户表数据
*/
public function checkUserData(): void
{
if (!$this->selectDatabase()) {
return;
}
try {
// 检查用户表是否存在
$stmt = $this->pdo->query("SHOW TABLES LIKE 'users'");
if (!$stmt->fetch()) {
echo "⚠️ 用户表不存在\n";
return;
}
// 检查用户数据
$stmt = $this->pdo->query("SELECT COUNT(*) FROM users");
$userCount = $stmt->fetchColumn();
echo "👥 用户数据:\n";
echo " - 总用户数: {$userCount}\n";
if ($userCount > 0) {
// 显示最近注册的用户
$stmt = $this->pdo->query("SELECT id, username, email, created_at FROM users ORDER BY created_at DESC LIMIT 5");
$recentUsers = $stmt->fetchAll();
echo " - 最近注册用户:\n";
foreach ($recentUsers as $user) {
echo " * {$user['username']} ({$user['email']}) - {$user['created_at']}\n";
}
}
} catch (PDOException $e) {
echo "❌ 检查用户数据失败: " . $e->getMessage() . "\n";
}
}
/**
* 测试数据库权限
*/
public function testPermissions(): void
{
echo "🔐 测试数据库权限:\n";
// 测试SELECT权限
try {
$this->pdo->query("SELECT 1");
echo " ✅ SELECT 权限正常\n";
} catch (PDOException $e) {
echo " ❌ SELECT 权限失败: " . $e->getMessage() . "\n";
}
// 测试INSERT权限
try {
$this->pdo->exec("CREATE TABLE IF NOT EXISTS test_permissions (id INT)");
$this->pdo->exec("INSERT INTO test_permissions (id) VALUES (1)");
$this->pdo->exec("DROP TABLE test_permissions");
echo " ✅ INSERT 权限正常\n";
} catch (PDOException $e) {
echo " ❌ INSERT 权限失败: " . $e->getMessage() . "\n";
}
// 测试CREATE权限
try {
$this->pdo->exec("CREATE TABLE IF NOT EXISTS test_create (id INT)");
$this->pdo->exec("DROP TABLE test_create");
echo " ✅ CREATE 权限正常\n";
} catch (PDOException $e) {
echo " ❌ CREATE 权限失败: " . $e->getMessage() . "\n";
}
}
/**
* 检查数据库连接池状态
*/
public function checkConnectionPool(): void
{
echo "🔗 检查连接池状态:\n";
// 模拟多个连接
$connections = [];
$successCount = 0;
for ($i = 0; $i < 5; $i++) {
try {
$dsn = sprintf(
'mysql:host=%s;port=%s;charset=%s',
$this->config['host'],
$this->config['port'],
$this->config['charset']
);
$pdo = new PDO($dsn, $this->config['username'], $this->config['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$pdo->query("SELECT 1");
$connections[] = $pdo;
$successCount++;
} catch (PDOException $e) {
echo " ❌ 连接 {$i} 失败: " . $e->getMessage() . "\n";
}
}
echo " ✅ 成功建立 {$successCount}/5 个连接\n";
// 关闭连接
foreach ($connections as $pdo) {
$pdo = null;
}
}
/**
* 运行完整检查
*/
public function runFullCheck(): void
{
echo "🔍 开始数据库完整检查...\n";
echo str_repeat("=", 50) . "\n";
// 显示配置信息
echo "📋 数据库配置:\n";
echo " - 主机: {$this->config['host']}\n";
echo " - 端口: {$this->config['port']}\n";
echo " - 数据库: {$this->config['database']}\n";
echo " - 用户名: {$this->config['username']}\n";
echo " - 字符集: {$this->config['charset']}\n";
echo str_repeat("-", 50) . "\n";
// 检查数据库是否存在
if (!$this->checkDatabaseExists()) {
echo "🔧 尝试创建数据库...\n";
if ($this->createDatabase()) {
echo "✅ 数据库创建成功\n";
} else {
echo "❌ 数据库创建失败,请检查权限\n";
return;
}
}
// 检查表结构
$this->checkTables();
echo str_repeat("-", 50) . "\n";
// 检查迁移
$this->checkMigrations();
echo str_repeat("-", 50) . "\n";
// 检查用户数据
$this->checkUserData();
echo str_repeat("-", 50) . "\n";
// 测试权限
$this->testPermissions();
echo str_repeat("-", 50) . "\n";
// 检查连接池
$this->checkConnectionPool();
echo str_repeat("=", 50) . "\n";
echo "✅ 数据库检查完成\n";
}
/**
* 修复数据库
*/
public function repairDatabase(): void
{
echo "🔧 开始修复数据库...\n";
// 创建数据库
if (!$this->checkDatabaseExists()) {
$this->createDatabase();
}
// 选择数据库
if (!$this->selectDatabase()) {
echo "❌ 无法选择数据库\n";
return;
}
// 运行迁移
echo "🔄 运行数据库迁移...\n";
$migrateCommand = "php bin/console migrate:run";
echo "执行: {$migrateCommand}\n";
system($migrateCommand);
// 填充测试数据
echo "🌱 填充测试数据...\n";
$seedCommand = "php bin/console migrate:seed";
echo "执行: {$seedCommand}\n";
system($seedCommand);
echo "✅ 数据库修复完成\n";
}
}
// 主程序
function main(): void
{
$options = getopt('cfr', ['check', 'fix', 'repair', 'help']);
if (isset($options['h']) || isset($options['help'])) {
echo "数据库检查工具\n";
echo "用法: php scripts/check-database.php [选项]\n";
echo "\n选项:\n";
echo " -c, --check 检查数据库状态 (默认)\n";
echo " -f, --fix 修复数据库问题\n";
echo " -r, --repair 修复数据库 (同 --fix)\n";
echo " -h, --help 显示帮助信息\n";
echo "\n示例:\n";
echo " php scripts/check-database.php # 检查数据库\n";
echo " php scripts/check-database.php --check # 检查数据库\n";
echo " php scripts/check-database.php --fix # 修复数据库\n";
return;
}
try {
$checker = new DatabaseChecker();
if (isset($options['f']) || isset($options['fix']) ||
isset($options['r']) || isset($options['repair'])) {
$checker->repairDatabase();
} else {
$checker->runFullCheck();
}
} catch (Exception $e) {
echo "❌ 检查失败: " . $e->getMessage() . "\n";
exit(1);
}
}
// 运行主程序
main();

466
scripts/check-database.ps1 Normal file
View File

@@ -0,0 +1,466 @@
# FendxPHP 数据库检查 PowerShell 脚本
# 用法: .\scripts\check-database.ps1 [选项]
param(
[Parameter(Mandatory=$false)]
[ValidateSet("check", "fix", "migrate", "seed", "help")]
[string]$Action = "check"
)
# 颜色输出函数
function Write-ColorOutput {
param(
[string]$Message,
[ConsoleColor]$Color = "White"
)
Write-Host $Message -ForegroundColor $Color
}
function Write-Info {
param([string]$Message)
Write-ColorOutput "[INFO] $Message" -Color Cyan
}
function Write-Success {
param([string]$Message)
Write-ColorOutput "[SUCCESS] $Message" -Color Green
}
function Write-Warning {
param([string]$Message)
Write-ColorOutput "[WARNING] $Message" -Color Yellow
}
function Write-Error {
param([string]$Message)
Write-ColorOutput "[ERROR] $Message" -Color Red
}
# 加载环境配置
function Load-Environment {
$envFile = Join-Path $PSScriptRoot "..\.env"
if (Test-Path $envFile) {
Write-Info "加载环境配置: $envFile"
Get-Content $envFile | ForEach-Object {
if ($_ -match '^([^=]+)=(.*)$' -and -not $_.StartsWith('#')) {
$name = $matches[1].Trim()
$value = $matches[2].Trim()
[Environment]::SetEnvironmentVariable($name, $value, "Process")
}
}
} else {
Write-Warning "环境配置文件不存在: $envFile"
}
}
# 测试MySQL客户端
function Test-MySQLClient {
try {
$result = & mysql --version 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Success "MySQL客户端可用"
return $true
}
} catch {
# 忽略错误
}
Write-Warning "MySQL客户端不可用尝试使用Docker"
return $false
}
# 测试数据库连接
function Test-DatabaseConnection {
$dbHost = if ($env:DB_HOST) { $env:DB_HOST } else { "localhost" }
$dbPort = if ($env:DB_PORT) { $env:DB_PORT } else { "3306" }
$dbDatabase = if ($env:DB_DATABASE) { $env:DB_DATABASE } else { "fendx_php" }
$dbUsername = if ($env:DB_USERNAME) { $env:DB_USERNAME } else { "root" }
$dbPassword = if ($env:DB_PASSWORD) { $env:DB_PASSWORD } else { "" }
$config = @{
Host = $dbHost
Port = $dbPort
Database = $dbDatabase
Username = $dbUsername
Password = $dbPassword
}
Write-Info "检查数据库连接..."
Write-Info " 主机: $($config.Host):$($config.Port)"
Write-Info " 数据库: $($config.Database)"
Write-Info " 用户名: $($config.Username)"
# 尝试使用MySQL客户端
if (Test-MySQLClient) {
try {
& mysql -h $config.Host -P $config.Port -u $config.Username -p$config.Password -e "SELECT 1" 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Success "数据库连接成功"
return @{ Success = $true; Config = $config }
}
} catch {
# 忽略错误
}
}
# 尝试使用Docker
if (Get-Command docker -ErrorAction SilentlyContinue) {
return Test-DockerDatabaseConnection $config
}
Write-Error "数据库连接失败"
return @{ Success = $false }
}
# 测试Docker数据库连接
function Test-DockerDatabaseConnection {
param($config)
Write-Info "尝试使用Docker连接数据库..."
# 检查Docker是否运行
try {
& docker info >$null 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Error "Docker未运行"
return @{ Success = $false }
}
} catch {
Write-Error "Docker未运行"
return @{ Success = $false }
}
# 检查MySQL容器
$containerName = "fendx-mysql-test"
$container = & docker ps --filter "name=$containerName" --format "table {{.Names}}\t{{.Status}}" | Select-Object -Skip 1
if (-not $container -or $container -notlike "*Up*") {
Write-Warning "MySQL容器未运行尝试启动..."
& docker-compose -f docker-compose.test.yml up -d mysql-test
# 等待启动
Write-Info "等待MySQL启动..."
$maxAttempts = 30
$attempt = 0
while ($attempt -lt $maxAttempts) {
try {
& docker exec $containerName mysqladmin ping -h localhost --silent >$null 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Success "MySQL启动成功"
break
}
} catch {
# 忽略错误
}
$attempt++
Write-Host "." -NoNewline
Start-Sleep -Seconds 2
}
if ($attempt -eq $maxAttempts) {
Write-Error "MySQL启动超时"
& docker-compose -f docker-compose.test.yml logs mysql-test
return @{ Success = $false }
}
}
# 测试连接
try {
& docker exec $containerName mysql -u test -ptest -e "SELECT 1" >$null 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Success "Docker数据库连接成功"
return @{ Success = $true; Config = $config; Docker = $true }
}
} catch {
# 忽略错误
}
Write-Error "Docker数据库连接失败"
return @{ Success = $false }
}
# 检查数据库是否存在
function Test-DatabaseExists {
param($Config, [switch]$UseDocker)
$database = $Config.Database
if ($UseDocker) {
Write-Info "检查数据库是否存在 (Docker)..."
$result = & docker exec fendx-mysql-test mysql -u test -ptest -e "SHOW DATABASES LIKE '$database'" 2>$null
if ($result -match $database) {
Write-Success "数据库 '$database' 存在"
return $true
}
} else {
Write-Info "检查数据库是否存在..."
$result = & mysql -h $Config.Host -P $Config.Port -u $Config.Username -p$Config.Password -e "SHOW DATABASES LIKE '$database'" 2>$null
if ($result -match $database) {
Write-Success "数据库 '$database' 存在"
return $true
}
}
Write-Warning "数据库 '$database' 不存在"
return $false
}
# 创建数据库
function New-Database {
param($Config, [switch]$UseDocker)
$database = $Config.Database
Write-Info "创建数据库 '$database'..."
if ($UseDocker) {
$sql = "CREATE DATABASE IF NOT EXISTS `$database` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
& docker exec fendx-mysql-test mysql -u test -ptest -e $sql >$null 2>&1
} else {
$sql = "CREATE DATABASE IF NOT EXISTS `$database` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
& mysql -h $Config.Host -P $Config.Port -u $Config.Username -p$Config.Password -e $sql >$null 2>&1
}
if ($LASTEXITCODE -eq 0) {
Write-Success "数据库创建成功"
return $true
} else {
Write-Error "数据库创建失败"
return $false
}
}
# 检查表结构
function Get-DatabaseTables {
param($Config, [switch]$UseDocker)
Write-Info "检查数据库表..."
if ($UseDocker) {
$tables = & docker exec fendx-mysql-test mysql -u test -ptest $Config.Database -e "SHOW TABLES" 2>$null
} else {
$tables = & mysql -h $Config.Host -P $Config.Port -u $Config.Username -p$Config.Password $Config.Database -e "SHOW TABLES" 2>$null
}
if (-not $tables -or $tables.Split("`n").Length -le 1) {
Write-Warning "数据库中没有表"
Write-Info "建议运行迁移命令:"
Write-Info " php bin/console migrate:run"
return
}
Write-Success "数据库表列表:"
$tableList = $tables.Split("`n") | Where-Object { $_ -and $_ -notmatch "Tables_in_" }
foreach ($table in $tableList) {
if ($table.Trim()) {
$count = Get-TableRecordCount $table $Config $UseDocker
Write-Host " - $table ($count 条记录)"
}
}
}
# 获取表记录数
function Get-TableRecordCount {
param($Table, $Config, [switch]$UseDocker)
if ($UseDocker) {
$result = & docker exec fendx-mysql-test mysql -u test -ptest $Config.Database -e "SELECT COUNT(*) FROM `$Table`" 2>$null
} else {
$result = & mysql -h $Config.Host -P $Config.Port -u $Config.Username -p$Config.Password $Config.Database -e "SELECT COUNT(*) FROM `$Table`" 2>$null
}
if ($result -match '\d+') {
return [int]$matches[0]
}
return 0
}
# 运行数据库迁移
function Invoke-DatabaseMigration {
param([switch]$UseDocker)
Write-Info "运行数据库迁移..."
if ($UseDocker) {
& docker-compose -f docker-compose.test.yml exec -T app php bin/console migrate:run
} else {
& php bin/console migrate:run
}
if ($LASTEXITCODE -eq 0) {
Write-Success "数据库迁移完成"
return $true
} else {
Write-Error "数据库迁移失败"
return $false
}
}
# 填充测试数据
function Invoke-DatabaseSeed {
param([switch]$UseDocker)
Write-Info "填充测试数据..."
if ($UseDocker) {
& docker-compose -f docker-compose.test.yml exec -T app php bin/console migrate:seed
} else {
& php bin/console migrate:seed
}
if ($LASTEXITCODE -eq 0) {
Write-Success "测试数据填充完成"
return $true
} else {
Write-Error "测试数据填充失败"
return $false
}
}
# 完整检查
function Invoke-FullCheck {
Write-Info "开始完整数据库检查..."
Write-Host "==================================================" -ForegroundColor Cyan
# 加载环境配置
Load-Environment
# 测试数据库连接
$connectionResult = Test-DatabaseConnection
if (-not $connectionResult.Success) {
Write-Host "`n💡 解决建议:" -ForegroundColor Yellow
Write-Host " 1. 检查数据库服务是否启动" -ForegroundColor White
Write-Host " 2. 验证数据库连接配置" -ForegroundColor White
Write-Host " 3. 确认用户权限正确" -ForegroundColor White
return
}
$config = $connectionResult.Config
$useDocker = $connectionResult.Docker
Write-Host "`n--------------------------------------------------" -ForegroundColor Cyan
# 检查数据库是否存在
if (-not (Test-DatabaseExists $config -UseDocker:$useDocker)) {
Write-Host "`n🔧 尝试创建数据库..." -ForegroundColor Yellow
if (New-Database $config -UseDocker:$useDocker) {
Write-Success "数据库已创建,请运行迁移命令:"
Write-Info " php bin/console migrate:run"
}
return
}
Write-Host "`n--------------------------------------------------" -ForegroundColor Cyan
# 检查表结构
Get-DatabaseTables $config -UseDocker:$useDocker
Write-Host "`n==================================================" -ForegroundColor Cyan
Write-Success "数据库检查完成"
}
# 修复数据库
function Repair-Database {
Write-Info "开始修复数据库..."
# 加载环境配置
Load-Environment
# 测试数据库连接
$connectionResult = Test-DatabaseConnection
if (-not $connectionResult.Success) {
Write-Error "数据库连接失败,无法修复"
return
}
$config = $connectionResult.Config
$useDocker = $connectionResult.Docker
# 创建数据库
if (-not (Test-DatabaseExists $config -UseDocker:$useDocker)) {
New-Database $config -UseDocker:$useDocker
}
# 运行迁移
Invoke-DatabaseMigration -UseDocker:$useDocker
# 填充数据
Invoke-DatabaseSeed -UseDocker:$useDocker
Write-Success "数据库修复完成"
}
# 显示帮助
function Show-Help {
Write-Host @"
FendxPHP (PowerShell)
: .\scripts\check-database.ps1 []
:
check ()
fix
migrate
seed
help
:
.\scripts\check-database.ps1 #
.\scripts\check-database.ps1 check #
.\scripts\check-database.ps1 fix #
.\scripts\check-database.ps1 migrate #
.\scripts\check-database.ps1 seed #
"@ -ForegroundColor White
}
# 主函数
function Main {
Write-Host "🚀 FendxPHP 数据库检查 (PowerShell)" -ForegroundColor Green
Write-Host "==================================================" -ForegroundColor Cyan
switch ($Action.ToLower()) {
"check" {
Invoke-FullCheck
}
"fix" {
Repair-Database
}
"migrate" {
Load-Environment
$connectionResult = Test-DatabaseConnection
if ($connectionResult.Success) {
Invoke-DatabaseMigration -UseDocker:$connectionResult.Docker
}
}
"seed" {
Load-Environment
$connectionResult = Test-DatabaseConnection
if ($connectionResult.Success) {
Invoke-DatabaseSeed -UseDocker:$connectionResult.Docker
}
}
"help" {
Show-Help
}
default {
Write-Error "未知选项: $Action"
Show-Help
exit 1
}
}
}
# 运行主函数
try {
Main
} catch {
Write-Error "脚本执行失败: $($_.Exception.Message)"
exit 1
}

366
scripts/check-docker-db.sh Normal file
View File

@@ -0,0 +1,366 @@
#!/bin/bash
# Docker环境数据库检查脚本
# 用法: ./scripts/check-docker-db.sh
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查Docker是否运行
check_docker() {
log_info "检查Docker状态..."
if ! docker info > /dev/null 2>&1; then
log_error "Docker未运行请启动Docker服务"
exit 1
fi
log_success "Docker运行正常"
}
# 检查Docker Compose文件
check_compose_file() {
local compose_file="docker-compose.test.yml"
if [[ ! -f "$compose_file" ]]; then
log_error "Docker Compose文件不存在: $compose_file"
exit 1
fi
log_success "找到Docker Compose文件: $compose_file"
}
# 启动测试环境
start_test_environment() {
log_info "启动Docker测试环境..."
# 检查容器是否已运行
if docker-compose -f docker-compose.test.yml ps mysql-test | grep -q "Up"; then
log_info "MySQL容器已在运行"
else
log_info "启动MySQL容器..."
docker-compose -f docker-compose.test.yml up -d mysql-test
# 等待MySQL启动
log_info "等待MySQL启动..."
local max_attempts=30
local attempt=0
while [[ $attempt -lt $max_attempts ]]; do
if docker-compose -f docker-compose.test.yml exec -T mysql-test mysqladmin ping -h localhost --silent; then
log_success "MySQL启动成功"
break
fi
attempt=$((attempt + 1))
echo -n "."
sleep 2
done
if [[ $attempt -eq $max_attempts ]]; then
log_error "MySQL启动超时"
docker-compose -f docker-compose.test.yml logs mysql-test
exit 1
fi
fi
}
# 检查MySQL容器状态
check_mysql_container() {
log_info "检查MySQL容器状态..."
local container_status=$(docker-compose -f docker-compose.test.yml ps mysql-test | grep -E "mysql-test.*Up")
if [[ -z "$container_status" ]]; then
log_error "MySQL容器未运行"
return 1
fi
log_success "MySQL容器运行正常"
echo "$container_status"
}
# 检查MySQL连接
check_mysql_connection() {
log_info "检查MySQL连接..."
# 测试连接
if docker-compose -f docker-compose.test.yml exec -T mysql-test mysql -u test -ptest -e "SELECT 1" > /dev/null 2>&1; then
log_success "MySQL连接成功"
return 0
else
log_error "MySQL连接失败"
return 1
fi
}
# 检查数据库是否存在
check_database_exists() {
log_info "检查数据库是否存在..."
local databases=$(docker-compose -f docker-compose.test.yml exec -T mysql-test mysql -u test -ptest -e "SHOW DATABASES LIKE 'fendx_test'" 2>/dev/null)
if echo "$databases" | grep -q "fendx_test"; then
log_success "数据库 'fendx_test' 存在"
return 0
else
log_warning "数据库 'fendx_test' 不存在"
return 1
fi
}
# 创建数据库
create_database() {
log_info "创建数据库..."
if docker-compose -f docker-compose.test.yml exec -T mysql-test mysql -u test -ptest -e "CREATE DATABASE IF NOT EXISTS fendx_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"; then
log_success "数据库创建成功"
return 0
else
log_error "数据库创建失败"
return 1
fi
}
# 检查表结构
check_tables() {
log_info "检查数据库表..."
local tables=$(docker-compose -f docker-compose.test.yml exec -T mysql-test mysql -u test -ptest fendx_test -e "SHOW TABLES" 2>/dev/null)
if [[ -z "$tables" ]]; then
log_warning "数据库中没有表"
echo "💡 建议运行迁移命令:"
echo " docker-compose -f docker-compose.test.yml exec app php bin/console migrate:run"
return 1
fi
log_success "数据库表列表:"
echo "$tables" | tail -n +2 | while read -r table; do
if [[ -n "$table" ]]; then
local count=$(docker-compose -f docker-compose.test.yml exec -T mysql-test mysql -u test -ptest fendx_test -e "SELECT COUNT(*) FROM \`$table\`" 2>/dev/null | tail -n 1)
echo " - $table ($count 条记录)"
fi
done
}
# 检查用户数据
check_user_data() {
log_info "检查用户数据..."
local user_count=$(docker-compose -f docker-compose.test.yml exec -T mysql-test mysql -u test -ptest fendx_test -e "SELECT COUNT(*) FROM users" 2>/dev/null | tail -n 1 2>/dev/null)
if [[ -z "$user_count" ]]; then
log_warning "用户表不存在"
return 1
fi
echo "👥 用户数据:"
echo " - 总用户数: $user_count"
if [[ "$user_count" -gt 0 ]]; then
echo " - 最近注册用户:"
docker-compose -f docker-compose.test.yml exec -T mysql-test mysql -u test -ptest fendx_test -e "SELECT username, email, created_at FROM users ORDER BY created_at DESC LIMIT 3" 2>/dev/null | tail -n +2 | while read -r line; do
if [[ -n "$line" ]]; then
echo " * $line"
fi
done
fi
}
# 运行数据库迁移
run_migrations() {
log_info "运行数据库迁移..."
if docker-compose -f docker-compose.test.yml exec -T app php bin/console migrate:run; then
log_success "数据库迁移完成"
return 0
else
log_error "数据库迁移失败"
return 1
fi
}
# 填充测试数据
seed_test_data() {
log_info "填充测试数据..."
if docker-compose -f docker-compose.test.yml exec -T app php bin/console migrate:seed; then
log_success "测试数据填充完成"
return 0
else
log_error "测试数据填充失败"
return 1
fi
}
# 显示MySQL日志
show_mysql_logs() {
log_info "显示MySQL日志..."
docker-compose -f docker-compose.test.yml logs --tail=20 mysql-test
}
# 重启MySQL服务
restart_mysql() {
log_info "重启MySQL服务..."
docker-compose -f docker-compose.test.yml restart mysql-test
# 等待重启完成
sleep 10
if check_mysql_connection; then
log_success "MySQL重启成功"
else
log_error "MySQL重启失败"
return 1
fi
}
# 完整检查
full_check() {
log_info "开始完整数据库检查..."
echo "=================================================="
# 检查Docker
check_docker
# 检查Compose文件
check_compose_file
# 启动环境
start_test_environment
# 检查容器状态
check_mysql_container
# 检查连接
if ! check_mysql_connection; then
log_error "数据库连接失败尝试重启MySQL..."
restart_mysql
fi
echo "--------------------------------------------------"
# 检查数据库
if ! check_database_exists; then
create_database
fi
# 检查表
check_tables
# 检查用户数据
check_user_data
echo "=================================================="
log_success "数据库检查完成"
}
# 修复数据库
fix_database() {
log_info "开始修复数据库..."
# 确保环境运行
start_test_environment
# 创建数据库
if ! check_database_exists; then
create_database
fi
# 运行迁移
run_migrations
# 填充数据
seed_test_data
log_success "数据库修复完成"
}
# 显示帮助
show_help() {
cat << EOF
Docker环境数据库检查工具
用法: $0 [选项]
选项:
check 检查数据库状态 (默认)
fix 修复数据库问题
migrate 运行数据库迁移
seed 填充测试数据
logs 显示MySQL日志
restart 重启MySQL服务
help 显示帮助信息
示例:
$0 # 检查数据库
$0 check # 检查数据库
$0 fix # 修复数据库
$0 migrate # 运行迁移
$0 seed # 填充数据
$0 logs # 查看日志
EOF
}
# 主函数
main() {
local action="${1:-check}"
case "$action" in
"check")
full_check
;;
"fix")
fix_database
;;
"migrate")
start_test_environment
run_migrations
;;
"seed")
start_test_environment
seed_test_data
;;
"logs")
show_mysql_logs
;;
"restart")
restart_mysql
;;
"help"|"-h"|"--help")
show_help
;;
*)
log_error "未知选项: $action"
show_help
exit 1
;;
esac
}
# 运行主函数
main "$@"

158
scripts/quick-db-check.php Normal file
View File

@@ -0,0 +1,158 @@
<?php
declare(strict_types=1);
/**
* 快速数据库检查脚本
* 用法: php scripts/quick-db-check.php
*/
// 加载环境配置
function loadEnv(): void
{
$envFile = __DIR__ . '/../.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos($line, '=') !== false && !str_starts_with($line, '#')) {
list($key, $value) = explode('=', $line, 2);
$_ENV[trim($key)] = trim($value);
$_SERVER[trim($key)] = trim($value);
}
}
}
}
// 检查数据库连接
function checkDatabaseConnection(): array
{
$config = [
'host' => $_ENV['DB_HOST'] ?? 'localhost',
'port' => $_ENV['DB_PORT'] ?? '3306',
'database' => $_ENV['DB_DATABASE'] ?? 'fendx_php',
'username' => $_ENV['DB_USERNAME'] ?? 'root',
'password' => $_ENV['DB_PASSWORD'] ?? '',
];
echo "🔍 检查数据库连接...\n";
echo " 主机: {$config['host']}:{$config['port']}\n";
echo " 数据库: {$config['database']}\n";
echo " 用户名: {$config['username']}\n\n";
try {
$dsn = "mysql:host={$config['host']};port={$config['port']};charset=utf8mb4";
$pdo = new PDO($dsn, $config['username'], $config['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
echo "✅ 数据库连接成功\n";
return ['success' => true, 'pdo' => $pdo, 'config' => $config];
} catch (PDOException $e) {
echo "❌ 数据库连接失败: " . $e->getMessage() . "\n";
return ['success' => false, 'error' => $e->getMessage()];
}
}
// 检查数据库是否存在
function checkDatabaseExists(PDO $pdo, string $database): bool
{
try {
$stmt = $pdo->query("SHOW DATABASES LIKE '$database'");
$result = $stmt->fetch();
if ($result) {
echo "✅ 数据库 '$database' 存在\n";
return true;
} else {
echo "❌ 数据库 '$database' 不存在\n";
return false;
}
} catch (PDOException $e) {
echo "❌ 检查数据库失败: " . $e->getMessage() . "\n";
return false;
}
}
// 创建数据库
function createDatabase(PDO $pdo, string $database): bool
{
try {
$pdo->exec("CREATE DATABASE IF NOT EXISTS `$database` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
echo "✅ 数据库 '$database' 创建成功\n";
return true;
} catch (PDOException $e) {
echo "❌ 创建数据库失败: " . $e->getMessage() . "\n";
return false;
}
}
// 检查表结构
function checkTables(PDO $pdo, string $database): void
{
try {
$pdo->exec("USE `$database`");
$stmt = $pdo->query("SHOW TABLES");
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
if (empty($tables)) {
echo "⚠️ 数据库中没有表\n";
return;
}
echo "📊 数据库表:\n";
foreach ($tables as $table) {
$stmt = $pdo->query("SELECT COUNT(*) FROM `$table`");
$count = $stmt->fetchColumn();
echo " - $table ($count 条记录)\n";
}
} catch (PDOException $e) {
echo "❌ 检查表失败: " . $e->getMessage() . "\n";
}
}
// 主检查函数
function main(): void
{
echo "🚀 FendxPHP 快速数据库检查\n";
echo str_repeat("=", 40) . "\n";
// 加载环境配置
loadEnv();
// 检查数据库连接
$result = checkDatabaseConnection();
if (!$result['success']) {
echo "\n💡 解决建议:\n";
echo " 1. 检查数据库服务是否启动\n";
echo " 2. 验证数据库连接配置\n";
echo " 3. 确认用户权限正确\n";
return;
}
$pdo = $result['pdo'];
$database = $result['config']['database'];
echo "\n";
// 检查数据库是否存在
if (!checkDatabaseExists($pdo, $database)) {
echo "\n🔧 尝试创建数据库...\n";
if (createDatabase($pdo, $database)) {
echo "✅ 数据库已创建,请运行迁移命令:\n";
echo " php bin/console migrate:run\n";
}
return;
}
echo "\n";
// 检查表结构
checkTables($pdo, $database);
echo "\n" . str_repeat("=", 40) . "\n";
echo "✅ 数据库检查完成\n";
}
// 运行检查
main();
?>

571
scripts/run-tests.sh Normal file
View File

@@ -0,0 +1,571 @@
#!/bin/bash
# FendxPHP 自动化测试脚本
# 用法: ./scripts/run-tests.sh [test_type] [options]
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 配置
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TEST_RESULTS_DIR="${PROJECT_ROOT}/reports"
LOG_FILE="${TEST_RESULTS_DIR}/test.log"
# 创建必要的目录
mkdir -p "${TEST_RESULTS_DIR}"
mkdir -p "${TEST_RESULTS_DIR}/coverage"
mkdir -p "${TEST_RESULTS_DIR}/performance"
# 日志函数
log() {
echo -e "$1" | tee -a "${LOG_FILE}"
}
log_info() {
log "${BLUE}[INFO]${NC} $1"
}
log_success() {
log "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
log "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
log "${RED}[ERROR]${NC} $1"
}
# 显示帮助信息
show_help() {
cat << EOF
FendxPHP 自动化测试脚本
用法: $0 [test_type] [options]
测试类型:
unit 运行单元测试
integration 运行集成测试
api 运行API测试
e2e 运行端到端测试
performance 运行性能测试
security 运行安全测试
all 运行所有测试 (默认)
选项:
--coverage 生成覆盖率报告
--parallel=N 并行运行测试 (N个进程)
--filter=FILTER 过滤测试用例
--verbose 详细输出
--no-docker 不使用Docker环境
--clean 清理测试环境
--help 显示此帮助信息
示例:
$0 unit --coverage
$0 integration --filter=UserTest
$0 all --parallel=4 --coverage
$0 performance --verbose
EOF
}
# 检查依赖
check_dependencies() {
log_info "检查依赖..."
# 检查PHP
if ! command -v php &> /dev/null; then
log_error "PHP 未安装"
exit 1
fi
# 检查Composer
if ! command -v composer &> /dev/null; then
log_error "Composer 未安装"
exit 1
fi
# 检查PHPUnit
if ! command -v vendor/bin/phpunit &> /dev/null; then
log_info "安装 PHPUnit..."
composer require --dev phpunit/phpunit
fi
# 检查Docker (如果需要)
if [[ "$USE_DOCKER" == "true" ]]; then
if ! command -v docker &> /dev/null; then
log_error "Docker 未安装"
exit 1
fi
fi
log_success "依赖检查完成"
}
# 准备测试环境
prepare_environment() {
log_info "准备测试环境..."
# 复制环境配置
if [[ ! -f "${PROJECT_ROOT}/.env" ]]; then
cp "${PROJECT_ROOT}/.env.example" "${PROJECT_ROOT}/.env"
fi
# 设置测试环境变量
export APP_ENV=testing
export DB_CONNECTION=sqlite
export DB_DATABASE=:memory:
export CACHE_DRIVER=array
export SESSION_DRIVER=array
export QUEUE_CONNECTION=sync
# 安装依赖
log_info "安装Composer依赖..."
composer install --optimize-autoloader --no-interaction
# 创建测试数据库
log_info "创建测试数据库..."
php bin/console migrate:run --env=testing
log_success "测试环境准备完成"
}
# 启动Docker测试环境
start_docker_environment() {
if [[ "$USE_DOCKER" != "true" ]]; then
return
fi
log_info "启动Docker测试环境..."
# 停止现有容器
docker-compose -f docker-compose.test.yml down --volumes 2>/dev/null || true
# 启动测试容器
docker-compose -f docker-compose.test.yml up -d --build
# 等待服务就绪
log_info "等待服务启动..."
sleep 30
# 检查服务状态
if ! docker-compose -f docker-compose.test.yml ps | grep -q "Up"; then
log_error "Docker服务启动失败"
docker-compose -f docker-compose.test.yml logs
exit 1
fi
log_success "Docker环境启动完成"
}
# 停止Docker测试环境
stop_docker_environment() {
if [[ "$USE_DOCKER" != "true" ]]; then
return
fi
log_info "停止Docker测试环境..."
docker-compose -f docker-compose.test.yml down --volumes
}
# 运行单元测试
run_unit_tests() {
log_info "运行单元测试..."
local cmd="vendor/bin/phpunit tests/Unit --colors=always --log-junit=${TEST_RESULTS_DIR}/junit-unit.xml"
if [[ "$GENERATE_COVERAGE" == "true" ]]; then
cmd="$cmd --coverage-html=${TEST_RESULTS_DIR}/coverage/unit --coverage-clover=${TEST_RESULTS_DIR}/coverage/unit.xml"
fi
if [[ -n "$FILTER" ]]; then
cmd="$cmd --filter=$FILTER"
fi
if [[ "$VERBOSE" == "true" ]]; then
cmd="$cmd --verbose"
fi
if [[ "$PARALLEL" -gt 1 ]]; then
cmd="$cmd --processes=$PARALLEL"
fi
log_info "执行: $cmd"
if eval "$cmd"; then
log_success "单元测试通过"
return 0
else
log_error "单元测试失败"
return 1
fi
}
# 运行集成测试
run_integration_tests() {
log_info "运行集成测试..."
local cmd="vendor/bin/phpunit tests/Integration --colors=always --log-junit=${TEST_RESULTS_DIR}/junit-integration.xml"
if [[ -n "$FILTER" ]]; then
cmd="$cmd --filter=$FILTER"
fi
if [[ "$VERBOSE" == "true" ]]; then
cmd="$cmd --verbose"
fi
log_info "执行: $cmd"
if eval "$cmd"; then
log_success "集成测试通过"
return 0
else
log_error "集成测试失败"
return 1
fi
}
# 运行API测试
run_api_tests() {
log_info "运行API测试..."
# 启动应用服务器
log_info "启动应用服务器..."
php -S localhost:8000 -t public/ > /dev/null 2>&1 &
local SERVER_PID=$!
# 等待服务器启动
sleep 5
# 运行API测试
local cmd="vendor/bin/codecept run api --colors --xml=${TEST_RESULTS_DIR}/api-results.xml"
if [[ -n "$FILTER" ]]; then
cmd="$cmd -g $FILTER"
fi
log_info "执行: $cmd"
local result=0
if ! eval "$cmd"; then
log_error "API测试失败"
result=1
else
log_success "API测试通过"
fi
# 停止服务器
kill $SERVER_PID 2>/dev/null || true
return $result
}
# 运行端到端测试
run_e2e_tests() {
log_info "运行端到端测试..."
# 启动完整环境
start_docker_environment
# 运行E2E测试
local cmd="vendor/bin/codecept run e2e --colors --xml=${TEST_RESULTS_DIR}/e2e-results.xml"
if [[ -n "$FILTER" ]]; then
cmd="$cmd -g $FILTER"
fi
log_info "执行: $cmd"
local result=0
if ! eval "$cmd"; then
log_error "E2E测试失败"
result=1
else
log_success "E2E测试通过"
fi
# 清理环境
stop_docker_environment
return $result
}
# 运行性能测试
run_performance_tests() {
log_info "运行性能测试..."
# 启动应用服务器
php -S localhost:8000 -t public/ > /dev/null 2>&1 &
local SERVER_PID=$!
sleep 5
# 并发测试
log_info "执行并发测试..."
ab -n 1000 -c 10 http://localhost:8000/api/users > "${TEST_RESULTS_DIR}/performance/ab-results.txt" 2>&1
# 内存测试
log_info "执行内存测试..."
php -d memory_limit=1G -r "
\$start = memory_get_usage();
for (\$i = 0; \$i < 10000; \$i++) {
\$data = ['id' => \$i, 'name' => 'test'];
json_encode(\$data);
}
echo 'Memory used: ' . ((memory_get_usage() - \$start) / 1024 / 1024) . ' MB\n';
" > "${TEST_RESULTS_DIR}/performance/memory-test.txt"
# 数据库性能测试
log_info "执行数据库性能测试..."
php bin/console benchmark:database > "${TEST_RESULTS_DIR}/performance/database-test.txt" 2>&1
# 停止服务器
kill $SERVER_PID 2>/dev/null || true
log_success "性能测试完成"
return 0
}
# 运行安全测试
run_security_tests() {
log_info "运行安全测试..."
# 依赖漏洞扫描
log_info "扫描依赖漏洞..."
composer audit > "${TEST_RESULTS_DIR}/security/composer-audit.txt" 2>&1
# 代码安全分析
log_info "执行代码安全分析..."
if command -v vendor/bin/phpstan &> /dev/null; then
vendor/bin/phpstan analyse --level=8 --error-format=json > "${TEST_RESULTS_DIR}/security/phpstan-results.json" 2>&1
fi
# API安全测试
log_info "执行API安全测试..."
if command -v vendor/bin/codecept &> /dev/null; then
vendor/bin/codecept run security --colors --xml="${TEST_RESULTS_DIR}/security-results.xml" 2>&1 || true
fi
log_success "安全测试完成"
return 0
}
# 生成测试报告
generate_report() {
log_info "生成测试报告..."
local report_file="${TEST_RESULTS_DIR}/test-report.html"
cat > "$report_file" << EOF
<!DOCTYPE html>
<html>
<head>
<title>FendxPHP 测试报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background: #f0f0f0; padding: 20px; border-radius: 5px; }
.section { margin: 20px 0; }
.success { color: green; }
.error { color: red; }
.warning { color: orange; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<div class="header">
<h1>FendxPHP 测试报告</h1>
<p>生成时间: $(date)</p>
<p>测试类型: ${TEST_TYPE}</p>
</div>
<div class="section">
<h2>测试结果概览</h2>
<table>
<tr><th>测试类型</th><th>状态</th><th>详情</th></tr>
EOF
# 添加各种测试结果
if [[ -f "${TEST_RESULTS_DIR}/junit-unit.xml" ]]; then
echo "<tr><td>单元测试</td><td class='success'>通过</td><td><a href='coverage/unit/index.html'>查看覆盖率</a></td></tr>" >> "$report_file"
fi
if [[ -f "${TEST_RESULTS_DIR}/junit-integration.xml" ]]; then
echo "<tr><td>集成测试</td><td class='success'>通过</td><td>查看详细日志</td></tr>" >> "$report_file"
fi
cat >> "$report_file" << EOF
</table>
</div>
<div class="section">
<h2>性能指标</h2>
<table>
<tr><th>指标</th><th>值</th></tr>
EOF
# 添加性能指标
if [[ -f "${TEST_RESULTS_DIR}/performance/ab-results.txt" ]]; then
local rps=$(grep "Requests per second" "${TEST_RESULTS_DIR}/performance/ab-results.txt" | awk '{print $4}')
echo "<tr><td>每秒请求数</td><td>${rps}</td></tr>" >> "$report_file"
fi
cat >> "$report_file" << EOF
</table>
</div>
</body>
</html>
EOF
log_success "测试报告已生成: $report_file"
}
# 清理测试环境
cleanup() {
log_info "清理测试环境..."
# 停止Docker环境
stop_docker_environment
# 清理临时文件
rm -f "${PROJECT_ROOT}/.env.testing"
log_success "清理完成"
}
# 主函数
main() {
local test_type="${1:-all}"
local use_docker="true"
local generate_coverage="false"
local parallel="1"
local filter=""
local verbose="false"
local clean_only="false"
# 解析参数
shift
while [[ $# -gt 0 ]]; do
case $1 in
--coverage)
generate_coverage="true"
;;
--parallel=*)
parallel="${1#*=}"
;;
--filter=*)
filter="${1#*=}"
;;
--verbose)
verbose="true"
;;
--no-docker)
use_docker="false"
;;
--clean)
clean_only="true"
;;
--help)
show_help
exit 0
;;
*)
log_error "未知参数: $1"
show_help
exit 1
;;
esac
shift
done
# 设置全局变量
export USE_DOCKER="$use_docker"
export GENERATE_COVERAGE="$generate_coverage"
export PARALLEL="$parallel"
export FILTER="$filter"
export VERBOSE="$verbose"
export TEST_TYPE="$test_type"
# 只清理环境
if [[ "$clean_only" == "true" ]]; then
cleanup
exit 0
fi
# 捕获退出信号
trap cleanup EXIT
log_info "开始运行 FendxPHP 测试套件"
log_info "测试类型: $test_type"
log_info "使用Docker: $use_docker"
log_info "生成覆盖率: $generate_coverage"
# 检查依赖
check_dependencies
# 准备环境
prepare_environment
# 启动Docker环境如果需要
start_docker_environment
# 运行测试
local failed_tests=0
case $test_type in
unit)
run_unit_tests || ((failed_tests++))
;;
integration)
run_integration_tests || ((failed_tests++))
;;
api)
run_api_tests || ((failed_tests++))
;;
e2e)
run_e2e_tests || ((failed_tests++))
;;
performance)
run_performance_tests || ((failed_tests++))
;;
security)
run_security_tests || ((failed_tests++))
;;
all)
run_unit_tests || ((failed_tests++))
run_integration_tests || ((failed_tests++))
run_api_tests || ((failed_tests++))
run_performance_tests || ((failed_tests++))
run_security_tests || ((failed_tests++))
;;
*)
log_error "未知的测试类型: $test_type"
show_help
exit 1
;;
esac
# 生成报告
generate_report
# 输出结果
if [[ $failed_tests -eq 0 ]]; then
log_success "所有测试通过! 🎉"
exit 0
else
log_error "$failed_tests 个测试失败 ❌"
exit 1
fi
}
# 运行主函数
main "$@"

264
scripts/test-database.ps1 Normal file
View File

@@ -0,0 +1,264 @@
# 简化的数据库检查脚本
param(
[Parameter(Mandatory=$false)]
[ValidateSet("check", "fix", "help")]
[string]$Action = "check"
)
# 颜色输出
function Write-Success { param($m) Write-Host $m -ForegroundColor Green }
function Write-Error { param($m) Write-Host $m -ForegroundColor Red }
function Write-Warning { param($m) Write-Host $m -ForegroundColor Yellow }
function Write-Info { param($m) Write-Host $m -ForegroundColor Cyan }
# 加载环境配置
function Import-Environment {
$envFile = Join-Path $PSScriptRoot "..\.env"
if (Test-Path $envFile) {
Get-Content $envFile | ForEach-Object {
if ($_ -match '^([^=]+)=(.*)$' -and -not $_.StartsWith('#')) {
[Environment]::SetEnvironmentVariable($matches[1].Trim(), $matches[2].Trim(), "Process")
}
}
Write-Info "环境配置加载完成"
} else {
Write-Warning "环境配置文件不存在: $envFile"
}
}
# 检查数据库连接
function Test-DatabaseConnection {
$dbHost = if ($env:DB_HOST) { $env:DB_HOST } else { "localhost" }
$dbPort = if ($env:DB_PORT) { $env:DB_PORT } else { "3306" }
$dbDatabase = if ($env:DB_DATABASE) { $env:DB_DATABASE } else { "fendx_php" }
$dbUsername = if ($env:DB_USERNAME) { $env:DB_USERNAME } else { "root" }
$dbPassword = if ($env:DB_PASSWORD) { $env:DB_PASSWORD } else { "" }
$config = @{
Host = $dbHost
Port = $dbPort
Database = $dbDatabase
Username = $dbUsername
Password = $dbPassword
}
Write-Info "检查数据库连接..."
Write-Info " 主机: $($config.Host):$($config.Port)"
Write-Info " 数据库: $($config.Database)"
Write-Info " 用户名: $($config.Username)"
# 尝试使用MySQL客户端
try {
& mysql -h $config.Host -P $config.Port -u $config.Username -p$config.Password -e "SELECT 1" 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Success "数据库连接成功"
return @{ Success = $true; Config = $config; Docker = $false }
}
} catch {
# 忽略错误
}
# 尝试使用Docker
if (Get-Command docker -ErrorAction SilentlyContinue) {
Write-Info "尝试使用Docker连接数据库..."
try {
& docker info >$null 2>&1
if ($LASTEXITCODE -eq 0) {
# 检查MySQL容器
$container = & docker ps --filter "name=fendx-mysql-test" --format "table {{.Names}}`t{{.Status}}" | Select-Object -Skip 1
if ($container -and $container -like "*Up*") {
# 测试Docker连接
& docker exec fendx-mysql-test mysql -u test -ptest -e "SELECT 1" >$null 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Success "Docker数据库连接成功"
return @{ Success = $true; Config = $config; Docker = $true }
}
} else {
Write-Warning "MySQL容器未运行"
}
}
} catch {
Write-Warning "Docker连接失败"
}
}
Write-Error "数据库连接失败"
return @{ Success = $false }
}
# 检查数据库是否存在
function Test-DatabaseExists {
param($Config, [switch]$UseDocker)
$database = $Config.Database
if ($UseDocker) {
Write-Info "检查数据库是否存在 (Docker)..."
$result = & docker exec fendx-mysql-test mysql -u test -ptest -e "SHOW DATABASES LIKE '$database'" 2>$null
if ($result -match $database) {
Write-Success "数据库 '$database' 存在"
return $true
}
} else {
Write-Info "检查数据库是否存在..."
$result = & mysql -h $Config.Host -P $Config.Port -u $Config.Username -p$Config.Password -e "SHOW DATABASES LIKE '$database'" 2>$null
if ($result -match $database) {
Write-Success "数据库 '$database' 存在"
return $true
}
}
Write-Warning "数据库 '$database' 不存在"
return $false
}
# 创建数据库
function New-Database {
param($Config, [switch]$UseDocker)
$database = $Config.Database
Write-Info "创建数据库 '$database'..."
if ($UseDocker) {
$sql = "CREATE DATABASE IF NOT EXISTS `$database` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
& docker exec fendx-mysql-test mysql -u test -ptest -e $sql >$null 2>&1
} else {
$sql = "CREATE DATABASE IF NOT EXISTS `$database` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
& mysql -h $Config.Host -P $Config.Port -u $Config.Username -p$Config.Password -e $sql >$null 2>&1
}
if ($LASTEXITCODE -eq 0) {
Write-Success "数据库创建成功"
return $true
} else {
Write-Error "数据库创建失败"
return $false
}
}
# 运行迁移
function Invoke-Migration {
param([switch]$UseDocker)
Write-Info "运行数据库迁移..."
if ($UseDocker) {
& docker-compose -f docker-compose.test.yml exec -T app php bin/console migrate:run
} else {
& php bin/console migrate:run
}
if ($LASTEXITCODE -eq 0) {
Write-Success "数据库迁移完成"
return $true
} else {
Write-Error "数据库迁移失败"
return $false
}
}
# 完整检查
function Invoke-FullCheck {
Write-Host "🚀 FendxPHP 数据库检查 (PowerShell)" -ForegroundColor Green
Write-Host "==================================================" -ForegroundColor Cyan
Import-Environment
$connectionResult = Test-DatabaseConnection
if (-not $connectionResult.Success) {
Write-Host "`n💡 解决建议:" -ForegroundColor Yellow
Write-Host " 1. 检查数据库服务是否启动" -ForegroundColor White
Write-Host " 2. 验证数据库连接配置" -ForegroundColor White
Write-Host " 3. 确认用户权限正确" -ForegroundColor White
return
}
$config = $connectionResult.Config
$useDocker = $connectionResult.Docker
Write-Host "`n--------------------------------------------------" -ForegroundColor Cyan
if (-not (Test-DatabaseExists $config -UseDocker:$useDocker)) {
Write-Host "`n🔧 数据库不存在,建议运行:" -ForegroundColor Yellow
Write-Host " .\scripts\test-database.ps1 fix" -ForegroundColor White
return
}
Write-Host "`n==================================================" -ForegroundColor Cyan
Write-Success "数据库检查完成"
}
# 修复数据库
function Repair-Database {
Write-Info "开始修复数据库..."
Import-Environment
$connectionResult = Test-DatabaseConnection
if (-not $connectionResult.Success) {
Write-Error "数据库连接失败,无法修复"
return
}
$config = $connectionResult.Config
$useDocker = $connectionResult.Docker
if (-not (Test-DatabaseExists $config -UseDocker:$useDocker)) {
New-Database $config -UseDocker:$useDocker
}
Invoke-Migration -UseDocker:$useDocker
Write-Success "数据库修复完成"
}
# 显示帮助
function Show-Help {
Write-Host @"
FendxPHP ()
: .\scripts\test-database.ps1 []
:
check ()
fix
help
:
.\scripts\test-database.ps1 #
.\scripts\test-database.ps1 check #
.\scripts\test-database.ps1 fix #
"@ -ForegroundColor White
}
# 主函数
function Main {
switch ($Action.ToLower()) {
"check" {
Invoke-FullCheck
}
"fix" {
Repair-Database
}
"help" {
Show-Help
}
default {
Write-Error "未知选项: $Action"
Show-Help
exit 1
}
}
}
# 运行主函数
try {
Main
} catch {
Write-Error "脚本执行失败: $($_.Exception.Message)"
exit 1
}