| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- <?php
- namespace App\Module\Pet\Services;
- use App\Module\Pet\Models\PetActiveSkill;
- use App\Module\Pet\Models\PetSkillProcessLog;
- use App\Module\Pet\Logic\PetAutoSkillLogic;
- use Illuminate\Support\Facades\Log;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Collection;
- /**
- * 宠物激活技能服务类
- *
- * 提供宠物激活技能处理的服务,包括批量处理激活技能、单个技能处理等功能。
- * 该类是宠物激活技能处理的主要入口,封装了技能处理的复杂逻辑。
- */
- class PetActiveSkillService
- {
- /**
- * 批量处理激活技能
- *
- * @param int $limit 处理数量限制,默认100
- * @return array 处理结果统计
- */
- public static function processActiveSkills(int $limit = 100): array
- {
- Log::info('开始处理宠物激活技能定时任务');
- try {
- // 获取所有生效中的技能,按最后检查时间排序(优先处理长时间未检查的)
- $activeSkills = self::getActiveSkills($limit);
- Log::info('找到激活技能数量', ['count' => $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;
- }
- }
|