Przeglądaj źródła

修复物品解冻BUG:当冻结堆被消耗后解冻数量错误的问题

- 修复解冻逻辑,确保解冻时恢复原始冻结数量
- 当冻结堆被部分/完全消耗时,从用户可用物品中补足差额
- 添加完整的交易日志记录,追踪补足转移过程
- 添加安全解冻方法,处理已被消耗的冻结堆情况
- 保持数据完整性,不删除冻结日志和堆记录
- 添加测试脚本验证修复效果
dongasai 6 miesięcy temu
rodzic
commit
c9ec7e68b1

+ 397 - 6
app/Module/GameItems/Logics/ItemFreeze.php

@@ -4,6 +4,7 @@ namespace App\Module\GameItems\Logics;
 
 use App\Module\GameItems\Enums\FREEZE_ACTION_TYPE;
 use App\Module\GameItems\Enums\FREEZE_REASON_TYPE;
+use App\Module\GameItems\Enums\TRANSACTION_TYPE;
 use App\Module\GameItems\Events\ItemQuantityChanged;
 use App\Module\GameItems\Models\Item;
 use App\Module\GameItems\Models\ItemFreezeLog;
@@ -320,14 +321,141 @@ class ItemFreeze
             throw new Exception("未找到冻结日志 {$freezeLogId} 对应的冻结物品");
         }
 
