mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
feat(database): 添加用户角色权限系统及相关监控功能
- 创建用户表(users)包含基本信息和认证字段 - 创建角色表(roles)用于权限控制 - 创建权限表(permissions)定义系统权限 - 创建用户角色关联表(user_roles)建立用户与角色关系 - 创建角色权限关联表(role_permissions)建立角色与权限关系 - 创建迁移记录表(migrations)追踪数据库变更 - 添加AdminController提供管理员面板功能 - 实现系统监控、配置管理、缓存清理等功能 - 添加AOP切面编程支持的各种通知类型 - 实现告警管理AlertManager支持多渠道告警 - 添加文档注解接口规范
This commit is contained in:
453
scripts/check-database.php
Normal file
453
scripts/check-database.php
Normal 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
466
scripts/check-database.ps1
Normal 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
366
scripts/check-docker-db.sh
Normal 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
158
scripts/quick-db-check.php
Normal 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
571
scripts/run-tests.sh
Normal 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
264
scripts/test-database.ps1
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user