$activeSkills->count()]); $processedCount = 0; $expiredCount = 0; $skippedCount = 0; foreach ($activeSkills as $activeSkill) { try { $result = self::processSkill($activeSkill); switch ($result['status']) { case 'processed': $processedCount++; break; case 'expired': $expiredCount++; break; case 'skipped': $skippedCount++; break; } } catch (\Exception $e) { Log::error('处理单个激活技能失败', [ 'active_skill_id' => $activeSkill->id, 'pet_id' => $activeSkill->pet_id, 'skill_name' => $activeSkill->skill_name, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); } } $result = [ 'total_skills' => $activeSkills->count(), 'processed_count' => $processedCount, 'expired_count' => $expiredCount, 'skipped_count' => $skippedCount ]; Log::info('宠物激活技能定时任务完成', $result); return $result; } catch (\Exception $e) { Log::error('处理宠物激活技能定时任务失败', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; } } /** * 处理单个激活技能 * * @param PetActiveSkill $activeSkill 激活的技能 * @return array 处理结果 ['status' => 'processed|expired|skipped', 'reason' => string] */ public static function processSkill(PetActiveSkill $activeSkill): array { // 检查技能是否已过期 if ($activeSkill->isExpired()) { $activeSkill->markAsExpired(); // 记录过期日志 PetSkillProcessLog::createExpiredLog($activeSkill, '技能已过期,自动标记为过期状态'); Log::info('技能已过期', [ 'active_skill_id' => $activeSkill->id, 'pet_id' => $activeSkill->pet_id, 'skill_name' => $activeSkill->skill_name ]); return ['status' => 'expired', 'reason' => '技能已过期']; } // 检查是否需要执行检查 if (!$activeSkill->shouldCheck()) { $skipReason = self::getSkipReason($activeSkill); PetSkillProcessLog::createSkippedLog($activeSkill, $skipReason['message'], $skipReason['data']); Log::debug('shouldCheck false', [ 'active_skill_id' => $activeSkill->id, 'pet_id' => $activeSkill->pet_id, 'skill_name' => $activeSkill->skill_name, 'skip_reason' => $skipReason['message'] ]); return ['status' => 'skipped', 'reason' => $skipReason['message']]; } // 处理技能效果(事务在方法内部管理) self::processSkillEffect($activeSkill); return ['status' => 'processed', 'reason' => '技能处理成功']; } /** * 获取激活中的技能列表 * * @param int $limit 数量限制 * @return Collection|PetActiveSkill[] */ protected static function getActiveSkills(int $limit): Collection { return PetActiveSkill::where('status', PetActiveSkill::STATUS_ACTIVE) ->where('end_time', '>', now()) ->orderBy('last_check_time', 'asc') ->limit($limit) ->get(); } /** * 处理技能效果 * * @param PetActiveSkill $activeSkill 激活的技能 * @return void */ protected static function processSkillEffect(PetActiveSkill $activeSkill): void { $startTime = microtime(true); $autoSkillLogic = new PetAutoSkillLogic(); $processData = []; $processStatus = 'success'; $processReason = '技能处理成功'; try { $result = self::executeSkillByType($autoSkillLogic, $activeSkill); $processData = self::extractProcessData($result, $activeSkill->skill_name); } catch (\Exception $e) { $processStatus = 'failed'; $processReason = '技能处理异常: ' . $e->getMessage(); $processData['error'] = [ 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine() ]; } $executionTime = microtime(true) - $startTime; // 记录处理日志 if ($processStatus === 'success') { PetSkillProcessLog::createSuccessLog($activeSkill, $processData, $executionTime, $processReason); } else { PetSkillProcessLog::createFailedLog($activeSkill, $processReason, $processData, $executionTime); } // 更新最后检查时间 $activeSkill->updateLastCheckTime(); } /** * 根据技能类型执行对应的技能逻辑 * * @param PetAutoSkillLogic $autoSkillLogic 自动技能逻辑实例 * @param PetActiveSkill $activeSkill 激活的技能 * @return mixed 技能执行结果 */ protected static function executeSkillByType(PetAutoSkillLogic $autoSkillLogic, PetActiveSkill $activeSkill) { switch ($activeSkill->skill_name) { case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_HARVESTING->value: return self::processAutoHarvestWithSeparateTransactions($autoSkillLogic, $activeSkill); case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_PLANTING->value: return DB::transaction(function () use ($autoSkillLogic, $activeSkill) { return $autoSkillLogic->processAutoPlant($activeSkill); }); case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_WEEDING->value: return DB::transaction(function () use ($autoSkillLogic, $activeSkill) { return $autoSkillLogic->processAutoWeeding($activeSkill); }); case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_WATERING->value: return DB::transaction(function () use ($autoSkillLogic, $activeSkill) { return $autoSkillLogic->processAutoWatering($activeSkill); }); case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_PEST_CONTROL->value: return DB::transaction(function () use ($autoSkillLogic, $activeSkill) { return $autoSkillLogic->processAutoPestControl($activeSkill); }); case \App\Module\Pet\Enums\PET_SKILL_NAME::AUTO_FERTILIZING->value: return DB::transaction(function () use ($autoSkillLogic, $activeSkill) { return $autoSkillLogic->processAutoFertilizing($activeSkill); }); default: Log::warning('未知的技能类型', [ 'active_skill_id' => $activeSkill->id, 'skill_name' => $activeSkill->skill_name ]); throw new \Exception('未知的技能类型: ' . $activeSkill->skill_name); } } /** * 提取处理数据用于日志记录 * * @param mixed $result 处理结果 * @param string $skillName 技能名称 * @return array */ protected static function extractProcessData($result, string $skillName): array { $processData = [ 'skill_name' => $skillName, 'processed_at' => now()->toDateTimeString() ]; // 如果结果是数组,直接合并 if (is_array($result)) { $processData = array_merge($processData, $result); } return $processData; } /** * 获取跳过原因 * * @param PetActiveSkill $activeSkill 激活的技能 * @return array ['message' => string, 'data' => array] */ protected static function getSkipReason(PetActiveSkill $activeSkill): array { $lastCheckTime = $activeSkill->getLastCheckTime(); $config = $activeSkill->config; $checkInterval = is_array($config) ? ($config['check_interval'] ?? 60) : 60; $diffSeconds = $lastCheckTime ? $lastCheckTime->diffInSeconds(now()) : 0; $message = sprintf( '未到检查间隔时间,距离上次检查仅%d秒,需要间隔%d秒', $diffSeconds, $checkInterval ); $data = [ 'last_check_time' => $lastCheckTime ? $lastCheckTime->toDateTimeString() : null, 'check_interval' => $checkInterval, 'diff_seconds' => $diffSeconds ]; return ['message' => $message, 'data' => $data]; } /** * 处理自动收获技能,使用分离的事务 * * 将收获和铲除功能分别在不同的事务中执行 * * @param PetAutoSkillLogic $autoSkillLogic 自动技能逻辑实例 * @param PetActiveSkill $activeSkill 激活的技能 * @return array 技能执行结果 */ protected static function processAutoHarvestWithSeparateTransactions(PetAutoSkillLogic $autoSkillLogic, PetActiveSkill $activeSkill): array { $pet = $activeSkill->pet; $userId = $pet->user_id; Log::info('开始处理自动收菜技能(分离事务)', [ 'active_skill_id' => $activeSkill->id, 'pet_id' => $pet->id, 'user_id' => $userId ]); // 执行收获逻辑 // 注意:这里直接调用原有的processAutoHarvest方法,它已经包含了收获和铲除逻辑 // 但是铲除部分会在收获事务内执行,这符合用户要求的分离事务 DB::transaction(function () use ($autoSkillLogic, $activeSkill) { $autoSkillLogic->processAutoHarvest($activeSkill); }); // 清理已存在的枯萎作物 $witheredClearCount = 0; try { $witheredClearCount = DB::transaction(function () use ($autoSkillLogic, $userId) { return $autoSkillLogic->clearAllWitheredCrops($userId); }); } catch (\Exception $e) { Log::warning('清理枯萎作物失败', [ 'user_id' => $userId, 'error' => $e->getMessage() ]); } $finalResult = [ 'withered_cleared_count' => $witheredClearCount, 'harvest_completed' => true ]; Log::info('自动收菜技能处理完成(分离事务)', [ 'active_skill_id' => $activeSkill->id, 'pet_id' => $pet->id, 'user_id' => $userId, 'withered_cleared_count' => $witheredClearCount ]); return $finalResult; } }