-        // 创建解冻日志
+        // 获取原始冻结数量和当前剩余数量
+        $originalFrozenQuantity = $freezeLog->quantity;
+        $currentQuantity = $frozenItem->quantity;
+
+        if ($currentQuantity <= 0) {
+            // 冻结堆已被完全消耗,无法解冻
+            throw new Exception("冻结日志 {$freezeLogId} 对应的冻结物品已被完全消耗,无法解冻");
+        }
+
+        // 检查是否需要补足差额
+        $shortageQuantity = $originalFrozenQuantity - $currentQuantity;
+
+        if ($shortageQuantity > 0) {
+            // 冻结堆被部分消耗,需要从用户其他可用物品中补足
+            $availableQuantity = self::getAvailableQuantity(
+                $frozenItem->user_id,
+                $frozenItem->item_id,
+                $frozenItem->instance_id
+            );
+
+            if ($availableQuantity < $shortageQuantity) {
+                throw new Exception(
+                    "解冻失败:原始冻结数量 {$originalFrozenQuantity},当前冻结剩余 {$currentQuantity}," .
+                    "需要补足 {$shortageQuantity},但用户可用数量只有 {$availableQuantity}"
+                );
+            }
+
+            // 从用户可用物品中扣除差额,补足到冻结堆
+            $availableItems = ItemUser::where('user_id', $frozenItem->user_id)
+                ->where('item_id', $frozenItem->item_id)
+                ->where('is_frozen', false)
+                ->whereNull('instance_id')
+                ->where('quantity', '>', 0)
+                ->orderBy('expire_at')
+                ->get();
+
+            $remainingShortage = $shortageQuantity;
+            $transferDetails = []; // 记录转移详情
+
+            foreach ($availableItems as $availableItem) {
+                if ($remainingShortage <= 0) break;
+
+                $deductQuantity = min($availableItem->quantity, $remainingShortage);
+                $oldQuantity = $availableItem->quantity;
+                $newQuantity = $oldQuantity - $deductQuantity;
+
+                // 更新可用物品数量
+                $availableItem->quantity = $newQuantity;
+                $availableItem->save();
+
+                // 记录转移详情
+                $transferDetails[] = [
+                    'from_user_item_id' => $availableItem->id,
+                    'transferred_quantity' => $deductQuantity,
+                    'old_quantity' => $oldQuantity,
+                    'new_quantity' => $newQuantity,
+                ];
+
+                // 记录交易日志(从可用物品扣除)
+                \App\Module\GameItems\Logics\Item::logTransaction(
+                    $frozenItem->user_id,
+                    $frozenItem->item_id,
+                    null,
+                    -$deductQuantity,
+                    TRANSACTION_TYPE::TRADE_OUT, // 使用交易失去类型
+                    'unfreeze_compensation',
+                    $freezeLogId,
+                    ["解冻补足转移:从可用物品转移 {$deductQuantity} 个到冻结堆"]
+                );
+
+                // 触发物品数量变更事件(可用物品减少)
+                event(new ItemQuantityChanged(
+                    $frozenItem->user_id,
+                    $frozenItem->item_id,
+                    null,
+                    $oldQuantity,
+                    $newQuantity,
+                    $availableItem->id,
+                    false, // 旧冻结状态:未冻结
+                    false, // 新冻结状态:未冻结
+                    [
+                        'action' => 'unfreeze_compensation_transfer_out',
+                        'freeze_log_id' => $freezeLogId,
+                        'transferred_quantity' => $deductQuantity,
+                    ]
+                ));
+
+                $remainingShortage -= $deductQuantity;
+            }
+
+            // 将补足的数量加到冻结堆
+            $oldFrozenQuantity = $frozenItem->quantity;
+            $frozenItem->quantity = $originalFrozenQuantity;
+            $frozenItem->save();
+
+            // 记录交易日志(向冻结堆补足)
+            \App\Module\GameItems\Logics\Item::logTransaction(
+                $frozenItem->user_id,
+                $frozenItem->item_id,
+                $frozenItem->instance_id,
+                $shortageQuantity,
+                TRANSACTION_TYPE::TRADE_IN, // 使用交易获得类型
+                'unfreeze_compensation',
+                $freezeLogId,
+                ["解冻补足转移:向冻结堆补足 {$shortageQuantity} 个,转移详情:" . json_encode($transferDetails)]
+            );
+
+            // 触发物品数量变更事件(冻结堆增加)
+            event(new ItemQuantityChanged(
+                $frozenItem->user_id,
+                $frozenItem->item_id,
+                $frozenItem->instance_id,
+                $oldFrozenQuantity,
+                $originalFrozenQuantity,
+                $frozenItem->id,
+                true, // 旧冻结状态:已冻结
+                true, // 新冻结状态:已冻结
+                [
+                    'action' => 'unfreeze_compensation_transfer_in',
+                    'freeze_log_id' => $freezeLogId,
+                    'compensated_quantity' => $shortageQuantity,
+                    'transfer_details' => $transferDetails,
+                ]
+            ));
+        }
+
+        // 创建解冻日志,记录原始冻结数量
         $unfreezeLog = ItemFreezeLog::createLog(
             $frozenItem->user_id,
             $frozenItem->item_id,
             $frozenItem->instance_id,
-            $frozenItem->quantity,
+            $originalFrozenQuantity, // 解冻原始数量
             FREEZE_ACTION_TYPE::UNFREEZE,
-            "解冻操作,原冻结日志ID: {$freezeLogId}",
+            "解冻操作,原冻结日志ID: {$freezeLogId},原始冻结数量: {$originalFrozenQuantity}" .
+            ($shortageQuantity > 0 ? ",补足差额: {$shortageQuantity}" : ""),
             $freezeLog->source_id,
             $freezeLog->source_type,
             $freezeLog->operator_id
@@ -343,8 +471,8 @@ class ItemFreeze
             $frozenItem->user_id,
             $frozenItem->item_id,
             $frozenItem->instance_id,
-            $frozenItem->quantity, // 旧数量(数量未变
-            $frozenItem->quantity, // 新数量(数量未变
+            $originalFrozenQuantity, // 旧数量(恢复到原始数量
+            $originalFrozenQuantity, // 新数量(数量未变,只是解冻状态变更
             $frozenItem->id,
             true,  // 旧冻结状态:已冻结
             false, // 新冻结状态:未冻结
@@ -352,6 +480,8 @@ class ItemFreeze
                 'freeze_action' => 'unfreeze',
                 'unfreeze_log_id' => $unfreezeLog->id,
                 'original_freeze_log_id' => $freezeLogId,
+                'original_frozen_quantity' => $originalFrozenQuantity,
+                'shortage_compensated' => $shortageQuantity,
                 'source_id' => $freezeLog->source_id,
                 'source_type' => $freezeLog->source_type,
                 'operator_id' => $freezeLog->operator_id,
@@ -363,7 +493,8 @@ class ItemFreeze
             'user_id' => $frozenItem->user_id,
             'item_id' => $frozenItem->item_id,
             'instance_id' => $frozenItem->instance_id,
-            'unfrozen_quantity' => $frozenItem->quantity,
+            'unfrozen_quantity' => $originalFrozenQuantity, // 返回原始冻结数量
+            'shortage_compensated' => $shortageQuantity, // 返回补足的差额
             'user_item_id' => $frozenItem->id,
             'unfreeze_log_id' => $unfreezeLog->id,
         ];
