宠物生活技能LastData修复
任务概述
修复宠物生活技能使用成功后,LastData没有进行宠物和生活技能状态同步的问题。
问题分析
问题现象
- 宠物生活技能使用成功,技能生效了
- 但是LastData没有包含宠物状态数据
- 前端无法获取到宠物的最新状态(如体力消耗、技能冷却等)
问题根因
通过代码分析发现:
- PetLogic::useSkill 方法在技能使用成功后触发了
PetSkillUsedEvent 事件
- Game模块 有处理其他宠物事件的监听器:
PetCreatedListener - 处理宠物创建事件
PetUpdateListener - 处理宠物更新事件
PetStatusChangedListener - 处理宠物状态变更事件
- 但是缺少
PetSkillUsedListener 来监听 PetSkillUsedEvent 事件
- AppGameProtobufResponseListener 统一处理LastData,但没有获取到宠物技能使用后的数据
事件流程分析
正常流程应该是:
- 用户调用宠物生活技能使用接口
- PetLogic::useSkill 执行技能逻辑,消耗体力,记录日志
- 触发 PetSkillUsedEvent 事件
- PetSkillUsedListener 监听到事件,调用 PetTemp::handlePetSkillUsed
- PetTemp 将宠物最新状态存储到临时缓存
- AppGameProtobufResponseListener 从临时缓存获取宠物数据,设置到LastData
- 前端收到包含宠物状态的LastData
实际流程中第4-6步缺失,导致LastData中没有宠物状态数据。
修复内容
1. 创建PetSkillUsedListener监听器
文件: app/Module/Game/Listeners/PetSkillUsedListener.php
- 监听
PetSkillUsedEvent 事件
- 调用
PetTemp::handlePetSkillUsed 处理宠物数据暂存
- 添加完整的错误处理和日志记录
- 与其他宠物事件监听器保持一致的结构
2. 在PetTemp中添加处理方法
文件: app/Module/Game/Logics/PetTemp.php
- 添加
handlePetSkillUsed 方法
- 复用现有的
handlePetFullData 方法进行数据处理
- 确保宠物技能使用后的完整状态被正确存储
3. 注册事件监听器
文件: app/Module/Game/Providers/GameServiceProvider.php
- 添加
PetSkillUsedListener 的import
- 添加
PetSkillUsedEvent 的import
- 在boot方法中注册事件监听器映射
修复原理
事件驱动架构
宠物模块使用事件驱动架构实现模块间通信:
- 宠物模块负责业务逻辑和事件发布
- Game模块负责数据暂存和LastData处理
- AppGame模块负责统一的LastData响应
数据流转机制
- 事件触发: 宠物技能使用成功后触发事件
- 数据暂存: Game模块监听事件,将宠物最新状态存储到临时缓存
- 数据同步: AppGameProtobufResponseListener从临时缓存获取数据,设置到LastData
- 前端更新: 前端收到LastData,更新宠物状态显示
一致性保证
- 所有宠物相关事件都通过统一的PetTemp处理
- 使用相同的数据格式和存储机制
- 确保事件处理的原子性和一致性
测试验证
修复后需要验证以下场景:
宠物生活技能使用
- 使用宠物生活技能
- 验证LastData中包含宠物状态数据
- 验证宠物体力、技能冷却等状态正确更新
多技能连续使用
- 连续使用多个宠物技能
- 验证每次使用后LastData都正确更新
- 验证宠物状态累积变化正确
技能使用失败场景
- 体力不足时使用技能
- 技能冷却期间使用技能
- 验证失败时不触发LastData更新
其他宠物事件
- 验证宠物创建、更新、状态变更等其他事件仍正常工作
- 确保新增监听器不影响现有功能
影响范围
- Game模块: 新增PetSkillUsedListener监听器
- 宠物模块: 无变更,继续发布PetSkillUsedEvent事件
- AppGame模块: 无变更,继续统一处理LastData
- 前端: 可以正常接收到宠物状态更新
后续修复
问题:宠物技能使用数据临时存储错误
在添加监听器后,发现新的错误:"Attempt to read property \"value\" on int"
根因分析:
- PetDtoFactory::createPetDataDto方法中试图访问
$pet->grade->value
- 但PetUser模型中grade字段只是integer类型,不是枚举
- 而status字段被正确转换为PetStatus枚举
修复内容:
- 创建缺失的PetGrade枚举
- 修复PetDtoFactory中的属性访问:
- grade字段直接使用integer值
- status字段使用枚举的value属性
- 修复DTO属性名称不匹配问题
问题:宠物技能使用数据临时存储中lifeSkills对象数组转换问题
在修复属性访问问题后,发现新的错误:"Attempt to read property \"skillId\" on array"
根因分析:
- PetTemp::handlePetFullData方法中使用
$petDto->toArray()复制属性
- BaseDto的toArray方法会将对象数组转换为普通数组
- 导致lifeSkills从PetLifeSkillDto对象数组变成了普通数组
- AppGameProtobufResponseListener访问
$lifeSkill->skillId时出错
修复内容:
- 修改PetTemp::handlePetFullData方法,直接复制对象属性而不使用toArray()
- 确保lifeSkills保持为PetLifeSkillDto对象数组类型
- 避免BaseDto::toArray()方法导致的对象类型转换问题
问题:PetStatusTempDto的fromCache方法无法正确恢复lifeSkills对象数组
在修复对象属性复制问题后,发现缓存恢复时仍然出现"Attempt to read property \"skillId\" on array"错误
根因分析:
- BaseDto的fromCache方法只是简单调用fromArray方法
- fromArray方法不能正确处理嵌套的对象数组(如lifeSkills)
- 当缓存中的数据被恢复时,lifeSkills数组中的PetLifeSkillDto对象变成了普通数组
- AppGameProtobufResponseListener访问
$lifeSkill->skillId时出错,因为$lifeSkill是数组而不是对象
修复内容:
- 为PetStatusTempDto创建自定义的fromCache方法
- 重写方法以正确处理lifeSkills数组的恢复
- 确保数组中的每个元素都被正确转换为PetLifeSkillDto对象
- 在AppGameProtobufResponseListener中添加兼容性处理,支持对象和数组两种格式
- 添加验证脚本验证修复效果
最终解决方案:兼容性处理
经过测试发现,虽然fromCache方法能够正确处理部分情况,但Laravel的缓存序列化机制仍然可能导致lifeSkills变成数组格式。
最终修复方案:
- 在AppGameProtobufResponseListener中添加兼容性处理
- 支持lifeSkills的对象和数组两种格式
- 确保无论缓存恢复时数据是什么格式,都能正确处理
提交信息
修复宠物生活技能使用后LastData缺少宠物状态同步问题:添加PetSkillUsedListener监听器
修复宠物技能使用数据临时存储错误:创建PetGrade枚举并修复PetDtoFactory属性访问问题
修复宠物技能使用数据临时存储中lifeSkills对象数组转换问题:直接复制对象属性避免toArray()导致的类型转换
添加AppGameProtobufResponseListener调试信息:诊断lifeSkills数据类型问题
完善PetStatusTempDto的fromCache方法:彻底修复lifeSkills对象数组的缓存恢复问题
移除调试信息并完善AppGameProtobufResponseListener的兼容性处理:支持lifeSkills的对象和数组两种格式
测试验证
通过Protobuf请求测试验证修复效果:
- 宠物技能使用成功
- LastData正确包含宠物状态数据
- 生活技能信息完整显示
- 不再出现"Attempt to read property \"skillId\" on array"错误
完成时间
2025-05-27 15:00(包含所有后续修复、验证和兼容性处理)