Ver Fonte

feat(pet): 实现宠物生活技能自动执行功能

- 新增宠物生活技能自动执行逻辑,包括自动收菜、自动播种和灾害防护
- 实现宠物激活技能的定时任务和命令
- 添加宠物信息和生活技能情况的统计卡片
- 优化宠物生活技能使用的请求处理
notfff há 7 meses atrás
pai
commit
d85e0e5815

+ 148 - 1
app/Module/AppGame/Handler/Pet/LifeSkillUseHandler.php

@@ -3,9 +3,13 @@
 namespace App\Module\AppGame\Handler\Pet;
 
 use App\Module\AppGame\Handler\BaseHandler;
+use App\Module\Pet\Services\PetService;
 use Google\Protobuf\Internal\Message;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
 use Uraus\Kku\Request\RequestPetLifeSkillUse;
 use Uraus\Kku\Response\ResponsePetLifeSkillUse;
+use UCore\Exception\LogicException;
 
 /**
  * 处理使用宠物生活技能请求
@@ -29,7 +33,150 @@ class LifeSkillUseHandler extends BaseHandler
         // 创建响应对象
         $response = new ResponsePetLifeSkillUse();
 
-        // TODO: 实现具体逻辑
+        try {
+            // 获取请求参数
+            $petId = $data->getPetId();
+            $skillIds = $data->getSkillId(); // RepeatedField,包含多个技能ID
+            $userId = $this->user_id;
+
+            // 将RepeatedField转换为数组
+            $skillIdArray = [];
+            foreach ($skillIds as $skillId) {
+                $skillIdArray[] = $skillId;
+            }
+
+            // 技能参数(暂时使用默认值)
+            $params = [];
+
+            Log::info('用户批量使用宠物生活技能', [
+                'user_id' => $userId,
+                'pet_id' => $petId,
+                'skill_ids' => $skillIdArray,
+                'skill_count' => count($skillIdArray)
+            ]);
+
+            // 验证参数
+            if (!$petId || $petId <= 0) {
+                throw new LogicException("宠物ID无效");
+            }
+            if (empty($skillIdArray)) {
+                throw new LogicException("技能ID列表不能为空");
+            }
+
+            // 验证技能ID
+            foreach ($skillIdArray as $skillId) {
+                if (!$skillId || $skillId <= 0) {
+                    throw new LogicException("技能ID无效: {$skillId}");
+                }
+            }
+
+            // 开启事务
+            DB::beginTransaction();
+
+            $successCount = 0;
+            $failedSkills = [];
+            $errorMessages = [];
+
+            // 逐个激活技能
+            foreach ($skillIdArray as $skillId) {
+                try {
+                    // 调用宠物服务使用技能
+                    $result = PetService::useSkill($userId, $petId, $skillId, $params);
+                    $successCount++;
+
+                    Log::info('单个技能激活成功', [
+                        'user_id' => $userId,
+                        'pet_id' => $petId,
+                        'skill_id' => $skillId
+                    ]);
+
+                } catch (\Exception $e) {
+                    $failedSkills[] = $skillId;
+                    $errorMessages[] = "技能{$skillId}: " . $e->getMessage();
+
+                    Log::warning('单个技能激活失败', [
+                        'user_id' => $userId,
+                        'pet_id' => $petId,
+                        'skill_id' => $skillId,
+                        'error' => $e->getMessage()
+                    ]);
+                }
+            }
+
+            // 如果有失败的技能,抛出异常说明情况
+            if (!empty($failedSkills)) {
+                $errorMsg = "部分技能激活失败:" . implode('; ', $errorMessages);
+                if ($successCount === 0) {
+                    // 全部失败
+                    throw new LogicException("所有技能激活失败:" . implode('; ', $errorMessages));
+                } else {
+                    // 部分失败,记录警告但不抛出异常
+                    Log::warning('部分技能激活失败', [
+                        'user_id' => $userId,
+                        'pet_id' => $petId,
+                        'success_count' => $successCount,
+                        'failed_count' => count($failedSkills),
+                        'error_message' => $errorMsg
+                    ]);
+                }
+            }
+
+            // 提交事务
+            DB::commit();
+
+            Log::info('宠物生活技能批量使用完成', [
+                'user_id' => $userId,
+                'pet_id' => $petId,
+                'total_skills' => count($skillIdArray),
+                'success_count' => $successCount,
+                'failed_count' => count($failedSkills),
+                'failed_skills' => $failedSkills
+            ]);
+
+        } catch (\UCore\Exception\ValidateException $e) {
+            // 验证失败
+            DB::rollBack();
+
+            Log::warning('宠物生活技能使用验证失败', [
+                'user_id' => $this->user_id,
+                'pet_id' => $petId ?? null,
+                'skill_id' => $skillId ?? null,
+                'error' => $e->getMessage()
+            ]);
+
+            throw $e;
+
+        } catch (LogicException $e) {
+            // 业务逻辑异常
+            if (DB::transactionLevel() > 0) {
+                DB::rollBack();
+            }
+
+            Log::warning('宠物生活技能使用失败', [
+                'user_id' => $this->user_id,
+                'pet_id' => $petId ?? null,
+                'skill_id' => $skillId ?? null,
+                'error' => $e->getMessage()
+            ]);
+
+            throw $e;
+
+        } catch (\Exception $e) {
+            // 系统异常
+            if (DB::transactionLevel() > 0) {
+                DB::rollBack();
+            }
+
+            Log::error('宠物生活技能使用异常', [
+                'user_id' => $this->user_id,
+                'pet_id' => $petId ?? null,
+                'skill_id' => $skillId ?? null,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            throw $e;
+        }
 
         return $response;
     }

+ 22 - 1
app/Module/AppGame/Validations/PetEatValidation.php

@@ -22,7 +22,28 @@ class PetEatValidation extends ValidationBase
      *
      * @var int
      */