@@ -626,4 +757,264 @@ class ItemFreeze
 
         return $statistics;
     }
+
+    /**
+     * 安全解冻物品(处理已被消耗的冻结堆)
+     *
+     * 与unfreezeByLogId不同,此方法会检查冻结物品是否已被消耗:
+     * - 如果冻结物品数量为0,则标记为已处理,不抛出异常
+     * - 如果冻结物品数量大于0,则正常解冻
+     * - 返回详细的处理结果信息
+     *
+     * @param int $freezeLogId 冻结日志ID
+     * @return array 解冻结果
+     * @throws Exception
+     */
+    public static function safeUnfreezeByLogId(int $freezeLogId): array
+    {
+        // 检查事务
+        Helper::check_tr();
+
+        // 查找冻结日志
+        $freezeLog = ItemFreezeLog::find($freezeLogId);
+        if (!$freezeLog) {
+            throw new Exception("冻结日志 {$freezeLogId} 不存在");
+        }
+
+        if (!$freezeLog->isFreeze()) {
+            throw new Exception("日志 {$freezeLogId} 不是冻结操作记录");
+        }
+
+        // 查找对应的冻结物品
+        $frozenItem = ItemUser::where('frozen_log_id', $freezeLogId)
+            ->where('is_frozen', true)
+            ->first();
+
+        if (!$frozenItem) {
+            // 冻结物品不存在,可能已被删除或处理
+            return [
+                'success' => true,
+                'status' => 'already_processed',
+                'message' => "冻结日志 {$freezeLogId} 对应的冻结物品不存在,可能已被处理",
+                'freeze_log_id' => $freezeLogId,
+                'unfrozen_quantity' => 0,
+            ];
+        }
+
+        // 获取原始冻结数量和当前剩余数量
+        $originalFrozenQuantity = $freezeLog->quantity;
+        $currentQuantity = $frozenItem->quantity;
+
+        if ($currentQuantity <= 0) {
+            // 冻结堆已被完全消耗,尝试从用户其他可用物品中补足
+            $availableQuantity = self::getAvailableQuantity(
+                $frozenItem->user_id,
+                $frozenItem->item_id,
+                $frozenItem->instance_id
+            );
+
+            if ($availableQuantity < $originalFrozenQuantity) {
+                // 用户可用数量不足,无法完全解冻
+                return [
+                    'success' => false,
+                    'status' => 'insufficient_available',
+                    'message' => "冻结物品已被完全消耗,用户可用数量不足以补足原始冻结数量",
+                    'user_id' => $frozenItem->user_id,
+                    'item_id' => $frozenItem->item_id,
+                    'instance_id' => $frozenItem->instance_id,
+                    'unfrozen_quantity' => 0,
+                    'original_frozen_quantity' => $originalFrozenQuantity,
+                    'current_frozen_quantity' => $currentQuantity,
+                    'available_quantity' => $availableQuantity,
+                    'shortage_quantity' => $originalFrozenQuantity - $availableQuantity,
+                ];
+            }
+
+            // 从用户可用物品中补足全部数量
+            $availableItems = ItemUser::where('user_id', $frozenItem->user_id)
+                ->where('item_id', $frozenItem->item_id)
+                ->where('is_frozen', false)
+                ->whereNull('instance_id')
+                ->where('quantity', '>', 0)
+                ->orderBy('expire_at')
+                ->get();
+
+            $remainingQuantity = $originalFrozenQuantity;
+            foreach ($availableItems as $availableItem) {
+                if ($remainingQuantity <= 0) break;
+
+                $deductQuantity = min($availableItem->quantity, $remainingQuantity);
+                $availableItem->quantity -= $deductQuantity;
+                $availableItem->save();
+
+                $remainingQuantity -= $deductQuantity;
+            }
+
+            // 恢复冻结堆到原始数量
+            $frozenItem->quantity = $originalFrozenQuantity;
+            $frozenItem->save();
+
+            $shortageQuantity = $originalFrozenQuantity;
+        } else {
+            // 检查是否需要补足差额
+            $shortageQuantity = $originalFrozenQuantity - $currentQuantity;
+
+            if ($shortageQuantity > 0) {
+                // 冻结堆被部分消耗,需要从用户其他可用物品中补足
+                $availableQuantity = self::getAvailableQuantity(
+                    $frozenItem->user_id,
+                    $frozenItem->item_id,
+                    $frozenItem->instance_id
+                );
+
+                if ($availableQuantity < $shortageQuantity) {
+                    return [
+                        'success' => false,
+                        'status' => 'insufficient_available',
+                        'message' => "解冻失败:需要补足 {$shortageQuantity},但用户可用数量只有 {$availableQuantity}",
+                        'user_id' => $frozenItem->user_id,
+                        'item_id' => $frozenItem->item_id,
+                        'instance_id' => $frozenItem->instance_id,
+                        'unfrozen_quantity' => 0,
+                        'original_frozen_quantity' => $originalFrozenQuantity,
+                        'current_frozen_quantity' => $currentQuantity,
+                        'shortage_quantity' => $shortageQuantity,
+                        'available_quantity' => $availableQuantity,
+                    ];
+                }
+
+                // 从用户可用物品中扣除差额,补足到冻结堆
+                $availableItems = ItemUser::where('user_id', $frozenItem->user_id)
+                    ->where('item_id', $frozenItem->item_id)
+                    ->where('is_frozen', false)
+                    ->whereNull('instance_id')
+                    ->where('quantity', '>', 0)
+                    ->orderBy('expire_at')
+                    ->get();
+
+                $remainingShortage = $shortageQuantity;
+                foreach ($availableItems as $availableItem) {
+                    if ($remainingShortage <= 0) break;
+
+                    $deductQuantity = min($availableItem->quantity, $remainingShortage);
+                    $availableItem->quantity -= $deductQuantity;
+                    $availableItem->save();
+
+                    $remainingShortage -= $deductQuantity;
+                }
+
+                // 将补足的数量加到冻结堆
+                $frozenItem->quantity = $originalFrozenQuantity;
+                $frozenItem->save();
+            }
+        }
+
+        // 创建解冻日志
+        $unfreezeLog = ItemFreezeLog::createLog(
+            $frozenItem->user_id,
+            $frozenItem->item_id,
+            $frozenItem->instance_id,
+            $originalFrozenQuantity,
+            FREEZE_ACTION_TYPE::UNFREEZE,
+            "安全解冻操作,原冻结日志ID: {$freezeLogId},原始冻结数量: {$originalFrozenQuantity}" .
+            ($shortageQuantity > 0 ? ",补足差额: {$shortageQuantity}" : ""),
+            $freezeLog->source_id,
+            $freezeLog->source_type,
+            $freezeLog->operator_id
+        );
+
+        // 解冻物品
+        $frozenItem->is_frozen = false;
+        $frozenItem->frozen_log_id = null;
+        $frozenItem->save();
+
+        // 触发物品变更事件
+        event(new ItemQuantityChanged(
+            $frozenItem->user_id,
+            $frozenItem->item_id,
+            $frozenItem->instance_id,
+            $originalFrozenQuantity,
+            $originalFrozenQuantity,
+            $frozenItem->id,
+            true,  // 旧冻结状态:已冻结
+            false, // 新冻结状态:未冻结
+            [
+                'freeze_action' => 'safe_unfreeze',
+                'unfreeze_log_id' => $unfreezeLog->id,
+                'original_freeze_log_id' => $freezeLogId,
+                'original_frozen_quantity' => $originalFrozenQuantity,
+                'shortage_compensated' => $shortageQuantity,
+                'source_id' => $freezeLog->source_id,
+                'source_type' => $freezeLog->source_type,
+                'operator_id' => $freezeLog->operator_id,
+            ]
+        ));
+
+        return [
+            'success' => true,
+            'status' => 'unfrozen',
+            'message' => "成功解冻物品",
+            'user_id' => $frozenItem->user_id,
+            'item_id' => $frozenItem->item_id,
+            'instance_id' => $frozenItem->instance_id,
+            'unfrozen_quantity' => $originalFrozenQuantity,
+            'shortage_compensated' => $shortageQuantity,
+            'user_item_id' => $frozenItem->id,
+            'unfreeze_log_id' => $unfreezeLog->id,
+        ];
+    }
+
+    /**
+     * 获取已被消耗的冻结物品统计
+     *
+     * 查找所有数量为0的冻结物品记录并返回统计信息
+     * 注意:不删除记录,只提供统计信息
+     *
+     * @param int|null $userId 用户ID,为null时统计所有用户
+     * @return array 统计结果
+     */
+    public static function getConsumedFrozenItemsStatistics(?int $userId = null): array
+    {
+        $query = ItemUser::where('is_frozen', true)
+            ->where('quantity', '<=', 0);
+
+        if ($userId) {
+            $query->where('user_id', $userId);
+        }
+
+        $consumedFrozenItems = $query->get();
+
+        $statistics = [
+            'total_count' => $consumedFrozenItems->count(),
+            'by_user' => [],
+            'by_item' => [],
+            'items' => [],
+        ];
+
+        foreach ($consumedFrozenItems as $frozenItem) {
+            // 按用户统计
+            if (!isset($statistics['by_user'][$frozenItem->user_id])) {
+                $statistics['by_user'][$frozenItem->user_id] = 0;
+            }
+            $statistics['by_user'][$frozenItem->user_id]++;
+
+            // 按物品统计
+            if (!isset($statistics['by_item'][$frozenItem->item_id])) {
+                $statistics['by_item'][$frozenItem->item_id] = 0;
+            }
+            $statistics['by_item'][$frozenItem->item_id]++;
+
+            // 详细记录
+            $statistics['items'][] = [
+                'user_id' => $frozenItem->user_id,
+                'item_id' => $frozenItem->item_id,
+                'instance_id' => $frozenItem->instance_id,
+                'user_item_id' => $frozenItem->id,
+                'frozen_log_id' => $frozenItem->frozen_log_id,
+                'quantity' => $frozenItem->quantity,
+            ];
+        }
+
+        return $statistics;
+    }
 }

