# 修复Mex模块取消挂单没有解冻的问题 ## 任务信息 - **时间**: 2025年06月20日 17:59-18:06 - **类型**: Bug修复 - **模块**: Mex(农贸市场) - **状态**: ✅ 已完成 ## 问题描述 用户在农贸市场创建卖出订单时,系统会冻结相应的物品。但是当用户取消挂单时,系统只是将订单状态改为取消,没有解冻之前冻结的物品,导致用户物品被永久冻结。 ## 问题分析 1. **原始代码问题**:`MexOrderLogic::cancelOrder`方法只更新订单状态,没有处理解冻逻辑 2. **影响范围**:所有取消的卖出订单都会导致物品永久冻结 3. **业务影响**:用户体验差,物品无法正常使用 ## 解决方案 ### 1. 修改取消订单逻辑 在`app/Module/Mex/Logic/MexOrderLogic.php`中: - 添加事务检查确保数据一致性 - 根据订单类型分别处理解冻逻辑: - 卖出订单:解冻物品 - 买入订单:解冻资金(占位实现) ### 2. 新增解冻方法 - `unfreezeOrderItems()`: 解冻卖出订单相关的物品 - `unfreezeOrderFunds()`: 解冻买入订单相关的资金 ### 3. 修改服务层 在`app/Module/Mex/Services/MexOrderService.php`中: - 确保取消订单操作在数据库事务中执行 - 添加异常处理 ## 核心代码修改 ### MexOrderLogic::cancelOrder方法 ```php public static function cancelOrder(int $userId, int $orderId): array { // 检查事务状态,确保调用者已开启事务 \UCore\Db\Helper::check_tr(); $order = MexOrder::where('id', $orderId)->where('user_id', $userId)->first(); if (!$order) { return ['success' => false, 'message' => '订单不存在']; } if ($order->status !== OrderStatus::PENDING) { return ['success' => false, 'message' => '只能取消等待中的订单']; } try { // 1. 更新订单状态为取消 $order->update(['status' => OrderStatus::CANCELLED]); // 2. 处理解冻逻辑 if ($order->order_type === OrderType::SELL) { // 卖出订单:解冻物品 $unfreezeResult = self::unfreezeOrderItems($order); if (!$unfreezeResult['success']) { return ['success' => false, 'message' => '解冻物品失败:' . $unfreezeResult['message']]; } } elseif ($order->order_type === OrderType::BUY) { // 买入订单:解冻资金 $unfreezeResult = self::unfreezeOrderFunds($order); if (!$unfreezeResult['success']) { return ['success' => false, 'message' => '解冻资金失败:' . $unfreezeResult['message']]; } } return ['success' => true, 'message' => '订单已取消']; } catch (\Exception $e) { return ['success' => false, 'message' => '取消订单失败:' . $e->getMessage()]; } } ``` ### 解冻物品方法 ```php private static function unfreezeOrderItems(MexOrder $order): array { try { // 查找该订单相关的冻结记录 $freezeLogs = \App\Module\GameItems\Models\ItemFreezeLog::where('source_id', $order->id) ->where('source_type', 'mex_sell_order') ->where('action_type', \App\Module\GameItems\Enums\FREEZE_ACTION_TYPE::FREEZE) ->get(); if ($freezeLogs->isEmpty()) { return ['success' => true, 'message' => '没有找到需要解冻的物品']; } $unfrozenCount = 0; foreach ($freezeLogs as $freezeLog) { try { // 使用ItemService解冻物品 \App\Module\GameItems\Services\ItemService::unfreezeItem($freezeLog->id); $unfrozenCount++; } catch (\Exception $e) { // 记录错误但继续处理其他冻结记录 \Log::error("解冻物品失败", [ 'order_id' => $order->id, 'freeze_log_id' => $freezeLog->id, 'error' => $e->getMessage() ]); } } return [ 'success' => true, 'message' => "成功解冻 {$unfrozenCount} 个冻结记录", 'unfrozen_count' => $unfrozenCount ]; } catch (\Exception $e) { return ['success' => false, 'message' => $e->getMessage()]; } } ``` ## 测试验证 ### 1. 手动测试 创建了完整的测试脚本`app/Module/Mex/Tests/manual_cancel_test.php` ### 2. 实际验证 使用真实数据进行测试: - 用户1001,萝卜(物品ID=2) - 创建卖出订单20个,价格10.5 - 验证物品冻结状态 - 取消订单 - 验证物品解冻状态 ### 3. 测试结果 ✅ **订单状态正确更新**:PENDING → CANCELLED ✅ **物品正确解冻**:is_frozen=1 → is_frozen=0 ✅ **解冻日志正确创建**:action_type=2(解冻) ✅ **物品数量正确**:用户物品完全恢复可用状态 ## 文件变更 - `app/Module/Mex/Logic/MexOrderLogic.php` - 修改取消订单逻辑 - `app/Module/Mex/Services/MexOrderService.php` - 添加事务处理 - `app/Module/Mex/Tests/MexOrderCancelTest.php` - 新增单元测试 - `app/Module/Mex/Tests/manual_cancel_test.php` - 新增手动测试脚本 ## 提交信息 ``` 修复Mex模块取消挂单时没有解冻物品的问题 - 修改MexOrderLogic::cancelOrder方法,添加解冻逻辑 - 卖出订单取消时自动解冻相关物品 - 买入订单取消时处理资金解冻(当前为占位实现) - 添加unfreezeOrderItems和unfreezeOrderFunds私有方法 - 修改MexOrderService::cancelOrder在事务中执行 - 添加完整的测试用例和手动测试脚本 - 通过实际测试验证功能正常工作 ``` ## 后续完善(已完成) ### Fund模块Bug修复 发现并修复了Fund模块中的参数类型错误: - `Circulation::handle`方法的`$amount`参数类型从`int`改为`float` - `User::handle`方法的`$amount`参数类型从`int`改为`float` 这些方法在Fund模块全面采用小数模式后,应该接受`float`类型参数,但代码中仍然声明为`int`,导致类型不匹配错误。 ### 买入订单资金冻结功能实现 完善了买入订单的真正资金冻结功能: 1. **创建订单时冻结资金**: - 在`createBuyOrder`方法中添加`freezeOrderFunds`调用 - 将资金从钻石账户转移到钻石冻结账户 - 使用Fund模块的`circulation`方法实现 2. **取消订单时解冻资金**: - 修改`unfreezeOrderFunds`方法实现真正的资金解冻 - 将资金从钻石冻结账户转移回钻石账户 - 添加余额验证和错误处理 3. **测试验证结果**: - ✅ 买入订单创建:钻石账户1000→500,钻石冻结账户0→500 - ✅ 买入订单取消:钻石冻结账户500→0,钻石账户500→1000 - ✅ 资金流转记录完整,包含冻结和解冻操作 - ✅ 订单状态正确更新:PENDING→CANCELLED ## 注意事项 1. **数据一致性**:所有操作都在事务中执行 2. **错误处理**:解冻失败时记录日志但不中断整个流程 3. **向后兼容**:不影响现有的订单创建和撮合逻辑 4. **类型安全**:修复了Fund模块的参数类型声明错误 5. **真实冻结**:买入订单现在会真正冻结和解冻用户资金 ## 技术实现要点 1. **Fund模块集成**:正确使用Fund模块的`circulation`方法进行资金流转 2. **账户类型映射**:通过`FundLogic`获取可用账户和冻结账户类型 3. **精度处理**:使用Fund模块的精度验证和格式化功能 4. **事务管理**:确保资金操作的原子性和一致性