id, true); if (!$contentResult['success']) { throw new \Exception('生成计划内容失败: ' . $contentResult['message']); } } DB::commit(); return [ 'success' => true, 'message' => '清理计划创建成功', 'data' => [ 'plan_id' => $plan->id, 'plan_name' => $plan->plan_name, 'plan_type' => $plan->plan_type, 'contents_count' => $plan->contents()->count(), ] ]; } catch (\Exception $e) { DB::rollBack(); Log::error('创建清理计划失败', [ 'plan_data' => $planData, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return [ 'success' => false, 'message' => '创建清理计划失败: ' . $e->getMessage(), 'data' => null ]; } } /** * 为计划生成内容配置 * * @param int $planId 计划ID * @param bool $autoGenerate 是否自动生成 * @return array 生成结果 */ public static function generateContents(int $planId, bool $autoGenerate = true): array { try { $plan = CleanupPlan::findOrFail($planId); // 根据计划类型获取目标表 $targetTables = static::getTargetTables($plan); if (empty($targetTables)) { return [ 'success' => false, 'message' => '未找到符合条件的目标表', 'data' => null ]; } $generatedCount = 0; $skippedCount = 0; $errors = []; foreach ($targetTables as $tableName) { try { // 检查是否已存在内容配置 $existingContent = CleanupPlanContent::where('plan_id', $planId) ->where('table_name', $tableName) ->first(); if ($existingContent && !$autoGenerate) { $skippedCount++; continue; } // 获取表的配置信息 $tableConfig = CleanupConfig::where('table_name', $tableName)->first(); // 生成内容配置 $contentData = static::generateTableContent($plan, $tableName, $tableConfig); if ($existingContent) { $existingContent->update($contentData); } else { $contentData['plan_id'] = $planId; $contentData['table_name'] = $tableName; CleanupPlanContent::create($contentData); } $generatedCount++; } catch (\Exception $e) { $errors[] = "表 {$tableName}: " . $e->getMessage(); Log::error("生成表内容配置失败", [ 'plan_id' => $planId, 'table_name' => $tableName, 'error' => $e->getMessage() ]); } } return [ 'success' => true, 'message' => "内容生成完成,生成 {$generatedCount} 个,跳过 {$skippedCount} 个", 'data' => [ 'generated_count' => $generatedCount, 'skipped_count' => $skippedCount, 'total_tables' => count($targetTables), 'errors' => $errors ] ]; } catch (\Exception $e) { Log::error('生成计划内容失败', [ 'plan_id' => $planId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return [ 'success' => false, 'message' => '生成计划内容失败: ' . $e->getMessage(), 'data' => null ]; } } /** * 验证计划数据 * * @param array $planData 计划数据 * @return array 验证后的数据 * @throws \Exception */ private static function validatePlanData(array $planData): array { // 必填字段验证 $required = ['plan_name', 'plan_type']; foreach ($required as $field) { if (empty($planData[$field])) { throw new \Exception("字段 {$field} 不能为空"); } } // 验证计划类型 $planType = PLAN_TYPE::tryFrom($planData['plan_type']); if (!$planType) { throw new \Exception('无效的计划类型'); } // 根据计划类型验证目标选择 if ($planType !== PLAN_TYPE::CUSTOM && empty($planData['target_selection'])) { throw new \Exception('目标选择不能为空'); } return [ 'plan_name' => $planData['plan_name'], 'plan_type' => $planData['plan_type'], 'target_selection' => $planData['target_selection'] ?? [], 'global_conditions' => $planData['global_conditions'] ?? [], 'backup_config' => $planData['backup_config'] ?? [], 'is_template' => $planData['is_template'] ?? false, 'is_enabled' => $planData['is_enabled'] ?? true, 'description' => $planData['description'] ?? '', 'created_by' => $planData['created_by'] ?? 0, ]; } /** * 根据计划获取目标表列表 * * @param CleanupPlan $plan 清理计划 * @return array 目标表列表 */ private static function getTargetTables(CleanupPlan $plan): array { $tables = []; $selectedModels = $plan->selected_tables ?? []; foreach ($selectedModels as $modelClass) { if (class_exists($modelClass) && is_subclass_of($modelClass, \Illuminate\Database\Eloquent\Model::class)) { try { // 通过模型实例获取表名 $model = new $modelClass(); $tableName = $model->getTable(); $tables[] = $tableName; } catch (\Exception $e) { // 如果模型实例化失败,记录错误但继续处理其他模型 \Log::warning("Failed to get table name for model: {$modelClass}", ['error' => $e->getMessage()]); } } } return array_unique($tables); } /** * 为表生成内容配置 * * @param CleanupPlan $plan 清理计划 * @param string $tableName 表名 * @param CleanupConfig|null $tableConfig 表配置 * @return array 内容配置数据 */ private static function generateTableContent(CleanupPlan $plan, string $tableName, ?CleanupConfig $tableConfig): array { // 基础配置 $contentData = [ 'cleanup_type' => $tableConfig?->default_cleanup_type ?? CLEANUP_TYPE::DELETE_ALL->value, 'conditions' => $tableConfig?->default_conditions ?? [], 'priority' => $tableConfig?->priority ?? 100, 'batch_size' => $tableConfig?->batch_size ?? 1000, 'backup_enabled' => true, 'is_enabled' => true, 'notes' => $tableConfig?->description ?? "自动生成的 {$tableName} 表清理配置", ]; // 合并计划的全局条件 if (!empty($plan->global_conditions)) { $contentData['conditions'] = array_merge( $contentData['conditions'], $plan->global_conditions ); } // 合并计划的备份配置 if (!empty($plan->backup_config)) { $contentData['backup_config'] = $plan->backup_config; } return $contentData; } /** * 获取计划详情 * * @param int $planId 计划ID * @return array 计划详情 */ public static function getPlanDetails(int $planId): array { try { $plan = CleanupPlan::with(['contents.config'])->findOrFail($planId); $contents = $plan->contents->map(function ($content) { return [ 'id' => $content->id, 'table_name' => $content->table_name, 'cleanup_type' => $content->cleanup_type, 'cleanup_type_name' => CLEANUP_TYPE::from($content->cleanup_type)->getDescription(), 'conditions' => $content->conditions, 'priority' => $content->priority, 'batch_size' => $content->batch_size, 'backup_enabled' => $content->backup_enabled, 'is_enabled' => $content->is_enabled, 'notes' => $content->notes, 'module_name' => $content->config?->module_name, 'data_category' => $content->config?->data_category, ]; }); return [ 'success' => true, 'data' => [ 'plan' => [ 'id' => $plan->id, 'plan_name' => $plan->plan_name, 'selected_tables' => $plan->selected_tables, 'global_conditions' => $plan->global_conditions, 'backup_config' => $plan->backup_config, 'is_template' => $plan->is_template, 'is_enabled' => $plan->is_enabled, 'description' => $plan->description, 'created_at' => $plan->created_at, 'updated_at' => $plan->updated_at, ], 'contents' => $contents, 'statistics' => [ 'total_tables' => $contents->count(), 'enabled_tables' => $contents->where('is_enabled', true)->count(), 'backup_enabled_tables' => $contents->where('backup_enabled', true)->count(), ] ] ]; } catch (\Exception $e) { Log::error('获取计划详情失败', [ 'plan_id' => $planId, 'error' => $e->getMessage() ]); return [ 'success' => false, 'message' => '获取计划详情失败: ' . $e->getMessage(), 'data' => null ]; } } /** * 更新计划 * * @param int $planId 计划ID * @param array $planData 计划数据 * @return array 更新结果 */ public static function updatePlan(int $planId, array $planData): array { try { $plan = CleanupPlan::findOrFail($planId); // 验证数据 $validatedData = static::validatePlanData($planData); // 更新计划 $plan->update($validatedData); return [ 'success' => true, 'message' => '计划更新成功', 'data' => [ 'plan_id' => $plan->id, 'plan_name' => $plan->plan_name, ] ]; } catch (\Exception $e) { Log::error('更新计划失败', [ 'plan_id' => $planId, 'plan_data' => $planData, 'error' => $e->getMessage() ]); return [ 'success' => false, 'message' => '更新计划失败: ' . $e->getMessage(), 'data' => null ]; } } /** * 删除计划 * * @param int $planId 计划ID * @return array 删除结果 */ public static function deletePlan(int $planId): array { try { DB::beginTransaction(); $plan = CleanupPlan::findOrFail($planId); // 检查是否有关联的任务 if ($plan->tasks()->exists()) { throw new \Exception('该计划存在关联的任务,无法删除'); } // 删除计划内容 $plan->contents()->delete(); // 删除计划 $plan->delete(); DB::commit(); return [ 'success' => true, 'message' => '计划删除成功', 'data' => null ]; } catch (\Exception $e) { DB::rollBack(); Log::error('删除计划失败', [ 'plan_id' => $planId, 'error' => $e->getMessage() ]); return [ 'success' => false, 'message' => '删除计划失败: ' . $e->getMessage(), 'data' => null ]; } } }