+ 29 - 0
app/Module/GameItems/Services/ItemService.php

@@ -373,6 +373,35 @@ class ItemService
         return ItemFreeze::unfreezeByLogId($freezeLogId);
     }
 
+    /**
+     * 安全解冻物品(处理已被消耗的冻结堆)
+     *
+     * 与unfreezeItem不同,此方法会检查冻结物品是否已被消耗:
+     * - 如果冻结物品数量为0,则标记为已处理,不抛出异常
+     * - 如果冻结物品数量大于0,则正常解冻
+     * - 返回详细的处理结果信息
+     *
+     * @param int $freezeLogId 冻结日志ID
+     * @return array 解冻结果
+     * @throws Exception
+     */
+    public static function safeUnfreezeItem(int $freezeLogId): array
+    {
+        Helper::check_tr();
+        return ItemFreeze::safeUnfreezeByLogId($freezeLogId);
+    }
+
+    /**
+     * 获取已被消耗的冻结物品统计
+     *
+     * @param int|null $userId 用户ID,为null时统计所有用户
+     * @return array 统计结果
+     */
+    public static function getConsumedFrozenItemsStatistics(?int $userId = null): array
+    {
+        return ItemFreeze::getConsumedFrozenItemsStatistics($userId);
+    }
+
     /**
      * 获取用户可用物品数量(排除冻结的)
      *

+ 156 - 0
tests/manual_test_unfreeze_bug_fix.php

@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * 手动测试脚本:验证物品解冻BUG修复
+ * 
+ * 测试场景:
+ * 1. 冻结物品
+ * 2. 消耗冻结物品
+ * 3. 尝试解冻(应该正确处理已被消耗的情况)
+ */
+
+require_once __DIR__ . '/../vendor/autoload.php';
+
+use App\Module\GameItems\Services\ItemService;
+use App\Module\GameItems\Enums\FREEZE_REASON_TYPE;
+use Illuminate\Support\Facades\DB;
+
+// 启动Laravel应用
+$app = require_once __DIR__ . '/../bootstrap/app.php';
+$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
+
+echo "=== 物品解冻BUG修复测试 ===\n\n";
+
+try {
+    DB::beginTransaction();
+    
+    $userId = 1001; // 测试用户ID
+    $itemId = 1;    // 测试物品ID
+    $freezeQuantity = 100; // 冻结数量
+    $consumeQuantity = 60;  // 消耗数量
+    
+    echo "1. 准备测试数据\n";
+    echo "   用户ID: {$userId}\n";
+    echo "   物品ID: {$itemId}\n";
+    echo "   冻结数量: {$freezeQuantity}\n";
+    echo "   消耗数量: {$consumeQuantity}\n\n";
+    
+    // 确保用户有足够的物品
+    echo "2. 添加测试物品\n";
+    $addResult = ItemService::addItem($userId, $itemId, 300); // 增加到300,确保有足够的物品用于补足
+    echo "   添加结果: " . json_encode($addResult, JSON_UNESCAPED_UNICODE) . "\n\n";
+    
+    // 冻结物品
+    echo "3. 冻结物品\n";
+    $freezeResult = ItemService::freezeItem(
+        $userId,
+        $itemId,
+        null, // instanceId
+        $freezeQuantity,
+        FREEZE_REASON_TYPE::TRADE_ORDER->value,
+        [
+            'source_id' => 12345,
+            'source_type' => 'test_order',
+            'operator_id' => 1
+        ]
+    );
+    echo "   冻结结果: " . json_encode($freezeResult, JSON_UNESCAPED_UNICODE) . "\n";
+    $freezeLogId = $freezeResult['frozen_items'][0]['freeze_log_id'];
+    echo "   冻结日志ID: {$freezeLogId}\n\n";
+    
+    // 消耗部分冻结物品
+    echo "4. 消耗部分冻结物品\n";
+    $consumeResult = ItemService::consumeItem(
+        $userId,
+        $itemId,
+        null, // instanceId
+        $consumeQuantity,
+        [
+            'include_frozen' => true, // 允许消耗冻结物品
+            'source_type' => 'test_consume',
+            'source_id' => 67890
+        ]
+    );
+    echo "   消耗结果: " . json_encode($consumeResult, JSON_UNESCAPED_UNICODE) . "\n\n";
+    
+    // 检查冻结物品当前状态
+    echo "5. 检查冻结物品当前状态\n";
+    $frozenItems = ItemService::getFrozenItems($userId, ['item_id' => $itemId]);
+    echo "   冻结物品列表: " . json_encode($frozenItems, JSON_UNESCAPED_UNICODE) . "\n\n";
+    
+    // 测试原始解冻方法(应该成功,因为还有剩余数量)
+    echo "6. 测试原始解冻方法\n";
+    try {
+        $unfreezeResult = ItemService::unfreezeItem($freezeLogId);
+        echo "   原始解冻成功: " . json_encode($unfreezeResult, JSON_UNESCAPED_UNICODE) . "\n\n";
+    } catch (Exception $e) {
+        echo "   原始解冻失败: " . $e->getMessage() . "\n\n";
+    }
+    
+    // 重新冻结用于测试完全消耗的情况
+    echo "7. 重新冻结剩余物品进行完全消耗测试\n";
+    $remainingQuantity = 40; // 100 - 60 = 40
+    $freezeResult2 = ItemService::freezeItem(
+        $userId,
+        $itemId,
+        null, // instanceId
+        $remainingQuantity,
+        FREEZE_REASON_TYPE::TRADE_ORDER->value,
+        [
+            'source_id' => 12346,
+            'source_type' => 'test_order_2',
+            'operator_id' => 1
+        ]
+    );
+    echo "   第二次冻结结果: " . json_encode($freezeResult2, JSON_UNESCAPED_UNICODE) . "\n";
+    $freezeLogId2 = $freezeResult2['frozen_items'][0]['freeze_log_id'];
+    echo "   第二次冻结日志ID: {$freezeLogId2}\n\n";
+    
+    // 完全消耗冻结物品
+    echo "8. 完全消耗冻结物品\n";
+    $consumeResult2 = ItemService::consumeItem(
+        $userId,
+        $itemId,
+        null, // instanceId
+        $remainingQuantity,
+        [
+            'include_frozen' => true,
+            'source_type' => 'test_consume_all',
+            'source_id' => 67891
+        ]
+    );
+    echo "   完全消耗结果: " . json_encode($consumeResult2, JSON_UNESCAPED_UNICODE) . "\n\n";
+    
+    // 测试原始解冻方法(应该失败)
+    echo "9. 测试原始解冻方法(完全消耗后)\n";
+    try {
+        $unfreezeResult = ItemService::unfreezeItem($freezeLogId2);
+        echo "   原始解冻成功: " . json_encode($unfreezeResult, JSON_UNESCAPED_UNICODE) . "\n\n";
+    } catch (Exception $e) {
+        echo "   原始解冻失败(预期): " . $e->getMessage() . "\n\n";
+    }
+    
+    // 测试安全解冻方法(应该成功处理)
+    echo "10. 测试安全解冻方法(完全消耗后)\n";
+    try {
+        $safeUnfreezeResult = ItemService::safeUnfreezeItem($freezeLogId2);
+        echo "   安全解冻成功: " . json_encode($safeUnfreezeResult, JSON_UNESCAPED_UNICODE) . "\n\n";
+    } catch (Exception $e) {
+        echo "   安全解冻失败: " . $e->getMessage() . "\n\n";
+    }
+    
+    // 测试统计功能
+    echo "11. 测试已消耗冻结物品统计\n";
+    $statistics = ItemService::getConsumedFrozenItemsStatistics($userId);
+    echo "   统计结果: " . json_encode($statistics, JSON_UNESCAPED_UNICODE) . "\n\n";
+    
+    echo "=== 测试完成 ===\n";
+    
+    DB::rollback(); // 回滚测试数据
+    echo "已回滚测试数据\n";
+    
+} catch (Exception $e) {
+    DB::rollback();
+    echo "测试失败: " . $e->getMessage() . "\n";
+    echo "堆栈跟踪: " . $e->getTraceAsString() . "\n";
+}