CleanupTaskLogic.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <?php
  2. namespace App\Module\Cleanup\Logics;
  3. use App\Module\Cleanup\Models\CleanupTask;
  4. use App\Module\Cleanup\Models\CleanupPlan;
  5. use App\Module\Cleanup\Models\CleanupBackup;
  6. use App\Module\Cleanup\Enums\TASK_STATUS;
  7. use Illuminate\Support\Facades\Log;
  8. /**
  9. * 清理任务管理逻辑类
  10. *
  11. * 负责清理任务的创建、管理和状态控制
  12. */
  13. class CleanupTaskLogic
  14. {
  15. /**
  16. * 基于计划创建清理任务
  17. *
  18. * @param int $planId 计划ID
  19. * @param array $taskOptions 任务选项
  20. * @return array 创建结果
  21. */
  22. public static function createTask(int $planId, array $taskOptions = []): array
  23. {
  24. // 检查事务是否已开启
  25. \UCore\Db\Helper::check_tr();
  26. try {
  27. $plan = CleanupPlan::with('contents')->findOrFail($planId);
  28. if (!$plan->is_enabled) {
  29. throw new \Exception('计划已禁用,无法创建任务');
  30. }
  31. if ($plan->contents->isEmpty()) {
  32. throw new \Exception('计划没有配置内容,无法创建任务');
  33. }
  34. // 验证任务选项
  35. $validatedOptions = static::validateTaskOptions($taskOptions);
  36. // 统计任务信息
  37. $enabledContents = $plan->contents->where('is_enabled', true);
  38. $totalTables = $enabledContents->count();
  39. if ($totalTables === 0) {
  40. throw new \Exception('计划中没有启用的表配置,无法创建任务');
  41. }
  42. // 创建任务
  43. $task = CleanupTask::create([
  44. 'task_name' => $validatedOptions['task_name'] ?? "清理任务 - {$plan->plan_name}",
  45. 'plan_id' => $planId,
  46. 'status' => TASK_STATUS::PENDING->value,
  47. 'progress' => 0,
  48. 'current_step' => '准备中',
  49. 'total_tables' => $totalTables,
  50. 'processed_tables' => 0,
  51. 'total_records' => 0,
  52. 'deleted_records' => 0,
  53. 'backup_size' => 0,
  54. 'execution_time' => 0,
  55. 'backup_time' => 0,
  56. 'created_by' => $validatedOptions['created_by'] ?? 0,
  57. ]);
  58. return [
  59. 'success' => true,
  60. 'message' => '清理任务创建成功',
  61. 'data' => [
  62. 'task_id' => $task->id,
  63. 'task_name' => $task->task_name,
  64. 'plan_name' => $plan->plan_name,
  65. 'total_tables' => $totalTables,
  66. 'status' => TASK_STATUS::from($task->status)->getDescription(),
  67. ]
  68. ];
  69. } catch (\Exception $e) {
  70. Log::error('创建清理任务失败', [
  71. 'plan_id' => $planId,
  72. 'task_options' => $taskOptions,
  73. 'error' => $e->getMessage(),
  74. 'trace' => $e->getTraceAsString()
  75. ]);
  76. // 重新抛出异常,不隐藏错误
  77. throw $e;
  78. }
  79. }
  80. /**
  81. * 更新任务状态
  82. *
  83. * @param int $taskId 任务ID
  84. * @param TASK_STATUS $status 新状态
  85. * @param array $updateData 更新数据
  86. * @return array 更新结果
  87. */
  88. public static function updateTaskStatus(int $taskId, TASK_STATUS $status, array $updateData = []): array
  89. {
  90. try {
  91. $task = CleanupTask::findOrFail($taskId);
  92. $data = array_merge($updateData, [
  93. 'status' => $status->value,
  94. ]);
  95. // 根据状态设置时间戳
  96. switch ($status) {
  97. case TASK_STATUS::RUNNING:
  98. $data['started_at'] = now();
  99. break;
  100. case TASK_STATUS::BACKING_UP:
  101. $data['started_at'] = $data['started_at'] ?? now();
  102. break;
  103. case TASK_STATUS::COMPLETED:
  104. $data['completed_at'] = now();
  105. $data['progress'] = 100;
  106. break;
  107. case TASK_STATUS::FAILED:
  108. case TASK_STATUS::CANCELLED:
  109. $data['completed_at'] = now();
  110. break;
  111. }
  112. $task->update($data);
  113. return [
  114. 'success' => true,
  115. 'message' => '任务状态更新成功',
  116. 'data' => [
  117. 'task_id' => $task->id,
  118. 'status' => $status->getDescription(),
  119. 'progress' => $task->progress,
  120. ]
  121. ];
  122. } catch (\Exception $e) {
  123. Log::error('更新任务状态失败', [
  124. 'task_id' => $taskId,
  125. 'status' => $status->value,
  126. 'update_data' => $updateData,
  127. 'error' => $e->getMessage()
  128. ]);
  129. // 重新抛出异常,不隐藏错误
  130. throw $e;
  131. }
  132. }
  133. /**
  134. * 更新任务进度
  135. *
  136. * @param int $taskId 任务ID
  137. * @param int $processedTables 已处理表数
  138. * @param int $deletedRecords 已删除记录数
  139. * @param string $currentStep 当前步骤
  140. * @return array 更新结果
  141. */
  142. public static function updateTaskProgress(int $taskId, int $processedTables, int $deletedRecords, string $currentStep): array
  143. {
  144. try {
  145. $task = CleanupTask::findOrFail($taskId);
  146. $progress = $task->total_tables > 0 ? round(($processedTables / $task->total_tables) * 100, 2) : 0;
  147. $task->update([
  148. 'progress' => $progress,
  149. 'processed_tables' => $processedTables,
  150. 'deleted_records' => $deletedRecords,
  151. 'current_step' => $currentStep,
  152. ]);
  153. return [
  154. 'success' => true,
  155. 'data' => [
  156. 'task_id' => $task->id,
  157. 'progress' => $progress,
  158. 'processed_tables' => $processedTables,
  159. 'total_tables' => $task->total_tables,
  160. 'deleted_records' => $deletedRecords,
  161. 'current_step' => $currentStep,
  162. ]
  163. ];
  164. } catch (\Exception $e) {
  165. Log::error('更新任务进度失败', [
  166. 'task_id' => $taskId,
  167. 'processed_tables' => $processedTables,
  168. 'deleted_records' => $deletedRecords,
  169. 'current_step' => $currentStep,
  170. 'error' => $e->getMessage()
  171. ]);
  172. // 重新抛出异常,不隐藏错误
  173. throw $e;
  174. }
  175. }
  176. /**
  177. * 取消任务
  178. *
  179. * @param int $taskId 任务ID
  180. * @param string $reason 取消原因
  181. * @return array 取消结果
  182. */
  183. public static function cancelTask(int $taskId, string $reason = ''): array
  184. {
  185. try {
  186. $task = CleanupTask::findOrFail($taskId);
  187. // 检查任务状态
  188. $currentStatus = TASK_STATUS::from($task->status);
  189. if (in_array($currentStatus, [TASK_STATUS::COMPLETED, TASK_STATUS::FAILED, TASK_STATUS::CANCELLED])) {
  190. throw new \Exception('任务已完成或已取消,无法再次取消');
  191. }
  192. $task->update([
  193. 'status' => TASK_STATUS::CANCELLED->value,
  194. 'completed_at' => now(),
  195. 'error_message' => $reason ?: '用户取消',
  196. ]);
  197. return [
  198. 'success' => true,
  199. 'message' => '任务已取消',
  200. 'data' => [
  201. 'task_id' => $task->id,
  202. 'status' => TASK_STATUS::CANCELLED->getDescription(),
  203. ]
  204. ];
  205. } catch (\Exception $e) {
  206. Log::error('取消任务失败', [
  207. 'task_id' => $taskId,
  208. 'reason' => $reason,
  209. 'error' => $e->getMessage()
  210. ]);
  211. // 重新抛出异常,不隐藏错误
  212. throw $e;
  213. }
  214. }
  215. /**
  216. * 获取任务详情
  217. *
  218. * @param int $taskId 任务ID
  219. * @return array 任务详情
  220. */
  221. public static function getTaskDetails(int $taskId): array
  222. {
  223. try {
  224. $task = CleanupTask::with(['plan', 'backup'])->findOrFail($taskId);
  225. return [
  226. 'success' => true,
  227. 'data' => [
  228. 'task' => [
  229. 'id' => $task->id,
  230. 'task_name' => $task->task_name,
  231. 'status' => $task->status,
  232. 'status_name' => TASK_STATUS::from($task->status)->getDescription(),
  233. 'progress' => $task->progress,
  234. 'current_step' => $task->current_step,
  235. 'total_tables' => $task->total_tables,
  236. 'processed_tables' => $task->processed_tables,
  237. 'total_records' => $task->total_records,
  238. 'deleted_records' => $task->deleted_records,
  239. 'backup_size' => $task->backup_size,
  240. 'execution_time' => $task->execution_time,
  241. 'backup_time' => $task->backup_time,
  242. 'started_at' => $task->started_at,
  243. 'backup_completed_at' => $task->backup_completed_at,
  244. 'completed_at' => $task->completed_at,
  245. 'error_message' => $task->error_message,
  246. 'created_at' => $task->created_at,
  247. ],
  248. 'plan' => $task->plan ? [
  249. 'id' => $task->plan->id,
  250. 'plan_name' => $task->plan->plan_name,
  251. 'plan_type' => $task->plan->plan_type,
  252. 'description' => $task->plan->description,
  253. ] : null,
  254. 'backup' => $task->backup ? [
  255. 'id' => $task->backup->id,
  256. 'backup_name' => $task->backup->backup_name,
  257. 'backup_size' => $task->backup->backup_size,
  258. 'file_count' => $task->backup->file_count,
  259. 'status' => $task->backup->status,
  260. ] : null,
  261. ]
  262. ];
  263. } catch (\Exception $e) {
  264. Log::error('获取任务详情失败', [
  265. 'task_id' => $taskId,
  266. 'error' => $e->getMessage()
  267. ]);
  268. // 重新抛出异常,不隐藏错误
  269. throw $e;
  270. }
  271. }
  272. /**
  273. * 验证任务选项
  274. *
  275. * @param array $taskOptions 任务选项
  276. * @return array 验证后的选项
  277. */
  278. private static function validateTaskOptions(array $taskOptions): array
  279. {
  280. return [
  281. 'task_name' => $taskOptions['task_name'] ?? null,
  282. 'created_by' => $taskOptions['created_by'] ?? 0,
  283. ];
  284. }
  285. /**
  286. * 启动任务执行
  287. *
  288. * @param int $taskId 任务ID
  289. * @return array 启动结果
  290. */
  291. public static function startTask(int $taskId): array
  292. {
  293. try {
  294. $task = CleanupTask::findOrFail($taskId);
  295. // 检查任务状态
  296. if ($task->status !== TASK_STATUS::PENDING->value) {
  297. return [
  298. 'success' => false,
  299. 'message' => '只有待执行状态的任务可以启动'
  300. ];
  301. }
  302. // 更新任务状态为执行中
  303. $task->update([
  304. 'status' => TASK_STATUS::RUNNING->value,
  305. 'started_at' => now(),
  306. 'current_step' => '任务启动中...'
  307. ]);
  308. return [
  309. 'success' => true,
  310. 'message' => '任务启动成功',
  311. 'data' => $task->fresh()
  312. ];
  313. } catch (\Exception $e) {
  314. // 重新抛出异常,不隐藏错误
  315. throw $e;
  316. }
  317. }
  318. /**
  319. * 暂停任务执行
  320. *
  321. * @param int $taskId 任务ID
  322. * @return array 暂停结果
  323. */
  324. public static function pauseTask(int $taskId): array
  325. {
  326. try {
  327. $task = CleanupTask::findOrFail($taskId);
  328. // 检查任务状态
  329. if (!in_array($task->status, [TASK_STATUS::BACKING_UP->value, TASK_STATUS::RUNNING->value])) {
  330. return [
  331. 'success' => false,
  332. 'message' => '只有备份中或执行中的任务可以暂停'
  333. ];
  334. }
  335. // 更新任务状态为已暂停
  336. $task->update([
  337. 'status' => TASK_STATUS::PAUSED->value,
  338. 'current_step' => '任务已暂停'
  339. ]);
  340. return [
  341. 'success' => true,
  342. 'message' => '任务暂停成功',
  343. 'data' => $task->fresh()
  344. ];
  345. } catch (\Exception $e) {
  346. // 重新抛出异常,不隐藏错误
  347. throw $e;
  348. }
  349. }
  350. /**
  351. * 恢复任务执行
  352. *
  353. * @param int $taskId 任务ID
  354. * @return array 恢复结果
  355. */
  356. public static function resumeTask(int $taskId): array
  357. {
  358. try {
  359. $task = CleanupTask::findOrFail($taskId);
  360. // 检查任务状态
  361. if ($task->status !== TASK_STATUS::PAUSED->value) {
  362. return [
  363. 'success' => false,
  364. 'message' => '只有已暂停的任务可以恢复'
  365. ];
  366. }
  367. // 更新任务状态为执行中
  368. $task->update([
  369. 'status' => TASK_STATUS::RUNNING->value,
  370. 'current_step' => '任务恢复执行中...'
  371. ]);
  372. return [
  373. 'success' => true,
  374. 'message' => '任务恢复成功',
  375. 'data' => $task->fresh()
  376. ];
  377. } catch (\Exception $e) {
  378. // 重新抛出异常,不隐藏错误
  379. throw $e;
  380. }
  381. }
  382. /**
  383. * 停止任务执行
  384. *
  385. * @param int $taskId 任务ID
  386. * @return array 停止结果
  387. */
  388. public static function stopTask(int $taskId): array
  389. {
  390. try {
  391. $task = CleanupTask::findOrFail($taskId);
  392. // 检查任务状态
  393. if (!in_array($task->status, [TASK_STATUS::BACKING_UP->value, TASK_STATUS::RUNNING->value, TASK_STATUS::PAUSED->value])) {
  394. return [
  395. 'success' => false,
  396. 'message' => '只有备份中、执行中或已暂停的任务可以停止'
  397. ];
  398. }
  399. // 更新任务状态为已取消
  400. $task->update([
  401. 'status' => TASK_STATUS::CANCELLED->value,
  402. 'finished_at' => now(),
  403. 'current_step' => '任务已停止',
  404. 'error_message' => '任务被手动停止'
  405. ]);
  406. return [
  407. 'success' => true,
  408. 'message' => '任务停止成功',
  409. 'data' => $task->fresh()
  410. ];
  411. } catch (\Exception $e) {
  412. // 重新抛出异常,不隐藏错误
  413. throw $e;
  414. }
  415. }
  416. /**
  417. * 获取任务进度
  418. *
  419. * @param int $taskId 任务ID
  420. * @return array 进度信息
  421. */
  422. public static function getTaskProgress(int $taskId): array
  423. {
  424. try {
  425. $task = CleanupTask::findOrFail($taskId);
  426. return [
  427. 'success' => true,
  428. 'data' => [
  429. 'task_id' => $task->id,
  430. 'status' => $task->status,
  431. 'progress' => $task->progress,
  432. 'current_step' => $task->current_step,
  433. 'total_tables' => $task->total_tables,
  434. 'processed_tables' => $task->processed_tables,
  435. 'deleted_records' => $task->deleted_records,
  436. 'started_at' => $task->started_at,
  437. 'finished_at' => $task->finished_at,
  438. ]
  439. ];
  440. } catch (\Exception $e) {
  441. // 重新抛出异常,不隐藏错误
  442. throw $e;
  443. }
  444. }
  445. }