Эх сурвалжийг харах

增加Cleanup模块数据库备份功能

- 新增数据库备份类型,将INSERT语句直接存储到数据库表中
- 创建cleanup_sql_backups表存储SQL备份记录
- 新增CleanupSqlBackup模型处理SQL备份数据
- 扩展BACKUP_TYPE枚举,添加DATABASE类型作为默认备份方式
- 更新BackupLogic类,实现数据库备份功能
- 在CleanupService中添加SQL备份管理方法
- 更新配置文件,将数据库备份设为默认方式
- 完善文档说明数据库备份的优势和使用方法
- 提供测试SQL语句验证备份功能
notfff 6 сар өмнө
parent
commit
ca83269d7d

+ 99 - 2
app/Module/Cleanup/Docs/数据备份设计.md

@@ -9,6 +9,7 @@
 - **空间优化**:采用压缩和增量备份减少存储空间
 
 ### 1.2 备份类型
+- **数据库备份**:将INSERT语句直接存储到数据库表中(默认方式)
 - **SQL备份**:生成INSERT语句的SQL文件
 - **JSON备份**:将数据导出为JSON格式
 - **CSV备份**:将数据导出为CSV格式(适合大数据量)
@@ -40,7 +41,30 @@
 
 ## 3. 数据库设计扩展
 
-### 3.1 备份记录表 (cleanup_backups)
+### 3.1 SQL备份记录表 (cleanup_sql_backups)
+
+```sql
+CREATE TABLE `kku_cleanup_sql_backups` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `backup_id` bigint(20) unsigned NOT NULL COMMENT '关联备份记录ID',
+  `table_name` varchar(100) NOT NULL COMMENT '表名',
+  `sql_content` longtext NOT NULL COMMENT 'INSERT语句内容',
+  `records_count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '记录数量',
+  `content_size` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '内容大小(字节)',
+  `content_hash` varchar(64) DEFAULT NULL COMMENT '内容SHA256哈希',
+  `backup_conditions` json DEFAULT NULL COMMENT '备份条件',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_backup_id` (`backup_id`),
+  KEY `idx_table_name` (`table_name`),
+  KEY `idx_records_count` (`records_count`),
+  KEY `idx_created_at` (`created_at`),
+  FOREIGN KEY (`backup_id`) REFERENCES `kku_cleanup_backups` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='SQL备份记录表';
+```
+
+### 3.2 备份记录表 (cleanup_backups)
 
 ```sql
 CREATE TABLE `kku_cleanup_backups` (
@@ -92,7 +116,80 @@ CREATE TABLE `kku_cleanup_backup_files` (
 
 ## 4. 备份实现方案
 
-### 4.1 SQL备份实现
+### 4.1 数据库备份实现
+
+#### 4.1.1 存储INSERT语句到数据库
+```php
+/**
+ * 备份表到数据库
+ *
+ * @param CleanupBackup $backup 备份记录
+ * @param string $tableName 表名
+ * @return array 备份结果
+ */
+private static function backupTableToDatabase(CleanupBackup $backup, string $tableName): array
+{
+    try {
+        // 获取表数据
+        $records = DB::table($tableName)->get();
+
+        if ($records->isEmpty()) {
+            // 即使没有数据也创建记录,记录表结构
+            $sqlContent = "-- 表 {$tableName} 的数据备份\n";
+            $sqlContent .= "-- 备份时间: " . now()->toDateTimeString() . "\n";
+            $sqlContent .= "-- 该表无数据记录\n\n";
+
+            // 获取表结构
+            $createTable = DB::select("SHOW CREATE TABLE `{$tableName}`")[0];
+            $sqlContent .= $createTable->{'Create Table'} . ";\n";
+
+            $recordsCount = 0;
+        } else {
+            // 生成INSERT语句
+            $sqlContent = static::generateInsertStatements($tableName, $records);
+            $recordsCount = $records->count();
+        }
+
+        $contentSize = strlen($sqlContent);
+        $contentHash = hash('sha256', $sqlContent);
+
+        // 保存到数据库
+        CleanupSqlBackup::create([
+            'backup_id' => $backup->id,
+            'table_name' => $tableName,
+            'sql_content' => $sqlContent,
+            'records_count' => $recordsCount,
+            'content_size' => $contentSize,
+            'content_hash' => $contentHash,
+            'backup_conditions' => null,
+        ]);
+
+        return [
+            'success' => true,
+            'message' => "表 {$tableName} 数据库备份成功",
+            'file_size' => $contentSize,
+            'records_count' => $recordsCount,
+        ];
+
+    } catch (\Exception $e) {
+        return [
+            'success' => false,
+            'message' => $e->getMessage(),
+            'file_size' => 0,
+            'records_count' => 0,
+        ];
+    }
+}
+```
+
+#### 4.1.2 数据库备份的优势
+- **查询快速**:直接存储在数据库中,查询和检索非常快速
+- **无文件管理**:不需要管理文件系统,避免文件丢失问题
+- **事务安全**:利用数据库事务保证数据一致性
+- **压缩存储**:数据库自动优化存储,无需额外压缩
+- **备份可靠**:随数据库一起备份,不会单独丢失
+
+### 4.2 SQL文件备份实现
 
 #### 4.1.1 生成INSERT语句
 ```php