-    public $user_id;
+    public int $user_id;
+
+    /**
+     * 宠物ID
+     *
+     * @var int
+     */
+    public int $pet_id;
+
+    /**
+     * 物品ID
+     *
+     * @var int
+     */
+    public int $item_id;
+
+    /**
+     * 数量
+     *
+     * @var int
+     */
+    public int $num;
 
     /**
      * 验证规则

+ 312 - 1
app/Module/Game/AdminControllers/FarmUserSummaryController.php

@@ -14,6 +14,9 @@ use App\Module\Fund\Models\FundModel;
 use App\Module\Fund\Services\AccountService;
 use App\Module\GameItems\Enums\ITEM_TYPE;
 use App\Module\GameItems\Models\ItemUser;
+use App\Module\Pet\Models\PetUser;
+use App\Module\Pet\Models\PetActiveSkill;
+use App\Module\Pet\Enums\PetStatus;
 use App\Module\User\Models\User;
 use Dcat\Admin\Grid;
 use Dcat\Admin\Layout\Content;
@@ -108,7 +111,13 @@ class FarmUserSummaryController extends AdminController
                 // 第四行:代币信息
                 $row->column(12, $this->fundInfoCard($user->id));
 
-                // 第五行:神像buff信息
+                // 第五行:宠物信息
+                $row->column(12, $this->petInfoCard($user->id));
+
+                // 第六行:宠物生活技能情况
+                $row->column(12, $this->petLifeSkillsCard($user->id));
+
+                // 第七行:神像buff信息
                 $row->column(12, $this->buffInfoCard($user->id));
             });
     }
@@ -673,6 +682,284 @@ class FarmUserSummaryController extends AdminController
         return new Card('代币信息', $content);
     }
 
+    /**
+     * 宠物信息卡片
+     *
+     * @param int $userId 用户ID
+     * @return Card
+     */
+    protected function petInfoCard($userId)
+    {
+        // 获取用户的宠物信息
+        $pets = PetUser::where('user_id', $userId)
+            ->orderBy('level', 'desc')
+            ->orderBy('grade', 'desc')
+            ->get();
+
+        if ($pets->isEmpty()) {
+            return new Card('宠物信息', new Alert('warning', '该用户没有宠物信息'));
+        }
+
+        // 宠物统计
+        $totalPets = $pets->count();
+        $gradeStats = $pets->groupBy('grade')->map->count();
+        $statusStats = $pets->groupBy('status')->map->count();
+        $levelStats = [
+            'max_level' => $pets->max('level'),
+            'avg_level' => round($pets->avg('level'), 1),
+            'total_exp' => $pets->sum('experience'),
+        ];
+
+        // 创建宠物表格
+        $headers = ['宠物名称', '品阶', '等级', '经验', '体力', '状态', '创建时间'];
+        $rows = [];
+
+        $gradeNames = [
+            1 => '一品',
+            2 => '二品',
+            3 => '三品',
+            4 => '四品',
+        ];
+
+        $statusNames = [
+            PetStatus::NONE->value => '未知',
+            PetStatus::NORMAL->value => '正常',
+            PetStatus::FIGHTING->value => '战斗中',
+            PetStatus::DEAD->value => '死亡',
+            PetStatus::FEEDING->value => '喂养中',
+            PetStatus::TRAINING->value => '训练中',
+            PetStatus::RESTING->value => '休息中',
+            PetStatus::TRAVELING->value => '外出中',
+        ];
+
+        foreach ($pets as $pet) {
+            try {
+                $gradeName = $gradeNames[$pet->grade] ?? "品阶{$pet->grade}";
+                $statusName = $statusNames[$pet->status->value] ?? "状态{$pet->status->value}";
+
+                // 状态颜色
+                $statusBadge = match($pet->status) {
+                    PetStatus::NORMAL => '<span class="badge badge-success">' . $statusName . '</span>',
+                    PetStatus::FIGHTING => '<span class="badge badge-warning">' . $statusName . '</span>',
+                    PetStatus::DEAD => '<span class="badge badge-danger">' . $statusName . '</span>',
+                    PetStatus::FEEDING, PetStatus::TRAINING => '<span class="badge badge-info">' . $statusName . '</span>',
+                    PetStatus::RESTING, PetStatus::TRAVELING => '<span class="badge badge-primary">' . $statusName . '</span>',
+                    default => '<span class="badge badge-secondary">' . $statusName . '</span>',
+                };
+
+                $rows[] = [
+                    $pet->name,
+                    $gradeName,
+                    $pet->level,
+                    $pet->experience,
+                    $pet->stamina,
+                    $statusBadge,
+                    $pet->created_at->format('Y-m-d H:i:s'),
+                ];
+            } catch (\Throwable) {
+                // 如果出现异常,添加一个错误行
+                $rows[] = [
+                    $pet->name ?? '未知',
+                    '数据错误',
+                    $pet->level ?? 0,
+                    $pet->experience ?? 0,
+                    $pet->stamina ?? 0,
+                    '<span class="badge badge-danger">数据错误</span>',
+                    $pet->created_at ? $pet->created_at->format('Y-m-d H:i:s') : '未知',
+                ];
+            }
+        }
+
+        $table = new Table($headers, $rows);
+
+        // 统计信息
+        $statsHtml = '<div class="row mb-3">';
+        $statsHtml .= '<div class="col-md-3"><strong>宠物总数:</strong> ' . $totalPets . '</div>';
+        $statsHtml .= '<div class="col-md-3"><strong>最高等级:</strong> ' . $levelStats['max_level'] . '</div>';
+        $statsHtml .= '<div class="col-md-3"><strong>平均等级:</strong> ' . $levelStats['avg_level'] . '</div>';
+        $statsHtml .= '<div class="col-md-3"><strong>总经验:</strong> ' . number_format($levelStats['total_exp']) . '</div>';
+        $statsHtml .= '</div>';
+
+        // 品阶统计
+        if (!$gradeStats->isEmpty()) {
+            $statsHtml .= '<div class="row mb-3"><div class="col-md-12"><strong>品阶分布:</strong> ';
+            foreach ($gradeStats as $grade => $count) {
+                $gradeName = $gradeNames[$grade] ?? "品阶{$grade}";
+                $statsHtml .= "{$gradeName}: {$count}只 ";
+            }
+            $statsHtml .= '</div></div>';
+        }
+
+        // 状态统计
+        if (!$statusStats->isEmpty()) {
+            $statsHtml .= '<div class="row mb-3"><div class="col-md-12"><strong>状态分布:</strong> ';
+            foreach ($statusStats as $status => $count) {
+                $statusName = $statusNames[$status] ?? "状态{$status}";
+                $statsHtml .= "{$statusName}: {$count}只 ";
+            }
+            $statsHtml .= '</div></div>';
+        }
+
+        $content = $statsHtml . $table->render();
+
+        return new Card('宠物信息', $content);
+    }
+
+    /**
+     * 宠物生活技能情况卡片
+     *
+     * @param int $userId 用户ID
+     * @return Card
+     */
+    protected function petLifeSkillsCard($userId)
+    {
+        // 获取用户的宠物
+        $pets = PetUser::where('user_id', $userId)->get();
+
+        if ($pets->isEmpty()) {
+            return new Card('宠物生活技能情况', new Alert('warning', '该用户没有宠物,无法使用生活技能'));
+        }
+
+        // 获取所有宠物的激活技能
+        $petIds = $pets->pluck('id')->toArray();
+        $activeSkills = PetActiveSkill::with(['pet', 'skill'])
+            ->whereIn('pet_id', $petIds)
+            ->orderBy('status', 'asc')
+            ->orderBy('end_time', 'desc')
+            ->get();
+
+        // 统计信息
+        $totalSkills = $activeSkills->count();
+        $activeCount = $activeSkills->where('status', 'active')->count();
+        $expiredCount = $activeSkills->where('status', 'expired')->count();
+        $cancelledCount = $activeSkills->where('status', 'cancelled')->count();
+
+        // 技能类型统计
+        $skillTypeStats = $activeSkills->groupBy('skill_name')->map->count();
+
+        // 创建统计信息
+        $statsHtml = '<div class="row mb-3">';
+        $statsHtml .= '<div class="col-md-3"><strong>总技能使用次数:</strong> ' . $totalSkills . '</div>';
+        $statsHtml .= '<div class="col-md-3"><strong>当前激活:</strong> <span class="badge badge-success">' . $activeCount . '</span></div>';
+        $statsHtml .= '<div class="col-md-3"><strong>已过期:</strong> <span class="badge badge-secondary">' . $expiredCount . '</span></div>';
+        $statsHtml .= '<div class="col-md-3"><strong>已取消:</strong> <span class="badge badge-warning">' . $cancelledCount . '</span></div>';
+        $statsHtml .= '</div>';
+
+        // 技能类型统计
+        if (!$skillTypeStats->isEmpty()) {
+            $statsHtml .= '<div class="row mb-3"><div class="col-md-12"><strong>技能使用统计:</strong> ';
+            foreach ($skillTypeStats as $skillName => $count) {
+                $statsHtml .= "<span class='badge badge-info mr-2'>{$skillName}: {$count}次</span> ";
+            }
+            $statsHtml .= '</div></div>';
+        }
+
+        if ($activeSkills->isEmpty()) {
+            $content = $statsHtml . '<div class="alert alert-info">该用户的宠物还没有使用过生活技能</div>';
+            return new Card('宠物生活技能情况', $content);
+        }
+
+        // 创建技能详情表格
+        $headers = ['宠物名称', '技能名称', '开始时间', '结束时间', '状态', '剩余时间', '技能配置'];
+        $rows = [];
+
+        foreach ($activeSkills as $activeSkill) {
+            try {
+                $petName = $activeSkill->pet ? $activeSkill->pet->name : "宠物{$activeSkill->pet_id}";
+                $skillName = $activeSkill->skill_name;
+                $startTime = $activeSkill->start_time->format('Y-m-d H:i:s');
+                $endTime = $activeSkill->end_time->format('Y-m-d H:i:s');
+
+                // 状态显示
+                $statusBadge = match($activeSkill->status) {
+                    'active' => '<span class="badge badge-success">生效中</span>',
+                    'expired' => '<span class="badge badge-secondary">已过期</span>',
+                    'cancelled' => '<span class="badge badge-warning">已取消</span>',
+                    default => '<span class="badge badge-dark">' . $activeSkill->status . '</span>',
+                };
+
+                // 剩余时间
+                $remainingTime = '';
+                if ($activeSkill->status === 'active') {
+                    $remaining = $activeSkill->getRemainingSeconds();
+                    if ($remaining > 0) {
+                        $hours = floor($remaining / 3600);
+                        $minutes = floor(($remaining % 3600) / 60);
+                        $seconds = $remaining % 60;
+                        $remainingTime = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
+                    } else {
+                        $remainingTime = '<span class="text-danger">已过期</span>';
+                    }
+                } else {
+                    $remainingTime = '-';
+                }
+
+                // 技能配置信息
+                $configInfo = '';
+                if (!empty($activeSkill->config)) {
+                    $config = $activeSkill->config;
+                    $configItems = [];
+
+                    // 显示关键配置信息
+                    if (isset($config['check_interval'])) {
+                        $configItems[] = "检查间隔: {$config['check_interval']}秒";
+                    }
+                    if (isset($config['preferred_seeds']) && !empty($config['preferred_seeds'])) {
+                        $seedCount = count($config['preferred_seeds']);
+                        $configItems[] = "优先种子: {$seedCount}种";
+                    }
+                    if (isset($config['protected_types']) && !empty($config['protected_types'])) {
+                        $protectedTypes = implode(', ', $config['protected_types']);
+                        $configItems[] = "防护类型: {$protectedTypes}";
+                    }
+                    if (isset($config['statistics']) && !empty($config['statistics'])) {
+                        $statsCount = count($config['statistics']);
+                        $configItems[] = "执行记录: {$statsCount}次";
+                    }
+
+                    $configInfo = implode('<br>', $configItems);
+                }
+
+                $rows[] = [
+                    $petName,
+                    $skillName,
+                    $startTime,
+                    $endTime,
+                    $statusBadge,
+                    $remainingTime,
+                    $configInfo ?: '-'
+                ];
+
+            } catch (\Throwable $e) {
+                // 如果出现异常,添加一个错误行
+                $rows[] = [
+                    $activeSkill->pet ? $activeSkill->pet->name : '未知',
+                    $activeSkill->skill_name ?? '未知',
+                    $activeSkill->start_time ? $activeSkill->start_time->format('Y-m-d H:i:s') : '未知',
+                    $activeSkill->end_time ? $activeSkill->end_time->format('Y-m-d H:i:s') : '未知',
+                    '<span class="badge badge-danger">数据错误</span>',
+                    '-',
+                    '数据解析错误'
+                ];
+            }
+        }
+
+        $table = new Table($headers, $rows);
+
+        $content = $statsHtml . $table->render();
+
+        // 添加说明信息
+        $content .= '<div class="alert alert-info mt-3">';
+        $content .= '<strong>说明:</strong><br>';
+        $content .= '• <strong>自动收菜</strong>:每分钟自动收获成熟的作物<br>';
+        $content .= '• <strong>自动播种</strong>:每分钟自动在空闲土地上播种<br>';
+        $content .= '• <strong>灾害防护</strong>:每5分钟检查并自动清除作物灾害<br>';
+        $content .= '• 所有自动操作仍需消耗相应的道具(种子、化肥等)';
+        $content .= '</div>';
+
+        return new Card('宠物生活技能情况', $content);
+    }
+
     /**
      * 神像buff信息卡片
      *
@@ -796,6 +1083,30 @@ class FarmUserSummaryController extends AdminController
                 return $fundCount > 0 ? "{$fundCount}个账户" : '<span class="text-muted">无账户</span>';
             });
 
+            // 宠物统计
+            $grid->column('id', '宠物统计')->display(function ($userId) {
+                $pets = PetUser::where('user_id', $userId)->get();
+                if ($pets->isEmpty()) {
+                    return '<span class="text-muted">无宠物</span>';
+                }
+
+                $totalPets = $pets->count();
+                $maxLevel = $pets->max('level');
+                $gradeStats = $pets->groupBy('grade')->map->count();
+
+                $html = "<div>总计: {$totalPets}只宠物</div>";
+                $html .= "<div>最高等级: {$maxLevel}</div>";
+
+                // 显示品阶分布
+                $gradeNames = [1 => '一品', 2 => '二品', 3 => '三品', 4 => '四品'];
+                foreach ($gradeStats as $grade => $count) {
+                    $gradeName = $gradeNames[$grade] ?? "品阶{$grade}";
+                    $html .= "<div>{$gradeName}: {$count}只</div>";
+                }
+
+                return $html;
+            });
+
             $grid->column('created_at', '创建时间')->sortable();
 
             // 添加查看详情操作

+ 8 - 0
app/Module/Game/Providers/GameServiceProvider.php

@@ -16,6 +16,7 @@ use App\Module\Farm\Events\DisasterClearedEvent;
 use App\Module\Farm\Events\HouseDowngradedEvent;
 use App\Module\Farm\Events\HouseUpgradedEvent;
 
+use App\Module\Farm\Events\LandStatusChangedEvent;
 use App\Module\Farm\Events\LandUpgradedEvent;
 use App\Module\Fund\Events\FundChangedEvent;
 use App\Module\Game\Listeners\CropGrowthStageChangedListener;
@@ -25,6 +26,7 @@ use App\Module\Game\Listeners\FundChangedListener;
 use App\Module\Game\Listeners\HouseDowngradedListener;
 use App\Module\Game\Listeners\HouseUpgradedListener;
 use App\Module\Game\Listeners\ItemQuantityChangedListener;
+use App\Module\Game\Listeners\LandStatusChangedListener;
 use App\Module\Game\Listeners\LandUpgradedListener;
 use App\Module\Game\Listeners\LogRewardGrantedListener;
 use App\Module\Game\Listeners\NotifyRewardGrantedListener;
@@ -117,6 +119,12 @@ class GameServiceProvider extends ServiceProvider
             LandUpgradedListener::class
         );
 
+        // 注册土地状态变更事件监听器
+        Event::listen(
+            LandStatusChangedEvent::class,
+            LandStatusChangedListener::class
+        );
+
         // 注册作物种植事件监听器
         Event::listen(
             CropPlantedEvent::class,

+ 60 - 0
app/Module/Pet/Console/ProcessActiveSkillsCommand.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace App\Module\Pet\Console;
+
+use App\Module\Pet\Jobs\ProcessActiveSkillsJob;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 处理宠物激活技能的定时命令
+ *
+ * 每分钟执行一次,处理所有激活中的宠物技能
+ */
+class ProcessActiveSkillsCommand extends Command
+{
+    /**
+     * 命令签名
+     *
+     * @var string
+     */
+    protected $signature = 'pet:process-active-skills';
+
+    /**
+     * 命令描述
+     *
+     * @var string
+     */
+    protected $description = '处理宠物激活技能的定时任务';
+
+    /**
+     * 执行命令
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        $this->info('开始处理宠物激活技能...');
+
+        try {
+            // 分发任务到队列
+            ProcessActiveSkillsJob::dispatch();
+
+            $this->info('宠物激活技能处理任务已分发到队列');
+
+            Log::info('宠物激活技能定时命令执行成功');
+
+            return Command::SUCCESS;
+
+        } catch (\Exception $e) {
+            $this->error('处理宠物激活技能失败: ' . $e->getMessage());
+
+            Log::error('宠物激活技能定时命令执行失败', [
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            return Command::FAILURE;
+        }
+    }
+}

+ 163 - 0
app/Module/Pet/Jobs/ProcessActiveSkillsJob.php

@@ -0,0 +1,163 @@
+<?php
+
+namespace App\Module\Pet\Jobs;
+
+use App\Module\Pet\Models\PetActiveSkill;
+use App\Module\Pet\Logic\PetAutoSkillLogic;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 处理宠物激活技能的定时任务
+ *
+ * 每分钟执行一次,检查所有激活中的宠物技能并执行相应操作
+ */
+class ProcessActiveSkillsJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    /**
+     * 任务超时时间(秒)
+     *
+     * @var int
+     */
+    public $timeout = 300; // 5分钟
+
+    /**
+     * 最大重试次数
+     *
+     * @var int
+     */
+    public $tries = 3;
+
+    /**
+     * 创建新的任务实例
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * 执行任务
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        Log::info('开始处理宠物激活技能定时任务');
+
+        try {
+            // 获取所有生效中的技能
+            $activeSkills = PetActiveSkill::active()->get();
+
+            Log::info('找到激活技能数量', ['count' => $activeSkills->count()]);
+
+            $processedCount = 0;
+            $expiredCount = 0;
+
+            foreach ($activeSkills as $activeSkill) {
+                try {
+                    // 检查技能是否已过期
+                    if ($activeSkill->isExpired()) {
+                        $activeSkill->markAsExpired();
+                        $expiredCount++;
+                        Log::info('技能已过期', [
+                            'active_skill_id' => $activeSkill->id,
+                            'pet_id' => $activeSkill->pet_id,
+                            'skill_name' => $activeSkill->skill_name
+                        ]);
+                        continue;
+                    }
+
+                    // 检查是否需要执行检查
+                    if (!$activeSkill->shouldCheck()) {
+                        continue;
+                    }
+
+                    // 处理技能效果
+                    $this->processSkillEffect($activeSkill);
+                    $processedCount++;
+
+                } 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()
+                    ]);
+                }
+            }
+
+            Log::info('宠物激活技能定时任务完成', [
+                'total_skills' => $activeSkills->count(),
+                'processed_count' => $processedCount,
+                'expired_count' => $expiredCount
+            ]);
+
+        } catch (\Exception $e) {
+            Log::error('处理宠物激活技能定时任务失败', [
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            throw $e;
+        }
+    }
+
+    /**
+     * 处理技能效果
+     *
+     * @param PetActiveSkill $activeSkill 激活的技能
+     * @return void
+     */
+    protected function processSkillEffect(PetActiveSkill $activeSkill): void
+    {
+        $autoSkillLogic = new PetAutoSkillLogic();
+
+        switch ($activeSkill->skill_name) {
+            case '自动收菜':
+                $autoSkillLogic->processAutoHarvest($activeSkill);
+                break;
+
+            case '自动播种':
+                $autoSkillLogic->processAutoPlant($activeSkill);
+                break;
+
+            case '灾害防护':
+                $autoSkillLogic->processDisasterProtection($activeSkill);
+                break;
+
+            default:
+                Log::warning('未知的技能类型', [
+                    'active_skill_id' => $activeSkill->id,
+                    'skill_name' => $activeSkill->skill_name
+                ]);
+                break;
+        }
+
+        // 更新最后检查时间
+        $activeSkill->updateLastCheckTime();
+    }
+
+    /**
+     * 任务失败时的处理
+     *
+     * @param \Throwable $exception
+     * @return void
+     */
+    public function failed(\Throwable $exception)
+    {
+        Log::error('宠物激活技能定时任务失败', [
+            'error' => $exception->getMessage(),
+            'trace' => $exception->getTraceAsString()
+        ]);
+    }
+}

