# 修复物品冻结拆堆lastdata同步问题 **任务时间**: 2025年06月26日 18:02 **任务类型**: 功能修复 **模块**: GameItems, Game **状态**: ✅ 已完成 ## 问题描述 用户反馈:Mex模块挂单时,物品冻结产生了拆堆,但同步物品状态只同步了一个物品堆的,应该是两个物品堆。 ### 问题分析 通过 `debug:reproduce-error` 命令重放请求 69010888,发现: 1. **拆堆场景**:当用户有10000个萝卜,挂单冻结100个时,会发生拆堆: - 原堆叠:数量从10000减少到9900 - 新冻结堆叠:创建100个冻结状态的物品 2. **同步问题**:在修复前,lastdata中只显示一个物品堆的信息,缺少另一个堆叠的状态 3. **根本原因**:`ItemTemp::handleItemQuantityChanged` 方法使用 `item_id` 作为键存储临时数据,导致同一物品的多个堆叠变更事件相互覆盖 ## 解决方案 ### 1. 修改临时数据存储键 **文件**: `app/Module/Game/Logics/ItemTemp.php` ```php // 修改前:使用物品ID作为键,导致同一物品的不同堆叠相互覆盖 $userItemsTemp[$event->itemId] = $itemData; // 修改后:使用用户物品记录ID作为键,避免覆盖 $userItemsTemp[$event->userItemId] = $itemData; ``` ### 2. 调整数据获取方法 **文件**: `app/Module/Game/Logics/ItemTemp.php` - 修改 `getUserItemChanges()` 方法返回所有物品堆的变更记录 - 新增 `getUserItemChangeByUserItemId()` 方法支持按用户物品记录ID查询 - 调整 `getUserItemChange()` 方法返回指定物品ID的所有堆叠变更 ### 3. 事件触发机制验证 确认 `ItemFreeze::freezeNormalItem` 方法在拆堆时正确触发两个事件: 1. 原堆叠数量减少事件(`change_type: quantity_decrease`) 2. 新冻结堆叠创建事件(`change_type: frozen_item_create`) ## 修复验证 ### 1. 请求重放验证 使用 `php artisan debug:reproduce-error 69010888` 重放原问题请求: **修复前响应**: ```json { "lastData": { "items": [ { "itemId": "2", "quantity": "100", "isFrozen": true, "iuId": "1047" } ] } } ``` **修复后响应**: ```json { "lastData": { "items": [ { "itemId": "2", "quantity": "9600", "iuId": "1042" }, { "itemId": "2", "quantity": "100", "isFrozen": true, "iuId": "1055" } ] } } ``` ### 2. 单元测试验证 新增测试文件: - `tests/Unit/GameItems/ItemFreezeSyncFixTest.php` - `tests/Unit/GameItems/ItemFreezeSplitStackSyncTest.php` 测试覆盖场景: 1. **拆堆冻结**:部分冻结导致拆堆,验证两个物品堆状态同步 2. **全部冻结**:全部冻结不拆堆,验证单个物品堆状态变更 所有测试通过,验证修复正确性。 ## 技术细节 ### 拆堆逻辑分析 当执行部分冻结时,`ItemFreeze::freezeNormalItem` 方法会: 1. **检查可用数量**:查找用户可用的物品堆叠 2. **判断是否拆堆**: - 如果冻结数量 == 可用数量:全部冻结,直接标记 `is_frozen=true` - 如果冻结数量 < 可用数量:部分冻结,需要拆堆 3. **拆堆操作**: - 减少原堆叠数量:`quantity = 原数量 - 冻结数量` - 创建新冻结堆叠:`quantity = 冻结数量, is_frozen = true` 4. **事件触发**: - 原堆叠:触发数量减少事件(冻结状态不变) - 新堆叠:触发冻结物品创建事件(从无到冻结状态) ### 同步机制 修复后的同步流程: 1. 冻结操作触发多个 `ItemQuantityChanged` 事件 2. `ItemTemp` 使用 `user_item_id` 作为键存储每个堆叠的变更 3. `AppGameProtobufResponseListener` 获取所有变更记录 4. 构建 `lastData` 包含所有变更的物品堆信息 ## 影响范围 - **正面影响**:修复拆堆场景下的状态同步问题,客户端能正确获取所有物品堆状态 - **兼容性**:向后兼容,不影响现有功能 - **性能**:轻微提升,避免了数据覆盖导致的信息丢失 ## 提交信息 ``` 修复物品冻结拆堆时lastdata同步问题 问题描述: - Mex模块挂单时,物品冻结产生拆堆,但只同步了一个物品堆的状态 - 原因是ItemTemp使用item_id作为键存储临时数据,导致同一物品的多个堆叠相互覆盖 修复内容: 1. 修改ItemTemp::handleItemQuantityChanged方法,使用user_item_id作为键而不是item_id 2. 调整getUserItemChanges方法返回所有物品堆的变更记录 3. 新增getUserItemChangeByUserItemId方法支持按用户物品记录ID查询 4. 添加测试用例验证拆堆场景下的双堆叠同步功能 测试验证: - 通过debug:reproduce-error命令验证,拆堆时lastdata正确包含两个物品堆信息 - 新增单元测试覆盖拆堆和全部冻结两种场景 ``` ## 总结 成功修复了物品冻结拆堆时的lastdata同步问题。通过改进临时数据存储机制,确保同一物品的多个堆叠变更都能正确同步到客户端,提升了系统的数据一致性和用户体验。