+ 21 - 10
app/Module/Cleanup/Enums/BACKUP_TYPE.php

@@ -9,23 +9,29 @@ namespace App\Module\Cleanup\Enums;
  */
 enum BACKUP_TYPE: int
 {
+    /**
+     * 数据库备份
+     * 将INSERT语句直接存储到数据库表中
+     */
+    case DATABASE = 1;
+
     /**
      * SQL格式备份
-     * 生成标准的SQL INSERT语句
+     * 生成标准的SQL INSERT语句文件
      */
-    case SQL = 1;
+    case SQL = 2;
 
     /**
      * JSON格式备份
      * 将数据导出为JSON格式
      */
-    case JSON = 2;
+    case JSON = 3;
 
     /**
      * CSV格式备份
      * 将数据导出为CSV格式
      */
-    case CSV = 3;
+    case CSV = 4;
 
     /**
      * 获取备份类型的描述
@@ -33,9 +39,10 @@ enum BACKUP_TYPE: int
     public function getDescription(): string
     {
         return match($this) {
-            self::SQL => 'SQL格式',
-            self::JSON => 'JSON格式',
-            self::CSV => 'CSV格式',
+            self::DATABASE => '数据库备份',
+            self::SQL => 'SQL文件',
+            self::JSON => 'JSON文件',
+            self::CSV => 'CSV文件',
         };
     }
 
@@ -45,9 +52,10 @@ enum BACKUP_TYPE: int
     public function getDetailDescription(): string
     {
         return match($this) {
-            self::SQL => '生成标准的SQL INSERT语句,可直接导入数据库',
-            self::JSON => '将数据导出为JSON格式,便于程序处理',
-            self::CSV => '将数据导出为CSV格式,便于Excel等工具查看',
+            self::DATABASE => '将INSERT语句直接存储到数据库表中,查询快速,默认方式',
+            self::SQL => '生成标准的SQL INSERT语句文件,可直接导入数据库',
+            self::JSON => '将数据导出为JSON格式文件,便于程序处理',
+            self::CSV => '将数据导出为CSV格式文件,便于Excel等工具查看',
         };
     }
 
@@ -57,6 +65,7 @@ enum BACKUP_TYPE: int
     public function getFileExtension(): string
     {
         return match($this) {
+            self::DATABASE => '', // 数据库备份无文件扩展名
             self::SQL => 'sql',
             self::JSON => 'json',
             self::CSV => 'csv',
@@ -69,6 +78,7 @@ enum BACKUP_TYPE: int
     public function getMimeType(): string
     {
         return match($this) {
+            self::DATABASE => 'application/octet-stream', // 数据库备份
             self::SQL => 'application/sql',
             self::JSON => 'application/json',
             self::CSV => 'text/csv',
@@ -81,6 +91,7 @@ enum BACKUP_TYPE: int
     public function supportsStructure(): bool
     {
         return match($this) {
+            self::DATABASE => true,
             self::SQL => true,
             self::JSON => false,
             self::CSV => false,

+ 0 - 340
app/Module/Cleanup/INSTALL.md

@@ -1,340 +0,0 @@
-# Cleanup 模块安装指南
-
-## 📦 安装步骤
-
-### 1. 注册服务提供者
-
-在 `config/app.php` 的 `providers` 数组中添加:
-
-```php
-'providers' => [
-    // ... 其他服务提供者
-    App\Module\Cleanup\CleanupServiceProvider::class,
-],
-```
-
-### 2. 创建数据库表
-
-执行以下SQL文件创建必要的数据库表:
-
-```bash
-# 执行数据库表创建脚本
-mysql -u your_username -p your_database < app/Module/Cleanup/Databases/GenerateSql/cleanup_tables.sql
-```
-
-或者手动执行SQL文件中的语句。
-
-### 3. 发布配置文件
-
-```bash
-# 发布配置文件
-php artisan vendor:publish --tag=cleanup-config
-
-# 发布数据库迁移文件
-php artisan vendor:publish --tag=cleanup-migrations
-```
-
-### 4. 配置环境变量
-
-在 `.env` 文件中添加以下配置:
-
-```env
-# Cleanup 模块配置
-CLEANUP_ENABLED=true
-CLEANUP_DEBUG=false
-CLEANUP_TIMEZONE=Asia/Shanghai
-
-# 数据库配置
-CLEANUP_TABLE_PREFIX=kku_
-CLEANUP_DB_CONNECTION=mysql
-CLEANUP_BATCH_SIZE=1000
-CLEANUP_QUERY_TIMEOUT=300
-
-# 备份配置
-CLEANUP_BACKUP_TYPE=1
-CLEANUP_BACKUP_COMPRESSION=2
-CLEANUP_BACKUP_PATH=/path/to/backup/storage
-CLEANUP_BACKUP_RETENTION=30
-CLEANUP_BACKUP_STRUCTURE=true
-
-# 执行配置
-CLEANUP_MAX_CONCURRENT=3
-CLEANUP_TASK_TIMEOUT=3600
-CLEANUP_ENABLE_PREVIEW=true
-CLEANUP_REQUIRE_CONFIRMATION=true
-CLEANUP_AUTO_BACKUP=true
-
-# 安全配置
-CLEANUP_IP_WHITELIST=false
-CLEANUP_ALLOWED_IPS=127.0.0.1
-CLEANUP_REQUIRE_ADMIN=true
-
-# 通知配置
-CLEANUP_NOTIFICATIONS=true
-CLEANUP_NOTIFY_MAIL=true
-CLEANUP_NOTIFY_DB=true
-CLEANUP_MAIL_TO=admin@example.com
-```
-
-### 5. 清除配置缓存
-
-```bash
-php artisan config:clear
-php artisan cache:clear
-```
-
-## 🚀 快速开始
-
-### 1. 扫描数据表
-
-首次安装后,需要扫描系统中的数据表:
-
-```bash
-# 扫描所有数据表并生成默认配置
-php artisan cleanup:scan-tables
-
-# 查看扫描结果详情
-php artisan cleanup:scan-tables --show-details
-
-# 强制重新扫描(会覆盖现有配置)
-php artisan cleanup:scan-tables --force
-```
-
-### 2. 查看扫描结果
-
-扫描完成后,可以通过以下方式查看结果:
-
-```bash
-# 查看系统状态
-php artisan cleanup:data status
-
-# 查看推荐的清理计划
-php artisan cleanup:data status --recommendations
-```
-
-### 3. 创建第一个清理计划
-
-```bash
-# 创建一个简单的日志清理计划
-php artisan cleanup:data create-plan \
-    --name="日志数据清理" \
-    --type=3 \
-    --categories=2
-
-# 创建农场模块清理计划
-php artisan cleanup:data create-plan \
-    --name="农场模块清理" \
-    --type=2 \
-    --modules=Farm \
-    --exclude-tables=kku_farm_configs
-```
-
-### 4. 预览清理结果
-
-在实际执行清理之前,建议先预览:
-
-```bash
-# 预览计划清理结果
-php artisan cleanup:data preview 1
-
-# 创建任务并预览
-php artisan cleanup:data create-task 1
-php artisan cleanup:data preview 2
-```
-
-## 🔧 配置说明
-
-### 重要配置项
-
-#### 安全配置
-```php
-'security' => [
-    // 受保护的表(永远不会被清理)
-    'protected_tables' => [
-        'kku_users',
-        'kku_user_profiles',
-        'kku_configs',
-        // ... 添加更多受保护的表
-    ],
-    
-    // 受保护的模块
-    'protected_modules' => [
-        'User',
-        'Permission',
-        'Config',
-        // ... 添加更多受保护的模块
-    ],
-],
-```
-
-#### 性能配置
-```php
-'database' => [
-    'batch_size' => [
-        'default' => 1000,  // 默认批处理大小
-        'min' => 100,       // 最小批处理大小
-        'max' => 10000,     // 最大批处理大小
-    ],
-],
-```
-
-#### 备份配置
-```php
-'backup' => [
-    'storage_path' => storage_path('app/cleanup/backups'),
-    'retention_days' => 30,
-    'max_file_size_mb' => 500,
-],
-```
-
-## 🛡️ 安全注意事项
-
-### 1. 权限设置
-
-确保只有授权用户可以访问清理功能:
-
-```php
-// 在路由中间件中添加权限检查
-Route::middleware(['auth', 'admin'])->group(function () {
-    // Cleanup 路由
-});
-```
-
-### 2. 数据保护
-
-- 配置受保护的表和模块
-- 启用自动备份功能
-- 设置合理的保留期限
-
-### 3. 操作审计
-
-所有清理操作都会记录详细日志:
-
-```bash
-# 查看操作日志
-tail -f storage/logs/cleanup.log
-
-# 查看特定任务的日志
-php artisan cleanup:data status 1
-```
-
-## 🔍 故障排除
-
-### 常见问题
-
-#### 1. 表扫描失败
-```bash
-# 检查数据库连接
-php artisan cleanup:scan-tables --force
-
-# 查看详细错误信息
-tail -f storage/logs/laravel.log
-```
-
-#### 2. 权限不足
-```bash
-# 检查数据库用户权限
-SHOW GRANTS FOR 'your_user'@'localhost';
-
-# 确保用户有以下权限:
-# SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
-```
-
-#### 3. 内存不足
-```env
-# 增加内存限制
-CLEANUP_MEMORY_LIMIT=1024
-```
-
-#### 4. 执行超时
-```env
-# 增加超时时间
-CLEANUP_TASK_TIMEOUT=7200
-CLEANUP_QUERY_TIMEOUT=600
-```
-
-### 调试模式
-
-启用调试模式获取更多信息:
-
-```env
-CLEANUP_DEBUG=true
-CLEANUP_LOG_LEVEL=debug
-CLEANUP_LOG_SQL=true
-```
-
-## 📊 监控和维护
-
-### 定期维护任务
-
-系统会自动注册以下定时任务:
-
-```php
-// 每天凌晨2点清理过期备份
-$schedule->call(function () {
-    CleanupService::cleanExpiredBackups(30);
-})->dailyAt('02:00');
-
-// 每天凌晨3点清理历史日志
-$schedule->call(function () {
-    CleanupService::cleanHistoryLogs(30);
-})->dailyAt('03:00');
-
-// 每小时更新表统计信息
-$schedule->call(function () {
-    CleanupService::scanTables(false);
-})->hourly();
-```
-
-### 健康检查
-
-```bash
-# 检查系统健康状态
-php artisan cleanup:data status
-
-# 查看详细的健康报告
-php artisan cleanup:health-check
-```
-
-### 性能监控
-
-```bash
-# 查看性能统计
-php artisan cleanup:stats
-
-# 查看最近的任务执行情况
-php artisan cleanup:data status --recent=7
-```
-
-## 🔄 升级指南
-
-### 从旧版本升级
-
-1. 备份现有数据
-2. 更新代码文件
-3. 运行数据库迁移
-4. 更新配置文件
-5. 清除缓存
-
-```bash
-# 升级步骤
-php artisan down
-# 更新代码...
-php artisan migrate
-php artisan config:clear
-php artisan cache:clear
-php artisan up
-```
-
-## 📞 技术支持
-
-如果遇到问题,请:
-
-1. 查看日志文件:`storage/logs/cleanup.log`
-2. 检查配置是否正确
-3. 确认数据库权限
-4. 联系技术支持团队
-
----
-
-**安装完成后,建议先在测试环境中验证所有功能正常工作,然后再部署到生产环境。**

+ 120 - 1
app/Module/Cleanup/Logics/BackupLogic.php

@@ -4,6 +4,7 @@ namespace App\Module\Cleanup\Logics;
 
 use App\Module\Cleanup\Models\CleanupBackup;
 use App\Module\Cleanup\Models\CleanupBackupFile;
+use App\Module\Cleanup\Models\CleanupSqlBackup;
 use App\Module\Cleanup\Models\CleanupPlan;
 use App\Module\Cleanup\Models\CleanupTask;
 use App\Module\Cleanup\Enums\BACKUP_TYPE;
@@ -221,6 +222,11 @@ class BackupLogic
     private static function backupTable(CleanupBackup $backup, string $tableName, BACKUP_TYPE $backupType, COMPRESSION_TYPE $compressionType): array
     {
         try {
+            // 数据库备份类型直接存储到数据库
+            if ($backupType === BACKUP_TYPE::DATABASE) {
+                return static::backupTableToDatabase($backup, $tableName);
+            }
+
             // 生成备份文件名
             $fileName = static::generateBackupFileName($backup, $tableName, $backupType);
             $filePath = "cleanup/backups/{$backup->id}/{$fileName}";
@@ -357,6 +363,116 @@ class BackupLogic
         return $csv;
     }
 
+    /**
+     * 备份表到数据库
+     *
+     * @param CleanupBackup $backup 备份记录
+     * @param string $tableName 表名
+     * @return array 备份结果
+     */
+    private static function backupTableToDatabase(CleanupBackup $backup, string $tableName): array
+    {
+        try {
+            // 获取表数据
+            $records = DB::table($tableName)->get();
+
+            if ($records->isEmpty()) {
+                // 即使没有数据也创建记录,记录表结构
+                $sqlContent = "-- 表 {$tableName} 的数据备份\n";
+                $sqlContent .= "-- 备份时间: " . now()->toDateTimeString() . "\n";
+                $sqlContent .= "-- 该表无数据记录\n\n";
+
+                // 获取表结构
+                $createTable = DB::select("SHOW CREATE TABLE `{$tableName}`")[0];
+                $sqlContent .= $createTable->{'Create Table'} . ";\n";
+
+                $recordsCount = 0;
+            } else {
+                // 生成INSERT语句
+                $sqlContent = static::generateInsertStatements($tableName, $records);
+                $recordsCount = $records->count();
+            }
+
+            $contentSize = strlen($sqlContent);
+            $contentHash = hash('sha256', $sqlContent);
+
+            // 保存到数据库
+            CleanupSqlBackup::create([
+                'backup_id' => $backup->id,
+                'table_name' => $tableName,
+                'sql_content' => $sqlContent,
+                'records_count' => $recordsCount,
+                'content_size' => $contentSize,
+                'content_hash' => $contentHash,
+                'backup_conditions' => null, // 暂时不支持条件备份
+            ]);
+
+            return [
+                'success' => true,
+                'message' => "表 {$tableName} 数据库备份成功",
+                'file_size' => $contentSize, // 用内容大小代替文件大小
+                'records_count' => $recordsCount,
+            ];
+
+        } catch (\Exception $e) {
+            return [
+                'success' => false,
+                'message' => $e->getMessage(),
+                'file_size' => 0,
+                'records_count' => 0,
+            ];
+        }
+    }
+
+    /**
+     * 生成INSERT语句
+     *
+     * @param string $tableName 表名
+     * @param \Illuminate\Support\Collection $records 记录集合
+     * @return string SQL内容
+     */
+    private static function generateInsertStatements(string $tableName, $records): string
+    {
+        $sql = "-- 表 {$tableName} 的数据备份\n";
+        $sql .= "-- 备份时间: " . now()->toDateTimeString() . "\n";
+        $sql .= "-- 记录数量: " . $records->count() . "\n\n";
+
+        // 获取表结构
+        $createTable = DB::select("SHOW CREATE TABLE `{$tableName}`")[0];
+        $sql .= $createTable->{'Create Table'} . ";\n\n";
+
+        // 生成INSERT语句
+        if ($records->isNotEmpty()) {
+            $sql .= "-- 数据插入\n";
+
+            // 获取字段名
+            $firstRecord = (array) $records->first();
+            $columns = array_keys($firstRecord);
+            $columnList = '`' . implode('`, `', $columns) . '`';
+
+            $sql .= "INSERT INTO `{$tableName}` ({$columnList}) VALUES\n";
+
+            $values = [];
+            foreach ($records as $record) {
+                $recordArray = (array) $record;
+                $escapedValues = array_map(function ($value) {
+                    if ($value === null) {
+                        return 'NULL';
+                    } elseif (is_numeric($value)) {
+                        return $value;
+                    } else {
+                        return "'" . addslashes($value) . "'";
+                    }
+                }, $recordArray);
+                $values[] = '(' . implode(', ', $escapedValues) . ')';
+            }
+
+            $sql .= implode(",\n", $values) . ";\n";
+        }
+
+        return $sql;
+    }
+
     /**
      * 压缩内容
      *
@@ -518,7 +634,7 @@ class BackupLogic
         try {
             DB::beginTransaction();
 
-            $backup = CleanupBackup::with('files')->findOrFail($backupId);
+            $backup = CleanupBackup::with(['files', 'sqlBackups'])->findOrFail($backupId);
 
             // 删除备份文件
             foreach ($backup->files as $file) {
@@ -537,6 +653,9 @@ class BackupLogic
             // 删除备份文件记录
             $backup->files()->delete();
 
+            // 删除SQL备份记录
+            $backup->sqlBackups()->delete();
+
             // 删除备份记录
             $backup->delete();
 

+ 8 - 0
app/Module/Cleanup/Models/CleanupBackup.php

@@ -284,6 +284,14 @@ class CleanupBackup extends ModelCore
         return $this->hasMany(CleanupBackupFile::class, 'backup_id');
     }
 
+    /**
+     * 关联SQL备份记录
+     */
+    public function sqlBackups(): HasMany
+    {
+        return $this->hasMany(CleanupSqlBackup::class, 'backup_id');
+    }
+
     /**
      * 作用域:按计划筛选
      */

+ 171 - 0
app/Module/Cleanup/Models/CleanupSqlBackup.php

@@ -0,0 +1,171 @@
+<?php
+
+namespace App\Module\Cleanup\Models;
+
+use UCore\ModelCore;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * SQL备份记录模型
+ * 
+ * 存储INSERT语句到数据库表中
+ */
+class CleanupSqlBackup extends ModelCore
+{
+    /**
+     * 数据表名
+     */
+    protected $table = 'cleanup_sql_backups';
+
+    // field start
+    /**
+     * 可批量赋值的字段
+     */
+    protected $fillable = [
+        'backup_id',
+        'table_name',
+        'sql_content',
+        'records_count',
+        'content_size',
+        'content_hash',
+        'backup_conditions',
+    ];
+    // field end
+
+    /**
+     * 字段类型转换
+     */
+    protected $casts = [
+        'backup_conditions' => 'array',
+        'records_count' => 'integer',
+        'content_size' => 'integer',
+    ];
+
+    /**
+     * 隐藏字段
+     */
+    protected $hidden = [
+        'sql_content', // 默认隐藏SQL内容,避免大量数据传输
+    ];
+
+    /**
+     * 关联备份记录
+     */
+    public function backup(): BelongsTo
+    {
+        return $this->belongsTo(CleanupBackup::class, 'backup_id');
+    }
+
+    /**
+     * 获取格式化的内容大小
+     */
+    public function getFormattedSizeAttribute(): string
+    {
+        $size = $this->content_size;
+        
+        if ($size < 1024) {
+            return $size . ' B';
+        } elseif ($size < 1024 * 1024) {
+            return round($size / 1024, 2) . ' KB';
+        } elseif ($size < 1024 * 1024 * 1024) {
+            return round($size / (1024 * 1024), 2) . ' MB';
+        } else {
+            return round($size / (1024 * 1024 * 1024), 2) . ' GB';
+        }
+    }
+
+    /**
+     * 获取SQL内容预览(前100个字符)
+     */
+    public function getSqlPreviewAttribute(): string
+    {
+        if (empty($this->sql_content)) {
+            return '';
+        }
+        
+        $preview = substr($this->sql_content, 0, 100);
+        if (strlen($this->sql_content) > 100) {
+            $preview .= '...';
+        }
+        
+        return $preview;
+    }
+
+    /**
+     * 验证内容哈希
+     */
+    public function verifyContentHash(): bool
+    {
+        if (empty($this->sql_content) || empty($this->content_hash)) {
+            return false;
+        }
+        
+        $currentHash = hash('sha256', $this->sql_content);
+        return $currentHash === $this->content_hash;
+    }
+
+    /**
+     * 作用域:按备份ID筛选
+     */
+    public function scopeByBackup($query, int $backupId)
+    {
+        return $query->where('backup_id', $backupId);
+    }
+
+    /**
+     * 作用域:按表名筛选
+     */
+    public function scopeByTable($query, string $tableName)
+    {
+        return $query->where('table_name', $tableName);
+    }
+
+    /**
+     * 作用域:按记录数量排序
+     */
+    public function scopeOrderByRecords($query, string $direction = 'desc')
+    {
+        return $query->orderBy('records_count', $direction);
+    }
+
+    /**
+     * 作用域:按内容大小排序
+     */
+    public function scopeOrderBySize($query, string $direction = 'desc')
+    {
+        return $query->orderBy('content_size', $direction);
+    }
+
+    /**
+     * 获取统计信息
+     */
+    public static function getStats(): array
+    {
+        return [
+            'total_count' => static::count(),
+            'total_records' => static::sum('records_count'),
+            'total_size' => static::sum('content_size'),
+            'avg_records_per_backup' => static::avg('records_count'),
+            'avg_size_per_backup' => static::avg('content_size'),
+        ];
+    }
+
+    /**
+     * 获取按表名分组的统计
+     */
+    public static function getTableStats(): array
+    {
+        return static::selectRaw('
+            table_name,
+            COUNT(*) as backup_count,
+            SUM(records_count) as total_records,
+            SUM(content_size) as total_size,
+            AVG(records_count) as avg_records,
+            AVG(content_size) as avg_size
+        ')
+        ->groupBy('table_name')
+        ->orderBy('total_size', 'desc')
+        ->get()
+        ->toArray();
+    }
+}

+ 21 - 5
app/Module/Cleanup/README.md

@@ -408,13 +408,27 @@ PlanService::configureTable(1, 'kku_cache', [
 ```php
 use App\Module\Cleanup\Services\CleanupService;
 
-// 创建备份
+// 创建备份(默认为数据库备份)
 $backupResult = CleanupService::createPlanBackup($planId, [
-    'backup_type' => 'sql',
+    'backup_type' => 1, // 1=数据库备份, 2=SQL文件, 3=JSON文件, 4=CSV文件
     'compression' => 'gzip',
     'include_structure' => true
 ]);
 
+// 获取SQL备份列表
+$sqlBackups = CleanupService::getSqlBackups([
+    'table_name' => 'kku_farm_users',
+    'min_records' => 100,
+    'order_by' => 'created_at',
+    'order_direction' => 'desc'
+]);
+
+// 获取SQL备份详情
+$backupDetail = CleanupService::getSqlBackupDetail($sqlBackupId);
+
+// 获取SQL备份内容
+$backupContent = CleanupService::getSqlBackupContent($sqlBackupId);
+
 // 恢复备份
 $restoreResult = CleanupService::restoreBackup($backupId);
 ```
@@ -444,11 +458,13 @@ $restoreResult = CleanupService::restoreBackup($backupId);
 - 备份文件自动压缩和哈希验证
 
 ### 3. 备份机制
+- **数据库备份**:将INSERT语句直接存储到数据库表中(默认方式)
+- **文件备份**:支持SQL、JSON、CSV格式文件备份
 - **自动备份**:清理前自动备份将要删除的数据
-- **多种格式**:支持SQL、JSON、CSV格式备份
-- **压缩存储**:自动压缩备份文件节省空间
-- **完整性验证**:MD5哈希验证备份文件完整性
+- **压缩存储**:文件备份支持自动压缩节省空间
+- **完整性验证**:SHA256哈希验证备份内容完整性
 - **快速恢复**:支持一键恢复备份数据
+- **查询便捷**:数据库备份支持快速查询和检索
 
 ### 4. 操作建议
 - 首次使用时建议先使用预览模式

+ 121 - 0
app/Module/Cleanup/Services/CleanupService.php

@@ -10,6 +10,7 @@ use App\Module\Cleanup\Logics\BackupLogic;
 use App\Module\Cleanup\Models\CleanupPlan;
 use App\Module\Cleanup\Models\CleanupTask;
 use App\Module\Cleanup\Models\CleanupBackup;
+use App\Module\Cleanup\Models\CleanupSqlBackup;
 
 /**
  * 清理服务类
@@ -217,6 +218,126 @@ class CleanupService
         return BackupLogic::cleanExpiredBackups($retentionDays);
     }
 
+    /**
+     * 获取SQL备份列表
+     *
+     * @param array $filters 筛选条件
+     * @return array SQL备份列表
+     */
+    public static function getSqlBackups(array $filters = []): array
+    {
+        $query = CleanupSqlBackup::with('backup');
+
+        // 按备份ID筛选
+        if (!empty($filters['backup_id'])) {
+            $query->where('backup_id', $filters['backup_id']);
+        }
+
+        // 按表名筛选
+        if (!empty($filters['table_name'])) {
+            $query->where('table_name', 'like', '%' . $filters['table_name'] . '%');
+        }
+
+        // 按记录数量筛选
+        if (!empty($filters['min_records'])) {
+            $query->where('records_count', '>=', $filters['min_records']);
+        }
+
+        // 按内容大小筛选
+        if (!empty($filters['min_size'])) {
+            $query->where('content_size', '>=', $filters['min_size']);
+        }
+
+        // 排序
+        $orderBy = $filters['order_by'] ?? 'created_at';
+        $orderDirection = $filters['order_direction'] ?? 'desc';
+        $query->orderBy($orderBy, $orderDirection);
+
+        // 分页
+        $perPage = $filters['per_page'] ?? 20;
+        $result = $query->paginate($perPage);
+
+        return [
+            'success' => true,
+            'data' => $result->items(),
+            'pagination' => [
+                'current_page' => $result->currentPage(),
+                'per_page' => $result->perPage(),
+                'total' => $result->total(),
+                'last_page' => $result->lastPage(),
+            ]
+        ];
+    }
+
+    /**
+     * 获取SQL备份详情
+     *
+     * @param int $sqlBackupId SQL备份ID
+     * @return array 备份详情
+     */
+    public static function getSqlBackupDetail(int $sqlBackupId): array
+    {
+        try {
+            $sqlBackup = CleanupSqlBackup::with('backup')->findOrFail($sqlBackupId);
+
+            return [
+                'success' => true,
+                'data' => [
+                    'id' => $sqlBackup->id,
+                    'backup_id' => $sqlBackup->backup_id,
+                    'backup_name' => $sqlBackup->backup->backup_name ?? '',
+                    'table_name' => $sqlBackup->table_name,
+                    'records_count' => $sqlBackup->records_count,
+                    'content_size' => $sqlBackup->content_size,
+                    'formatted_size' => $sqlBackup->formatted_size,
+                    'content_hash' => $sqlBackup->content_hash,
+                    'sql_preview' => $sqlBackup->sql_preview,
+                    'backup_conditions' => $sqlBackup->backup_conditions,
+                    'created_at' => $sqlBackup->created_at,
+                    'updated_at' => $sqlBackup->updated_at,
+                ]
+            ];
+
+        } catch (\Exception $e) {
+            return [
+                'success' => false,
+                'message' => '获取SQL备份详情失败: ' . $e->getMessage(),
+                'data' => null
+            ];
+        }
+    }
+
+    /**
+     * 获取SQL备份内容
+     *
+     * @param int $sqlBackupId SQL备份ID
+     * @return array 备份内容
+     */
+    public static function getSqlBackupContent(int $sqlBackupId): array
+    {
+        try {
+            $sqlBackup = CleanupSqlBackup::findOrFail($sqlBackupId);
+
+            return [
+                'success' => true,
+                'data' => [
+                    'id' => $sqlBackup->id,
+                    'table_name' => $sqlBackup->table_name,
+                    'sql_content' => $sqlBackup->sql_content,
+                    'records_count' => $sqlBackup->records_count,
+                    'content_size' => $sqlBackup->content_size,
+                ]
+            ];
+
+        } catch (\Exception $e) {
+            return [
+                'success' => false,
+                'message' => '获取SQL备份内容失败: ' . $e->getMessage(),
+                'data' => null
+            ];
+        }
+    }
+
     /**
      * 清理历史日志
      *

+ 1 - 1
app/Module/Cleanup/config/cleanup.php

@@ -48,7 +48,7 @@ return [
     |--------------------------------------------------------------------------
     */
     'backup' => [
-        // 默认备份类型:1=SQL, 2=JSON, 3=CSV
+        // 默认备份类型:1=数据库, 2=SQL文件, 3=JSON文件, 4=CSV文件
         'default_type' => env('CLEANUP_BACKUP_TYPE', 1),
         
         // 默认压缩类型:1=无压缩, 2=GZIP, 3=ZIP

+ 21 - 0
app/Module/Cleanup/database/cleanup_sql_backups.sql

@@ -0,0 +1,21 @@
+-- Cleanup模块SQL备份记录表
+-- 用于存储INSERT语句到数据库中
+
+CREATE TABLE `kku_cleanup_sql_backups` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `backup_id` bigint(20) unsigned NOT NULL COMMENT '关联备份记录ID',
+  `table_name` varchar(100) NOT NULL COMMENT '表名',
+  `sql_content` longtext NOT NULL COMMENT 'INSERT语句内容',
+  `records_count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '记录数量',
+  `content_size` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '内容大小(字节)',
+  `content_hash` varchar(64) DEFAULT NULL COMMENT '内容SHA256哈希',
+  `backup_conditions` json DEFAULT NULL COMMENT '备份条件',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_backup_id` (`backup_id`),
+  KEY `idx_table_name` (`table_name`),
+  KEY `idx_records_count` (`records_count`),
+  KEY `idx_created_at` (`created_at`),
+  FOREIGN KEY (`backup_id`) REFERENCES `kku_cleanup_backups` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='SQL备份记录表';

+ 90 - 0
app/Module/Cleanup/database/test_database_backup.sql

@@ -0,0 +1,90 @@
+-- 测试数据库备份功能的SQL语句
+
+-- 1. 创建SQL备份记录表
+CREATE TABLE IF NOT EXISTS `kku_cleanup_sql_backups` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `backup_id` bigint(20) unsigned NOT NULL COMMENT '关联备份记录ID',
+  `table_name` varchar(100) NOT NULL COMMENT '表名',
+  `sql_content` longtext NOT NULL COMMENT 'INSERT语句内容',
+  `records_count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '记录数量',
+  `content_size` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '内容大小(字节)',
+  `content_hash` varchar(64) DEFAULT NULL COMMENT '内容SHA256哈希',
+  `backup_conditions` json DEFAULT NULL COMMENT '备份条件',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_backup_id` (`backup_id`),
+  KEY `idx_table_name` (`table_name`),
+  KEY `idx_records_count` (`records_count`),
+  KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='SQL备份记录表';
+
+-- 2. 测试数据库备份功能
+-- 假设我们有一个测试表和一些测试数据
+
+-- 创建测试表
+CREATE TABLE IF NOT EXISTS `kku_test_backup_demo` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(50) NOT NULL,
+  `email` varchar(100) DEFAULT NULL,
+  `status` tinyint(1) DEFAULT '1',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='备份功能测试表';
+
+-- 插入测试数据
+INSERT INTO `kku_test_backup_demo` (`name`, `email`, `status`) VALUES
+('张三', 'zhangsan@example.com', 1),
+('李四', 'lisi@example.com', 1),
+('王五', 'wangwu@example.com', 0),
+('赵六', 'zhaoliu@example.com', 1);
+
+-- 3. 查询SQL备份记录的示例
+-- 查看所有SQL备份记录
+SELECT 
+    sb.id,
+    sb.backup_id,
+    sb.table_name,
+    sb.records_count,
+    sb.content_size,
+    ROUND(sb.content_size / 1024, 2) as size_kb,
+    sb.created_at,
+    b.backup_name,
+    b.backup_type
+FROM kku_cleanup_sql_backups sb
+LEFT JOIN kku_cleanup_backups b ON sb.backup_id = b.id
+ORDER BY sb.created_at DESC;
+
+-- 查看特定表的SQL备份
+SELECT 
+    sb.*,
+    LEFT(sb.sql_content, 200) as sql_preview
+FROM kku_cleanup_sql_backups sb
+WHERE sb.table_name = 'kku_test_backup_demo'
+ORDER BY sb.created_at DESC;
+
+-- 统计SQL备份信息
+SELECT 
+    COUNT(*) as total_backups,
+    COUNT(DISTINCT table_name) as unique_tables,
+    SUM(records_count) as total_records,
+    SUM(content_size) as total_size_bytes,
+    ROUND(SUM(content_size) / 1024 / 1024, 2) as total_size_mb,
+    AVG(records_count) as avg_records_per_backup,
+    AVG(content_size) as avg_size_per_backup
+FROM kku_cleanup_sql_backups;
+
+-- 按表名分组的统计
+SELECT 
+    table_name,
+    COUNT(*) as backup_count,
+    SUM(records_count) as total_records,
+    SUM(content_size) as total_size,
+    ROUND(SUM(content_size) / 1024, 2) as size_kb,
+    MAX(created_at) as latest_backup
+FROM kku_cleanup_sql_backups
+GROUP BY table_name
+ORDER BY total_size DESC;
+
+-- 4. 清理测试数据(可选)
+-- DROP TABLE IF EXISTS `kku_test_backup_demo`;