# 浇水成功 Response 的 LastData 缺失 DataLands 修复说明 ## 问题描述 用户浇水成功后,Response 的 LastData 中缺失 DataLands 信息,导致客户端无法及时更新土地状态。 ## 问题原因分析 通过代码分析发现,系统使用了土地暂存系统(LandTemp)来处理土地状态变更,然后通过 `AppGameProtobufResponseListener` 监听器在响应中自动添加 LastData。但是浇水操作成功后,存在以下问题: 1. **灾害清理时未触发土地状态变更事件**:在 `CropLogic::clearDisaster` 方法中,虽然更新了土地状态,但没有触发 `LandStatusChangedEvent` 事件。 2. **缺少灾害清理事件监听器**:虽然触发了 `DisasterClearedEvent` 事件,但没有相应的监听器来处理土地暂存数据的更新。 3. **灾害生成时也存在同样问题**:在 `DisasterLogic::applyDisastersToCrop` 方法中,更新土地状态时也没有触发相应的事件。 ## 修复方案 ### 1. 修复灾害清理时的土地状态变更事件触发 **文件**: `app/Module/Farm/Logics/CropLogic.php` 在 `clearDisaster` 方法中添加土地状态变更事件的触发: ```php // 如果没有其他活跃灾害,更新土地状态 $oldLandStatus = $land->status; if (!$hasActiveDisaster) { $land->status = LAND_STATUS::PLANTING->value; } // 保存更改 $crop->save(); $land->save(); // 如果土地状态发生了变化,触发土地状态变更事件 if ($oldLandStatus !== $land->status) { event(new LandStatusChangedEvent($userId, $landId, $oldLandStatus, $land->status)); } ``` ### 2. 创建灾害清理事件监听器 **文件**: `app/Module/Game/Listeners/DisasterClearedListener.php` 创建专门的监听器来处理 `DisasterClearedEvent` 事件,更新土地暂存数据: ```php public function handle(DisasterClearedEvent $event): void { // 获取土地信息并构建暂存数据 $land = $event->crop->land; $landStatusData = [ 'land_id' => $land->id, 'status' => $land->status, 'crop' => $event->crop->toArray(), 'updated_at' => now()->toDateTimeString() ]; // 存储到暂存系统 $tempKey = LandTemp::TEMP_KEY_STATUS_PREFIX . $event->userId; $userLandsStatusTemp = \UCore\Helper\Cache::get($tempKey, []); $userLandsStatusTemp[$land->id] = \App\Module\Game\Dtos\LandStatusTempDto::fromArray($landStatusData); \UCore\Helper\Cache::put($tempKey, $userLandsStatusTemp, LandTemp::TEMP_TTL); } ``` ### 3. 注册灾害清理事件监听器 **文件**: `app/Module/Game/Providers/GameServiceProvider.php` 在 Game 模块的服务提供者中注册新的监听器: ```php // 注册灾害清理事件监听器 Event::listen( DisasterClearedEvent::class, DisasterClearedListener::class ); ``` ### 4. 修复灾害生成时的土地状态变更事件触发 **文件**: `app/Module/Farm/Logics/DisasterLogic.php` 在 `applyDisastersToCrop` 方法中添加土地状态变更事件的触发: ```php // 更新土地状态为灾害 $land = $crop->land; $oldLandStatus = $land->status; $land->status = LAND_STATUS::DISASTER->value; // 保存更改 $crop->save(); $land->save(); // 如果土地状态发生了变化,触发土地状态变更事件 if ($oldLandStatus !== $land->status) { event(new \App\Module\Farm\Events\LandStatusChangedEvent($crop->user_id, $land->id, $oldLandStatus, $land->status)); } ``` ## 修复效果 修复后,当用户进行浇水操作时: 1. **成功浇水**: - 灾害被清理,土地状态从 `DISASTER` 更新为 `PLANTING` - 触发 `LandStatusChangedEvent` 和 `DisasterClearedEvent` 事件 - 监听器更新土地暂存数据 - `AppGameProtobufResponseListener` 自动将暂存数据添加到 Response 的 LastData 中 - 客户端收到完整的土地状态更新 2. **失败浇水**: - 物品被消耗,但灾害状态不变 - 不触发土地状态变更事件 - 返回错误信息 ## 数据流程 ``` 用户浇水操作 ↓ WateringHandler 处理请求 ↓ CropService::removeDisasterWithItem (概率判断) ↓ DisasterRemovalLogic::removeDisaster ↓ CropLogic::clearDisaster ↓ 触发 LandStatusChangedEvent + DisasterClearedEvent ↓ LandStatusChangedListener + DisasterClearedListener 处理事件 ↓ 更新土地暂存数据 (LandTemp) ↓ AppGameProtobufResponseListener 监听响应事件 ↓ 从暂存数据构建 LastData.lands ↓ 客户端收到包含 DataLands 的响应 ``` ## 测试验证 创建了功能测试 `tests/Feature/WateringResponseTest.php` 来验证: 1. 浇水成功后 Response 包含 LastData.lands 2. 土地状态正确更新 3. 灾害状态正确清理 4. 浇水失败时的错误处理 ## 相关文件 ### 修改的文件 - `app/Module/Farm/Logics/CropLogic.php` - `app/Module/Farm/Logics/DisasterLogic.php` - `app/Module/Game/Providers/GameServiceProvider.php` ### 新增的文件 - `app/Module/Game/Listeners/DisasterClearedListener.php` - `tests/Feature/WateringResponseTest.php` ## 注意事项 1. **事件触发顺序**:确保在保存数据库更改后再触发事件 2. **状态比较**:只有在土地状态真正发生变化时才触发事件,避免不必要的处理 3. **异常处理**:监听器中包含完整的异常处理,确保单个事件处理失败不影响整体流程 4. **缓存一致性**:暂存数据的 TTL 设置合理,避免数据过期导致的不一致 ## 后续优化建议 1. 考虑将土地状态变更事件的触发逻辑抽取为通用方法 2. 添加更多的单元测试覆盖边界情况 3. 监控事件处理的性能,确保不影响响应速度 4. 考虑添加事件处理失败的重试机制