+ 408 - 0
app/Module/Pet/Logic/PetAutoSkillLogic.php

@@ -0,0 +1,408 @@
+<?php
+
+namespace App\Module\Pet\Logic;
+
+use App\Module\Pet\Models\PetActiveSkill;
+use App\Module\Farm\Services\CropService;
+use App\Module\Farm\Services\LandService;
+use App\Module\GameItems\Services\ItemService;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 宠物自动技能处理逻辑
+ *
+ * 处理宠物激活技能的自动执行逻辑
+ */
+class PetAutoSkillLogic
+{
+    /**
+     * 处理自动收菜技能
+     *
+     * @param PetActiveSkill $activeSkill 激活的技能
+     * @return void
+     */
+    public function processAutoHarvest(PetActiveSkill $activeSkill): void
+    {
+        try {
+            $pet = $activeSkill->pet;
+            $userId = $pet->user_id;
+
+            Log::info('开始处理自动收菜技能', [
+                'active_skill_id' => $activeSkill->id,
+                'pet_id' => $pet->id,
+                'user_id' => $userId
+            ]);
+
+            // 获取用户所有可收获的土地
+            $harvestableLands = LandService::getHarvestableLands($userId);
+
+            if ($harvestableLands->isEmpty()) {
+                Log::info('没有可收获的土地', [
+                    'user_id' => $userId,
+                    'pet_id' => $pet->id
+                ]);
+                return;
+            }
+
+            $harvestCount = 0;
+            $harvestResults = [];
+
+            foreach ($harvestableLands as $land) {
+                try {
+                    DB::beginTransaction();
+
+                    // 调用收获服务
+                    $result = CropService::harvestCrop($userId, $land->id);
+                    
+                    if (!$result->error) {
+                        $harvestCount++;
+                        $harvestResults[] = [
+                            'land_id' => $land->id,
+                            'result' => $result->data
+                        ];
+
+                        Log::info('自动收菜成功', [
+                            'user_id' => $userId,
+                            'pet_id' => $pet->id,
+                            'land_id' => $land->id
+                        ]);
+                    }
+
+                    DB::commit();
+
+                } catch (\Exception $e) {
+                    DB::rollBack();
+                    Log::warning('自动收菜失败', [
+                        'user_id' => $userId,
+                        'pet_id' => $pet->id,
+                        'land_id' => $land->id,
+                        'error' => $e->getMessage()
+                    ]);
+                }
+            }
+
+            // 记录统计信息
+            $this->recordSkillStatistics($activeSkill, 'auto_harvest', [
+                'harvest_count' => $harvestCount,
+                'total_lands_checked' => $harvestableLands->count(),
+                'harvest_results' => $harvestResults
+            ]);
+
+            Log::info('自动收菜技能处理完成', [
+                'active_skill_id' => $activeSkill->id,
+                'pet_id' => $pet->id,
+                'user_id' => $userId,
+                'harvest_count' => $harvestCount,
+                'total_lands' => $harvestableLands->count()
+            ]);
+
+        } catch (\Exception $e) {
+            Log::error('处理自动收菜技能失败', [
+                'active_skill_id' => $activeSkill->id,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+        }
+    }
+
+    /**
+     * 处理自动播种技能
+     *
+     * @param PetActiveSkill $activeSkill 激活的技能
+     * @return void
+     */
+    public function processAutoPlant(PetActiveSkill $activeSkill): void
+    {
+        try {
+            $pet = $activeSkill->pet;
+            $userId = $pet->user_id;
+
+            Log::info('开始处理自动播种技能', [
+                'active_skill_id' => $activeSkill->id,
+                'pet_id' => $pet->id,
+                'user_id' => $userId
+            ]);
+
+            // 获取用户所有空闲的土地
+            $idleLands = LandService::getIdleLands($userId);
+
+            if ($idleLands->isEmpty()) {
+                Log::info('没有空闲的土地', [
+                    'user_id' => $userId,
+                    'pet_id' => $pet->id
+                ]);
+                return;
+            }
+
+            // 获取优先使用的种子列表
+            $preferredSeeds = $activeSkill->getConfigValue('preferred_seeds', []);
+            
+            // 获取用户拥有的种子物品
+            $availableSeeds = $this->getAvailableSeeds($userId, $preferredSeeds);
+
+            if (empty($availableSeeds)) {
+                Log::info('没有可用的种子', [
+                    'user_id' => $userId,
+                    'pet_id' => $pet->id
+                ]);
+                return;
+            }
+
+            $plantCount = 0;
+            $plantResults = [];
+
+            foreach ($idleLands as $land) {
+                if (empty($availableSeeds)) {
+                    break; // 种子用完了
+                }
+
+                try {
+                    DB::beginTransaction();
+
+                    // 选择种子(优先使用配置的种子)
+                    $seedItemId = array_shift($availableSeeds);
+
+                    // 调用种植服务
+                    $result = CropService::plantCrop($userId, $land->id, $seedItemId);
+                    
+                    if ($result) {
+                        $plantCount++;
+                        $plantResults[] = [
+                            'land_id' => $land->id,
+                            'seed_item_id' => $seedItemId,
+                            'result' => $result
+                        ];
+
+                        Log::info('自动播种成功', [
+                            'user_id' => $userId,
+                            'pet_id' => $pet->id,
+                            'land_id' => $land->id,
+                            'seed_item_id' => $seedItemId
+                        ]);
+                    }
+
+                    DB::commit();
+
+                } catch (\Exception $e) {
+                    DB::rollBack();
+                    Log::warning('自动播种失败', [
+                        'user_id' => $userId,
+                        'pet_id' => $pet->id,
+                        'land_id' => $land->id,
+                        'seed_item_id' => $seedItemId ?? null,
+                        'error' => $e->getMessage()
+                    ]);
+                }
+            }
+
+            // 记录统计信息
+            $this->recordSkillStatistics($activeSkill, 'auto_plant', [
+                'plant_count' => $plantCount,
+                'total_lands_checked' => $idleLands->count(),
+                'plant_results' => $plantResults
+            ]);
+
+            Log::info('自动播种技能处理完成', [
+                'active_skill_id' => $activeSkill->id,
+                'pet_id' => $pet->id,
+                'user_id' => $userId,
+                'plant_count' => $plantCount,
+                'total_lands' => $idleLands->count()
+            ]);
+
+        } catch (\Exception $e) {
+            Log::error('处理自动播种技能失败', [
+                'active_skill_id' => $activeSkill->id,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+        }
+    }
+
+    /**
+     * 处理灾害防护技能
+     *
+     * @param PetActiveSkill $activeSkill 激活的技能
+     * @return void
+     */
+    public function processDisasterProtection(PetActiveSkill $activeSkill): void
+    {
+        try {
+            $pet = $activeSkill->pet;
+            $userId = $pet->user_id;
+
+            Log::info('开始处理灾害防护技能', [
+                'active_skill_id' => $activeSkill->id,
+                'pet_id' => $pet->id,
+                'user_id' => $userId
+            ]);
+
+            // 获取防护的灾害类型
+            $protectedTypes = $activeSkill->getConfigValue('protected_types', ['all']);
+
+            // 获取用户所有有作物的土地
+            $landsWithCrops = LandService::getLandsWithCrops($userId);
+
+            if ($landsWithCrops->isEmpty()) {
+                Log::info('没有种植作物的土地', [
+                    'user_id' => $userId,
+                    'pet_id' => $pet->id
+                ]);
+                return;
+            }
+
+            $protectionCount = 0;
+
+            foreach ($landsWithCrops as $land) {
+                try {
+                    // 检查土地是否有灾害
+                    $hasDisaster = $this->checkLandDisaster($land, $protectedTypes);
+                    
+                    if ($hasDisaster) {
+                        // 自动清除灾害(需要消耗相应道具)
+                        $cleared = $this->autoClearDisaster($userId, $land, $protectedTypes);
+                        
+                        if ($cleared) {
+                            $protectionCount++;
+                            Log::info('自动清除灾害成功', [
+                                'user_id' => $userId,
+                                'pet_id' => $pet->id,
+                                'land_id' => $land->id
+                            ]);
+                        }
+                    }
+
+                } catch (\Exception $e) {
+                    Log::warning('灾害防护处理失败', [
+                        'user_id' => $userId,
+                        'pet_id' => $pet->id,
+                        'land_id' => $land->id,
+                        'error' => $e->getMessage()
+                    ]);
+                }
+            }
+
+            // 记录统计信息
+            $this->recordSkillStatistics($activeSkill, 'disaster_protection', [
+                'protection_count' => $protectionCount,
+                'total_lands_checked' => $landsWithCrops->count(),
+                'protected_types' => $protectedTypes
+            ]);
+
+            Log::info('灾害防护技能处理完成', [
+                'active_skill_id' => $activeSkill->id,
+                'pet_id' => $pet->id,
+                'user_id' => $userId,
+                'protection_count' => $protectionCount,
+                'total_lands' => $landsWithCrops->count()
+            ]);
+
+        } catch (\Exception $e) {
+            Log::error('处理灾害防护技能失败', [
+                'active_skill_id' => $activeSkill->id,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+        }
+    }
+
+    /**
+     * 获取用户可用的种子
+     *
+     * @param int $userId 用户ID
+     * @param array $preferredSeeds 优先种子列表
+     * @return array 可用种子ID列表
+     */
+    protected function getAvailableSeeds(int $userId, array $preferredSeeds = []): array
+    {
+        // 这里需要调用物品服务获取用户拥有的种子
+        // 优先返回配置的种子,然后是其他种子
+        
+        // 示例实现,需要根据实际的物品服务接口调整
+        $allSeeds = ItemService::getUserSeedItems($userId);
+        
+        $availableSeeds = [];
+        
+        // 优先添加配置的种子
+        foreach ($preferredSeeds as $seedId) {
+            if (isset($allSeeds[$seedId]) && $allSeeds[$seedId] > 0) {
+                for ($i = 0; $i < $allSeeds[$seedId]; $i++) {
+                    $availableSeeds[] = $seedId;
+                }
+            }
+        }
+        
+        // 添加其他种子
+        foreach ($allSeeds as $seedId => $quantity) {
+            if (!in_array($seedId, $preferredSeeds) && $quantity > 0) {
+                for ($i = 0; $i < $quantity; $i++) {
+                    $availableSeeds[] = $seedId;
+                }
+            }
+        }
+        
+        return $availableSeeds;
+    }
+
+    /**
+     * 检查土地是否有灾害
+     *
+     * @param mixed $land 土地对象
+     * @param array $protectedTypes 防护的灾害类型
+     * @return bool
+     */
+    protected function checkLandDisaster($land, array $protectedTypes): bool
+    {
+        // 这里需要根据实际的土地模型结构来检查灾害
+        // 示例实现
+        return false;
+    }
+
+    /**
+     * 自动清除灾害
+     *
+     * @param int $userId 用户ID
+     * @param mixed $land 土地对象
+     * @param array $protectedTypes 防护的灾害类型
+     * @return bool
+     */
+    protected function autoClearDisaster(int $userId, $land, array $protectedTypes): bool
+    {
+        // 这里需要实现自动清除灾害的逻辑
+        // 需要消耗相应的道具
+        // 示例实现
+        return false;
+    }
+
+    /**
+     * 记录技能统计信息
+     *
+     * @param PetActiveSkill $activeSkill 激活的技能
+     * @param string $actionType 操作类型
+     * @param array $statistics 统计数据
+     * @return void
+     */
+    protected function recordSkillStatistics(PetActiveSkill $activeSkill, string $actionType, array $statistics): void
+    {
+        $config = $activeSkill->config;
+        
+        if (!isset($config['statistics'])) {
+            $config['statistics'] = [];
+        }
+        
+        $config['statistics'][] = [
+            'action_type' => $actionType,
+            'timestamp' => now()->toDateTimeString(),
+            'data' => $statistics
+        ];
+        
+        // 只保留最近10条统计记录
+        if (count($config['statistics']) > 10) {
+            $config['statistics'] = array_slice($config['statistics'], -10);
+        }
+        
+        $activeSkill->config = $config;
+        $activeSkill->save();
+    }
+}

+ 143 - 30
app/Module/Pet/Logic/PetLogic.php

@@ -581,40 +581,13 @@ class PetLogic
         // 根据技能名称执行不同的效果
         switch ($skill->skill_name) {
             case '自动收菜':
-                // 调用农场模块的收获接口
-                // 这里需要根据实际项目中的农场模块接口进行实现
-                // 暂时使用占位代码
-                $harvestResult = [
-                    'success' => true,
-                    'harvested_count' => mt_rand(1, 5),
-                    'items' => []
-                ];
-
-                return $harvestResult;
+                return $this->activateAutoHarvestSkill($pet, $skill, $params);
 
             case '自动播种':
-                // 调用农场模块的播种接口
-                // 这里需要根据实际项目中的农场模块接口进行实现
-                // 暂时使用占位代码
-                $plantResult = [
-                    'success' => true,
-                    'planted_count' => mt_rand(1, 5),
-                    'seeds' => []
-                ];
-
-                return $plantResult;
+                return $this->activateAutoPlantSkill($pet, $skill, $params);
 
             case '灾害防护':
-                // 设置农场模块的灾害防护标志
-                // 这里需要根据实际项目中的农场模块接口进行实现
-                // 暂时使用占位代码
-                $protectResult = [
-                    'success' => true,
-                    'protected_type' => $params['disaster_type'] ?? 'all',
-                    'duration' => 3600 // 1小时
-                ];
-
-                return $protectResult;
+                return $this->activateDisasterProtectionSkill($pet, $skill, $params);
 
             default:
                 return [
@@ -624,6 +597,146 @@ class PetLogic
         }
     }
 
+    /**
+     * 激活自动收菜技能
+     *
+     * @param PetUser $pet 宠物对象
+     * @param PetSkill $skill 技能对象
+     * @param array $params 技能参数
+     * @return array 激活结果
+     */
+    protected function activateAutoHarvestSkill(PetUser $pet, PetSkill $skill, array $params): array
+    {
+        // 技能持续时间(默认2小时)
+        $duration = $params['duration'] ?? 7200; // 2小时
+        $endTime = now()->addSeconds($duration);
+
+        // 创建技能激活记录
+        $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
+            'pet_id' => $pet->id,
+            'skill_id' => $skill->id,
+            'skill_name' => $skill->skill_name,
+            'start_time' => now(),
+            'end_time' => $endTime,
+            'status' => 'active',
+            'config' => json_encode([
+                'auto_harvest' => true,
+                'check_interval' => 60, // 每分钟检查一次
+                'last_check_time' => now()->toDateTimeString()
+            ])
+        ]);
+
+        Log::info('自动收菜技能激活成功', [
+            'pet_id' => $pet->id,
+            'skill_id' => $skill->id,
+            'duration' => $duration,
+            'end_time' => $endTime->toDateTimeString(),
+            'active_skill_id' => $activeSkill->id
+        ]);
+
+        return [
+            'success' => true,
+            'skill_type' => 'auto_harvest',
+            'duration' => $duration,
+            'end_time' => $endTime->toDateTimeString(),
+            'message' => "自动收菜技能已激活,持续时间:{$duration}秒"
+        ];
+    }
+
+    /**
+     * 激活自动播种技能
+     *
+     * @param PetUser $pet 宠物对象
+     * @param PetSkill $skill 技能对象
+     * @param array $params 技能参数
+     * @return array 激活结果
+     */
+    protected function activateAutoPlantSkill(PetUser $pet, PetSkill $skill, array $params): array
+    {
+        // 技能持续时间(默认4小时)
+        $duration = $params['duration'] ?? 14400; // 4小时
+        $endTime = now()->addSeconds($duration);
+
+        // 创建技能激活记录
+        $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
+            'pet_id' => $pet->id,
+            'skill_id' => $skill->id,
+            'skill_name' => $skill->skill_name,
+            'start_time' => now(),
+            'end_time' => $endTime,
+            'status' => 'active',
+            'config' => json_encode([
+                'auto_plant' => true,
+                'check_interval' => 60, // 每分钟检查一次
+                'last_check_time' => now()->toDateTimeString(),
+                'preferred_seeds' => $params['preferred_seeds'] ?? [] // 优先使用的种子ID列表
+            ])
+        ]);
+
+        Log::info('自动播种技能激活成功', [
+            'pet_id' => $pet->id,
+            'skill_id' => $skill->id,
+            'duration' => $duration,
+            'end_time' => $endTime->toDateTimeString(),
+            'active_skill_id' => $activeSkill->id
+        ]);
+
+        return [
+            'success' => true,
+            'skill_type' => 'auto_plant',
+            'duration' => $duration,
+            'end_time' => $endTime->toDateTimeString(),
+            'message' => "自动播种技能已激活,持续时间:{$duration}秒"
+        ];
+    }
+
+    /**
+     * 激活灾害防护技能
+     *
+     * @param PetUser $pet 宠物对象
+     * @param PetSkill $skill 技能对象
+     * @param array $params 技能参数
+     * @return array 激活结果
+     */
+    protected function activateDisasterProtectionSkill(PetUser $pet, PetSkill $skill, array $params): array
+    {
+        // 技能持续时间(默认6小时)
+        $duration = $params['duration'] ?? 21600; // 6小时
+        $endTime = now()->addSeconds($duration);
+
+        // 创建技能激活记录
+        $activeSkill = \App\Module\Pet\Models\PetActiveSkill::create([
+            'pet_id' => $pet->id,
+            'skill_id' => $skill->id,
+            'skill_name' => $skill->skill_name,
+            'start_time' => now(),
+            'end_time' => $endTime,
+            'status' => 'active',
+            'config' => json_encode([
+                'disaster_protection' => true,
+                'protected_types' => $params['disaster_types'] ?? ['all'], // 防护的灾害类型
+                'check_interval' => 300, // 每5分钟检查一次
+                'last_check_time' => now()->toDateTimeString()
+            ])
+        ]);
+
+        Log::info('灾害防护技能激活成功', [
+            'pet_id' => $pet->id,
+            'skill_id' => $skill->id,
+            'duration' => $duration,
+            'end_time' => $endTime->toDateTimeString(),
+            'active_skill_id' => $activeSkill->id
+        ]);
+
+        return [
+            'success' => true,
+            'skill_type' => 'disaster_protection',
+            'duration' => $duration,
+            'end_time' => $endTime->toDateTimeString(),
+            'message' => "灾害防护技能已激活,持续时间:{$duration}秒"
+        ];
+    }
+
     /**
      * 变更宠物状态
      *

+ 261 - 0
app/Module/Pet/Models/PetActiveSkill.php

@@ -0,0 +1,261 @@
+<?php
+
+namespace App\Module\Pet\Models;
+
+use UCore\ModelCore;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * 宠物激活技能模型
+ *
+ * 用于记录宠物当前激活的技能状态,支持定时任务自动处理
+ *
+ * @property int $id 主键ID
+ * @property int $pet_id 宠物ID
+ * @property int $skill_id 技能ID
+ * @property string $skill_name 技能名称
+ * @property \Carbon\Carbon $start_time 开始时间
+ * @property \Carbon\Carbon $end_time 结束时间
+ * @property string $status 状态:active-生效中,expired-已过期,cancelled-已取消
+ * @property array $config 技能配置信息
+ * @property \Carbon\Carbon $created_at 创建时间
+ * @property \Carbon\Carbon $updated_at 更新时间
+ */
+class PetActiveSkill extends ModelCore
+{
+    /**
+     * 数据表名
+     *
+     * @var string
+     */
+    protected $table = 'pet_active_skills';
+
+    /**
+     * 可批量赋值的属性
+     *
+     * @var array<int, string>
+     */
+    protected $fillable = [
+        'pet_id',
+        'skill_id',
+        'skill_name',
+        'start_time',
+        'end_time',
+        'status',
+        'config',
+    ];
+
+    /**
+     * 属性类型转换
+     *
+     * @var array<string, string>
+     */
+    protected $casts = [
+        'start_time' => 'datetime',
+        'end_time' => 'datetime',
+        'config' => 'array',
+        'created_at' => 'datetime',
+        'updated_at' => 'datetime',
+    ];
+
+    /**
+     * 技能状态常量
+     */
+    const STATUS_ACTIVE = 'active';      // 生效中
+    const STATUS_EXPIRED = 'expired';    // 已过期
+    const STATUS_CANCELLED = 'cancelled'; // 已取消
+
+    /**
+     * 关联宠物
+     *
+     * @return BelongsTo
+     */
+    public function pet(): BelongsTo
+    {
+        return $this->belongsTo(PetUser::class, 'pet_id');
+    }
+
+    /**
+     * 关联技能配置
+     *
+     * @return BelongsTo
+     */
+    public function skill(): BelongsTo
+    {
+        return $this->belongsTo(PetSkill::class, 'skill_id');
+    }
+
+    /**
+     * 检查技能是否仍然有效
+     *
+     * @return bool
+     */
+    public function isActive(): bool
+    {
+        return $this->status === self::STATUS_ACTIVE && $this->end_time > now();
+    }
+
+    /**
+     * 检查技能是否已过期
+     *
+     * @return bool
+     */
+    public function isExpired(): bool
+    {
+        return $this->end_time <= now();
+    }
+
+    /**
+     * 获取剩余时间(秒)
+     *
+     * @return int
+     */
+    public function getRemainingSeconds(): int
+    {
+        if ($this->isExpired()) {
+            return 0;
+        }
+
+        return now()->diffInSeconds($this->end_time);
+    }
+
+    /**
+     * 标记技能为已过期
+     *
+     * @return bool
+     */
+    public function markAsExpired(): bool
+    {
+        $this->status = self::STATUS_EXPIRED;
+        return $this->save();
+    }
+
+    /**
+     * 取消技能
+     *
+     * @return bool
+     */
+    public function cancel(): bool
+    {
+        $this->status = self::STATUS_CANCELLED;
+        return $this->save();
+    }
+
+    /**
+     * 更新最后检查时间
+     *
+     * @return bool
+     */
+    public function updateLastCheckTime(): bool
+    {
+        $config = $this->config;
+        $config['last_check_time'] = now()->toDateTimeString();
+        $this->config = $config;
+        return $this->save();
+    }
+
+    /**
+     * 获取最后检查时间
+     *
+     * @return \Carbon\Carbon|null
+     */
+    public function getLastCheckTime(): ?\Carbon\Carbon
+    {
+        $lastCheckTime = $this->config['last_check_time'] ?? null;
+        return $lastCheckTime ? \Carbon\Carbon::parse($lastCheckTime) : null;
+    }
+
+    /**
+     * 检查是否需要执行检查
+     *
+     * @return bool
+     */
+    public function shouldCheck(): bool
+    {
+        if (!$this->isActive()) {
+            return false;
+        }
+
+        $lastCheckTime = $this->getLastCheckTime();
+        if (!$lastCheckTime) {
+            return true;
+        }
+
+        $checkInterval = $this->config['check_interval'] ?? 60; // 默认60秒
+        return now()->diffInSeconds($lastCheckTime) >= $checkInterval;
+    }
+
+    /**
+     * 获取技能配置中的特定值
+     *
+     * @param string $key 配置键名
+     * @param mixed $default 默认值
+     * @return mixed
+     */
+    public function getConfigValue(string $key, $default = null)
+    {
+        return $this->config[$key] ?? $default;
+    }
+
+    /**
+     * 设置技能配置中的特定值
+     *
+     * @param string $key 配置键名
+     * @param mixed $value 配置值
+     * @return bool
+     */
+    public function setConfigValue(string $key, $value): bool
+    {
+        $config = $this->config;
+        $config[$key] = $value;
+        $this->config = $config;
+        return $this->save();
+    }
+
+    /**
+     * 查询生效中的技能
+     *
+     * @param \Illuminate\Database\Eloquent\Builder $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function scopeActive($query)
+    {
+        return $query->where('status', self::STATUS_ACTIVE)
+                    ->where('end_time', '>', now());
+    }
+
+    /**
+     * 查询已过期的技能
+     *
+     * @param \Illuminate\Database\Eloquent\Builder $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function scopeExpired($query)
+    {
+        return $query->where('end_time', '<=', now());
+    }
+
+    /**
+     * 查询特定技能类型
+     *
+     * @param \Illuminate\Database\Eloquent\Builder $query
+     * @param string $skillName 技能名称
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function scopeBySkillName($query, string $skillName)
+    {
+        return $query->where('skill_name', $skillName);
+    }
+
+    /**
+     * 查询特定宠物的技能
+     *
+     * @param \Illuminate\Database\Eloquent\Builder $query
+     * @param int $petId 宠物ID
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function scopeByPet($query, int $petId)
+    {
+        return $query->where('pet_id', $petId);
+    }
+}

+ 2 - 0
app/Module/Pet/Providers/PetServiceProvider.php

@@ -3,6 +3,7 @@
 namespace App\Module\Pet\Providers;
 
 use App\Module\Pet\Commands\GeneratePetJsonCommand;
+use App\Module\Pet\Console\ProcessActiveSkillsCommand;
 use App\Module\Pet\Events\PetBattleEvent;
 use App\Module\Pet\Events\PetCreatedEvent;
 use App\Module\Pet\Events\PetLevelUpEvent;
@@ -60,6 +61,7 @@ class PetServiceProvider extends ServiceProvider
         if ($this->app->runningInConsole()) {
             $this->commands([
                 \App\Module\Pet\Commands\GeneratePetJsonCommand::class, // 生成宠物配置JSON数据命令
+                \App\Module\Pet\Console\ProcessActiveSkillsCommand::class, // 处理宠物激活技能命令
             ]);
         }
     }

+ 3 - 0
app/Module/Pet/Services/PetService.php

@@ -300,6 +300,9 @@ class PetService
             }
 
             // 获取技能信息
+            /**
+             * @var PetSkill $skill  
+             */
             $skill = PetSkill::find($skillId);
             if (!$skill) {
                 throw new Exception("技能不存在");

+ 55 - 0
database/migrations/2024_12_30_174500_create_pet_active_skills_table.php

@@ -0,0 +1,55 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/**
+ * 创建宠物激活技能表
+ */
+return new class extends Migration
+{
+    /**
+     * 运行迁移
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('kku_pet_active_skills', function (Blueprint $table) {
+            $table->id()->comment('主键ID');
+            $table->unsignedBigInteger('pet_id')->comment('宠物ID');
+            $table->unsignedInteger('skill_id')->comment('技能ID');
+            $table->string('skill_name', 50)->comment('技能名称');
+            $table->timestamp('start_time')->comment('开始时间');
+            $table->timestamp('end_time')->comment('结束时间');
+            $table->enum('status', ['active', 'expired', 'cancelled'])
+                  ->default('active')
+                  ->comment('状态:active-生效中,expired-已过期,cancelled-已取消');
+            $table->json('config')->nullable()->comment('技能配置信息');
+            $table->timestamps();
+
+            // 索引
+            $table->index(['pet_id', 'status'], 'idx_pet_status');
+            $table->index(['skill_name', 'status'], 'idx_skill_status');
+            $table->index(['end_time', 'status'], 'idx_end_time_status');
+            $table->index('start_time', 'idx_start_time');
+
+            // 外键约束
+            $table->foreign('pet_id')->references('id')->on('kku_pet_users')->onDelete('cascade');
+            $table->foreign('skill_id')->references('id')->on('kku_pet_skills')->onDelete('cascade');
+
+            $table->comment('宠物激活技能表 - 记录宠物当前激活的技能状态');
+        });
+    }
+
+    /**
+     * 回滚迁移
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('kku_pet_active_skills');
+    }
+};

+ 28 - 0
database/sql/create_pet_active_skills_table.sql

@@ -0,0 +1,28 @@
+-- 创建宠物激活技能表
+-- 用于记录宠物当前激活的技能状态,支持定时任务自动处理
+
+CREATE TABLE `kku_pet_active_skills` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `pet_id` bigint(20) unsigned NOT NULL COMMENT '宠物ID',
+  `skill_id` int(10) unsigned NOT NULL COMMENT '技能ID',
+  `skill_name` varchar(50) NOT NULL COMMENT '技能名称',
+  `start_time` timestamp NOT NULL COMMENT '开始时间',
+  `end_time` timestamp NOT NULL COMMENT '结束时间',
+  `status` enum('active','expired','cancelled') NOT NULL DEFAULT 'active' COMMENT '状态:active-生效中,expired-已过期,cancelled-已取消',
+  `config` json DEFAULT NULL COMMENT '技能配置信息',
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_pet_status` (`pet_id`,`status`),
+  KEY `idx_skill_status` (`skill_name`,`status`),
+  KEY `idx_end_time_status` (`end_time`,`status`),
+  KEY `idx_start_time` (`start_time`),
+  CONSTRAINT `kku_pet_active_skills_pet_id_foreign` FOREIGN KEY (`pet_id`) REFERENCES `kku_pet_users` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `kku_pet_active_skills_skill_id_foreign` FOREIGN KEY (`skill_id`) REFERENCES `kku_pet_skills` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='宠物激活技能表 - 记录宠物当前激活的技能状态';
+
+-- 插入示例数据(可选)
+-- INSERT INTO `kku_pet_active_skills` (`pet_id`, `skill_id`, `skill_name`, `start_time`, `end_time`, `status`, `config`) VALUES
+-- (1, 1, '自动收菜', NOW(), DATE_ADD(NOW(), INTERVAL 2 HOUR), 'active', '{"auto_harvest": true, "check_interval": 60, "last_check_time": "2024-12-30 17:45:00"}'),
+-- (2, 2, '自动播种', NOW(), DATE_ADD(NOW(), INTERVAL 4 HOUR), 'active', '{"auto_plant": true, "check_interval": 60, "last_check_time": "2024-12-30 17:45:00", "preferred_seeds": [1001, 1002]}'),
+-- (3, 3, '灾害防护', NOW(), DATE_ADD(NOW(), INTERVAL 6 HOUR), 'active', '{"disaster_protection": true, "protected_types": ["all"], "check_interval": 300, "last_check_time": "2024-12-30 17:45:00"}');

+ 2 - 0
routes/console.php

@@ -14,3 +14,5 @@ Artisan::command('inspire', function () {
 \Illuminate\Support\Facades\Schedule::command('farm:update-crop-growth')->everyMinute();
 // 随机生成灾害
 \Illuminate\Support\Facades\Schedule::command(\App\Module\Farm\Commands\GenerateDisastersCommand::class)->everyMinute();
+// 每分钟处理宠物激活技能
+\Illuminate\Support\Facades\Schedule::command('pet:process-active-skills')->everyMinute();