|
|
@@ -0,0 +1,162 @@
|
|
|
+# 修复物品冻结拆堆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同步问题。通过改进临时数据存储机制,确保同一物品的多个堆叠变更都能正确同步到客户端,提升了系统的数据一致性和用户体验。
|