201759-修复Mex模块取消挂单没有解冻的问题.md 7.4 KB

修复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方法

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()];
    }
}

解冻物品方法

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. 事务管理:确保资金操作的原子性和一致性