CleanupService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. <?php
  2. namespace App\Module\Cleanup\Services;
  3. use App\Module\Cleanup\Logics\ModelScannerLogic;
  4. use App\Module\Cleanup\Logics\CleanupPlanLogic;
  5. use App\Module\Cleanup\Logics\CleanupTaskLogic;
  6. use App\Module\Cleanup\Logics\CleanupExecutorLogic;
  7. use App\Module\Cleanup\Logics\BackupLogic;
  8. use App\Module\Cleanup\Models\CleanupPlan;
  9. use App\Module\Cleanup\Models\CleanupTask;
  10. use App\Module\Cleanup\Models\CleanupBackup;
  11. use App\Module\Cleanup\Models\CleanupSqlBackup;
  12. /**
  13. * 清理服务类
  14. *
  15. * 提供对外的清理服务接口
  16. */
  17. class CleanupService
  18. {
  19. /**
  20. * 扫描Model类(推荐使用)
  21. *
  22. * @param bool $forceRefresh 是否强制刷新
  23. * @return array 扫描结果
  24. */
  25. public static function scanModels(bool $forceRefresh = false): array
  26. {
  27. return ModelScannerLogic::scanAllModels($forceRefresh);
  28. }
  29. /**
  30. * 扫描系统中的所有数据表(已废弃,建议使用scanModels)
  31. *
  32. * @deprecated 请使用 scanModels() 方法
  33. * @param bool $forceRefresh 是否强制刷新
  34. * @return array 扫描结果
  35. */
  36. public static function scanTables(bool $forceRefresh = false): array
  37. {
  38. // 为了向后兼容,重定向到新的Model扫描方法
  39. return static::scanModels($forceRefresh);
  40. }
  41. /**
  42. * 创建清理计划
  43. *
  44. * @param array $planData 计划数据
  45. * @return array 创建结果
  46. */
  47. public static function createCleanupPlan(array $planData): array
  48. {
  49. return CleanupPlanLogic::createPlan($planData);
  50. }
  51. /**
  52. * 为计划生成内容配置
  53. *
  54. * @param int $planId 计划ID
  55. * @param bool $autoGenerate 是否自动生成
  56. * @return array 生成结果
  57. */
  58. public static function generatePlanContents(int $planId, bool $autoGenerate = true): array
  59. {
  60. return CleanupPlanLogic::generateContents($planId, $autoGenerate);
  61. }
  62. /**
  63. * 基于计划创建清理任务
  64. *
  65. * @param int $planId 计划ID
  66. * @param array $taskOptions 任务选项
  67. * @return array 创建结果
  68. */
  69. public static function createCleanupTask(int $planId, array $taskOptions = []): array
  70. {
  71. return CleanupTaskLogic::createTask($planId, $taskOptions);
  72. }
  73. /**
  74. * 预览计划的清理结果
  75. *
  76. * @param int $planId 计划ID
  77. * @return array 预览结果
  78. */
  79. public static function previewPlanCleanup(int $planId): array
  80. {
  81. return CleanupExecutorLogic::previewPlanCleanup($planId);
  82. }
  83. /**
  84. * 预览任务的清理结果
  85. *
  86. * @param int $taskId 任务ID
  87. * @return array 预览结果
  88. */
  89. public static function previewTaskCleanup(int $taskId): array
  90. {
  91. return CleanupExecutorLogic::previewTaskCleanup($taskId);
  92. }
  93. /**
  94. * 执行清理任务
  95. *
  96. * @param int $taskId 任务ID
  97. * @param bool $dryRun 是否为预演模式
  98. * @return array 执行结果
  99. */
  100. public static function executeCleanupTask(int $taskId, bool $dryRun = false): array
  101. {
  102. return CleanupExecutorLogic::executeTask($taskId, $dryRun);
  103. }
  104. /**
  105. * 为计划创建数据备份
  106. *
  107. * @param int $planId 计划ID
  108. * @param array $backupOptions 备份选项
  109. * @return array 备份结果
  110. */
  111. public static function createPlanBackup(int $planId, array $backupOptions = []): array
  112. {
  113. return BackupLogic::createPlanBackup($planId, $backupOptions);
  114. }
  115. /**
  116. * 为任务创建数据备份
  117. *
  118. * @param int $taskId 任务ID
  119. * @param array $backupOptions 备份选项
  120. * @return array 备份结果
  121. */
  122. public static function createTaskBackup(int $taskId, array $backupOptions = []): array
  123. {
  124. return BackupLogic::createTaskBackup($taskId, $backupOptions);
  125. }
  126. /**
  127. * 恢复数据备份
  128. *
  129. * @param int $backupId 备份ID
  130. * @param array $restoreOptions 恢复选项
  131. * @return array 恢复结果
  132. */
  133. public static function restoreBackup(int $backupId, array $restoreOptions = []): array
  134. {
  135. return BackupLogic::restoreBackup($backupId, $restoreOptions);
  136. }
  137. /**
  138. * 验证备份完整性
  139. *
  140. * @param int $backupId 备份ID
  141. * @return array 验证结果
  142. */
  143. public static function verifyBackup(int $backupId): array
  144. {
  145. return BackupLogic::verifyBackup($backupId);
  146. }
  147. /**
  148. * 获取任务执行进度
  149. *
  150. * @param int $taskId 任务ID
  151. * @return array 进度信息
  152. */
  153. public static function getTaskProgress(int $taskId): array
  154. {
  155. try {
  156. return CleanupTaskLogic::getTaskProgress($taskId);
  157. } catch (\Exception $e) {
  158. return [
  159. 'success' => false,
  160. 'message' => '获取任务进度失败:' . $e->getMessage()
  161. ];
  162. }
  163. }
  164. /**
  165. * 停止任务执行
  166. *
  167. * @param int $taskId 任务ID
  168. * @return array 停止结果
  169. */
  170. public static function stopTask(int $taskId): array
  171. {
  172. try {
  173. return CleanupTaskLogic::stopTask($taskId);
  174. } catch (\Exception $e) {
  175. return [
  176. 'success' => false,
  177. 'message' => '停止任务失败:' . $e->getMessage()
  178. ];
  179. }
  180. }
  181. /**
  182. * 暂停任务执行
  183. *
  184. * @param int $taskId 任务ID
  185. * @return array 暂停结果
  186. */
  187. public static function pauseTask(int $taskId): array
  188. {
  189. try {
  190. return CleanupTaskLogic::pauseTask($taskId);
  191. } catch (\Exception $e) {
  192. return [
  193. 'success' => false,
  194. 'message' => '暂停任务失败:' . $e->getMessage()
  195. ];
  196. }
  197. }
  198. /**
  199. * 恢复任务执行
  200. *
  201. * @param int $taskId 任务ID
  202. * @return array 恢复结果
  203. */
  204. public static function resumeTask(int $taskId): array
  205. {
  206. try {
  207. return CleanupTaskLogic::resumeTask($taskId);
  208. } catch (\Exception $e) {
  209. return [
  210. 'success' => false,
  211. 'message' => '恢复任务失败:' . $e->getMessage()
  212. ];
  213. }
  214. }
  215. /**
  216. * 启动任务执行
  217. *
  218. * @param int $taskId 任务ID
  219. * @return array 启动结果
  220. */
  221. public static function startTask(int $taskId): array
  222. {
  223. try {
  224. return CleanupTaskLogic::startTask($taskId);
  225. } catch (\Exception $e) {
  226. return [
  227. 'success' => false,
  228. 'message' => '启动任务失败:' . $e->getMessage()
  229. ];
  230. }
  231. }
  232. /**
  233. * 取消任务执行
  234. *
  235. * @param int $taskId 任务ID
  236. * @param string $reason 取消原因
  237. * @return array 取消结果
  238. */
  239. public static function cancelTask(int $taskId, string $reason = ''): array
  240. {
  241. try {
  242. return CleanupTaskLogic::cancelTask($taskId, $reason);
  243. } catch (\Exception $e) {
  244. return [
  245. 'success' => false,
  246. 'message' => '取消任务失败:' . $e->getMessage()
  247. ];
  248. }
  249. }
  250. /**
  251. * 获取清理统计信息
  252. *
  253. * @param array $filters 筛选条件
  254. * @return array 统计信息
  255. */
  256. public static function getCleanupStats(array $filters = []): array
  257. {
  258. return [
  259. 'plans' => CleanupPlan::getEnabledStats(),
  260. 'tasks' => CleanupTask::getStatusStats(),
  261. 'backups' => CleanupBackup::getStatusStats(),
  262. 'recent_tasks' => CleanupTask::getRecentStats(7),
  263. 'storage' => CleanupBackup::getStorageStats(),
  264. ];
  265. }
  266. /**
  267. * 清理过期备份
  268. *
  269. * @param int $retentionDays 保留天数
  270. * @return array 清理结果
  271. */
  272. public static function cleanExpiredBackups(int $retentionDays = 30): array
  273. {
  274. return BackupLogic::cleanExpiredBackups($retentionDays);
  275. }
  276. /**
  277. * 获取SQL备份列表
  278. *
  279. * @param array $filters 筛选条件
  280. * @return array SQL备份列表
  281. */
  282. public static function getSqlBackups(array $filters = []): array
  283. {
  284. $query = CleanupSqlBackup::with('backup');
  285. // 按备份ID筛选
  286. if (!empty($filters['backup_id'])) {
  287. $query->where('backup_id', $filters['backup_id']);
  288. }
  289. // 按表名筛选
  290. if (!empty($filters['table_name'])) {
  291. $query->where('table_name', 'like', '%' . $filters['table_name'] . '%');
  292. }
  293. // 按记录数量筛选
  294. if (!empty($filters['min_records'])) {
  295. $query->where('records_count', '>=', $filters['min_records']);
  296. }
  297. // 按内容大小筛选
  298. if (!empty($filters['min_size'])) {
  299. $query->where('content_size', '>=', $filters['min_size']);
  300. }
  301. // 排序
  302. $orderBy = $filters['order_by'] ?? 'created_at';
  303. $orderDirection = $filters['order_direction'] ?? 'desc';
  304. $query->orderBy($orderBy, $orderDirection);
  305. // 分页
  306. $perPage = $filters['per_page'] ?? 20;
  307. $result = $query->paginate($perPage);
  308. return [
  309. 'success' => true,
  310. 'data' => $result->items(),
  311. 'pagination' => [
  312. 'current_page' => $result->currentPage(),
  313. 'per_page' => $result->perPage(),
  314. 'total' => $result->total(),
  315. 'last_page' => $result->lastPage(),
  316. ]
  317. ];
  318. }
  319. /**
  320. * 获取SQL备份详情
  321. *
  322. * @param int $sqlBackupId SQL备份ID
  323. * @return array 备份详情
  324. */
  325. public static function getSqlBackupDetail(int $sqlBackupId): array
  326. {
  327. try {
  328. $sqlBackup = CleanupSqlBackup::with('backup')->findOrFail($sqlBackupId);
  329. return [
  330. 'success' => true,
  331. 'data' => [
  332. 'id' => $sqlBackup->id,
  333. 'backup_id' => $sqlBackup->backup_id,
  334. 'backup_name' => $sqlBackup->backup->backup_name ?? '',
  335. 'table_name' => $sqlBackup->table_name,
  336. 'records_count' => $sqlBackup->records_count,
  337. 'content_size' => $sqlBackup->content_size,
  338. 'formatted_size' => $sqlBackup->formatted_size,
  339. 'content_hash' => $sqlBackup->content_hash,
  340. 'sql_preview' => $sqlBackup->sql_preview,
  341. 'backup_conditions' => $sqlBackup->backup_conditions,
  342. 'created_at' => $sqlBackup->created_at,
  343. 'updated_at' => $sqlBackup->updated_at,
  344. ]
  345. ];
  346. } catch (\Exception $e) {
  347. return [
  348. 'success' => false,
  349. 'message' => '获取SQL备份详情失败: ' . $e->getMessage(),
  350. 'data' => null
  351. ];
  352. }
  353. }
  354. /**
  355. * 获取SQL备份内容
  356. *
  357. * @param int $sqlBackupId SQL备份ID
  358. * @return array 备份内容
  359. */
  360. public static function getSqlBackupContent(int $sqlBackupId): array
  361. {
  362. try {
  363. $sqlBackup = CleanupSqlBackup::findOrFail($sqlBackupId);
  364. return [
  365. 'success' => true,
  366. 'data' => [
  367. 'id' => $sqlBackup->id,
  368. 'table_name' => $sqlBackup->table_name,
  369. 'sql_content' => $sqlBackup->sql_content,
  370. 'records_count' => $sqlBackup->records_count,
  371. 'content_size' => $sqlBackup->content_size,
  372. ]
  373. ];
  374. } catch (\Exception $e) {
  375. return [
  376. 'success' => false,
  377. 'message' => '获取SQL备份内容失败: ' . $e->getMessage(),
  378. 'data' => null
  379. ];
  380. }
  381. }
  382. /**
  383. * 清理历史日志
  384. *
  385. * @param int $retentionDays 保留天数
  386. * @return array 清理结果
  387. */
  388. public static function cleanHistoryLogs(int $retentionDays = 30): array
  389. {
  390. return CleanupTaskLogic::cleanHistoryLogs($retentionDays);
  391. }
  392. /**
  393. * 获取系统健康状态
  394. *
  395. * @return array 健康状态
  396. */
  397. public static function getSystemHealth(): array
  398. {
  399. $runningTasks = CleanupTask::running()->count();
  400. $failedTasks = CleanupTask::byStatus(\App\Module\Cleanup\Enums\TASK_STATUS::FAILED->value)
  401. ->where('created_at', '>=', now()->subDay())
  402. ->count();
  403. $expiredBackups = CleanupBackup::expired()->count();
  404. $expiringSoonBackups = CleanupBackup::expiringSoon(3)->count();
  405. $health = 'good';
  406. $issues = [];
  407. if ($runningTasks > 5) {
  408. $health = 'warning';
  409. $issues[] = "有 {$runningTasks} 个任务正在运行,可能存在性能问题";
  410. }
  411. if ($failedTasks > 0) {
  412. $health = 'warning';
  413. $issues[] = "最近24小时内有 {$failedTasks} 个任务执行失败";
  414. }
  415. if ($expiredBackups > 0) {
  416. $health = 'warning';
  417. $issues[] = "有 {$expiredBackups} 个备份已过期,建议清理";
  418. }
  419. if ($expiringSoonBackups > 0) {
  420. $issues[] = "有 {$expiringSoonBackups} 个备份即将过期";
  421. }
  422. if ($failedTasks > 5) {
  423. $health = 'critical';
  424. }
  425. return [
  426. 'health' => $health,
  427. 'issues' => $issues,
  428. 'metrics' => [
  429. 'running_tasks' => $runningTasks,
  430. 'failed_tasks_24h' => $failedTasks,
  431. 'expired_backups' => $expiredBackups,
  432. 'expiring_soon_backups' => $expiringSoonBackups,
  433. ],
  434. ];
  435. }
  436. /**
  437. * 获取推荐的清理计划
  438. *
  439. * @return array 推荐计划
  440. */
  441. public static function getRecommendedPlans(): array
  442. {
  443. $recommendations = [];
  444. // 检查是否有大量日志数据
  445. $logTables = \App\Module\Cleanup\Models\CleanupConfig::byCategory(
  446. \App\Module\Cleanup\Enums\DATA_CATEGORY::LOG_DATA->value
  447. )->get();
  448. if ($logTables->count() > 0) {
  449. $recommendations[] = [
  450. 'type' => 'category',
  451. 'title' => '日志数据清理',
  452. 'description' => '清理系统中的日志数据,释放存储空间',
  453. 'config' => [
  454. 'plan_type' => \App\Module\Cleanup\Enums\PLAN_TYPE::CATEGORY->value,
  455. 'target_selection' => [
  456. 'selection_type' => 'category',
  457. 'categories' => [\App\Module\Cleanup\Enums\DATA_CATEGORY::LOG_DATA->value],
  458. ],
  459. ],
  460. 'estimated_tables' => $logTables->count(),
  461. ];
  462. }
  463. // 检查是否有缓存数据
  464. $cacheTables = \App\Module\Cleanup\Models\CleanupConfig::byCategory(
  465. \App\Module\Cleanup\Enums\DATA_CATEGORY::CACHE_DATA->value
  466. )->get();
  467. if ($cacheTables->count() > 0) {
  468. $recommendations[] = [
  469. 'type' => 'category',
  470. 'title' => '缓存数据清理',
  471. 'description' => '清理系统中的缓存数据,提高系统性能',
  472. 'config' => [
  473. 'plan_type' => \App\Module\Cleanup\Enums\PLAN_TYPE::CATEGORY->value,
  474. 'target_selection' => [
  475. 'selection_type' => 'category',
  476. 'categories' => [\App\Module\Cleanup\Enums\DATA_CATEGORY::CACHE_DATA->value],
  477. ],
  478. ],
  479. 'estimated_tables' => $cacheTables->count(),
  480. ];
  481. }
  482. return $recommendations;
  483. }
  484. }