make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); echo "=== 物品解冻并发安全性测试 ===\n\n"; try { DB::beginTransaction(); $userId = 1001; $itemId = 1; $freezeQuantity = 100; $consumeQuantity = 60; echo "1. 准备测试数据\n"; // 添加物品(确保有足够的物品用于测试) $addResult = ItemService::addItem($userId, $itemId, 200); echo " 添加物品: " . json_encode($addResult, JSON_UNESCAPED_UNICODE) . "\n"; // 冻结物品 echo "\n2. 冻结物品\n"; $freezeResult = ItemService::freezeItem( $userId, $itemId, null, $freezeQuantity, FREEZE_REASON_TYPE::TRADE_ORDER->value, [ 'source_id' => 12345, 'source_type' => 'test_order', 'operator_id' => 1 ] ); $freezeLogId = $freezeResult['frozen_items'][0]['freeze_log_id']; echo " 冻结成功,日志ID: {$freezeLogId}\n"; // 消耗部分冻结物品 echo "\n3. 消耗部分冻结物品\n"; $consumeResult = ItemService::consumeItem( $userId, $itemId, null, $consumeQuantity, [ 'include_frozen' => true, 'source_type' => 'test_consume', 'source_id' => 67890 ] ); echo " 消耗成功: {$consumeQuantity}个\n"; // 检查当前状态 echo "\n4. 检查当前物品状态\n"; $userItems = DB::table('item_users') ->where('user_id', $userId) ->where('item_id', $itemId) ->get(); foreach ($userItems as $item) { $frozenStatus = $item->is_frozen ? '冻结' : '可用'; echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n"; } // 模拟并发场景:在解冻的同时消耗可用物品 echo "\n5. 模拟并发场景测试\n"; echo " 开始解冻操作(会锁定可用物品)...\n"; // 这个操作会锁定可用物品记录 $unfreezeResult = ItemService::unfreezeItem($freezeLogId); echo " 解冻成功: " . json_encode($unfreezeResult, JSON_UNESCAPED_UNICODE) . "\n"; echo " 补足差额: " . ($unfreezeResult['shortage_compensated'] ?? 0) . "\n"; // 检查解冻后的状态 echo "\n6. 检查解冻后的物品状态\n"; $userItemsAfter = DB::table('item_users') ->where('user_id', $userId) ->where('item_id', $itemId) ->get(); foreach ($userItemsAfter as $item) { $frozenStatus = $item->is_frozen ? '冻结' : '可用'; echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n"; } // 测试边界情况:可用数量刚好不足 echo "\n7. 测试边界情况:可用数量不足\n"; // 消耗大部分可用物品,使其不足以补足 $availableQuantity = DB::table('item_users') ->where('user_id', $userId) ->where('item_id', $itemId) ->where('is_frozen', false) ->sum('quantity'); echo " 当前可用数量: {$availableQuantity}\n"; if ($availableQuantity > 10) { $consumeMore = $availableQuantity - 5; // 留5个,不足以补足 echo " 消耗更多可用物品: {$consumeMore}个\n"; $consumeResult2 = ItemService::consumeItem( $userId, $itemId, null, $consumeMore, [ 'include_frozen' => false, // 只消耗可用物品 'source_type' => 'test_consume_more', 'source_id' => 67891 ] ); // 重新冻结更多数量,然后部分消耗,创建需要补足但数量不足的场景 $freezeResult2 = ItemService::freezeItem( $userId, $itemId, null, 5, // 冻结5个 FREEZE_REASON_TYPE::TRADE_ORDER->value, [ 'source_id' => 12346, 'source_type' => 'test_order_2', 'operator_id' => 1 ] ); $freezeLogId2 = $freezeResult2['frozen_items'][0]['freeze_log_id']; // 部分消耗这个冻结堆(消耗3个,剩余2个) $consumeResult3 = ItemService::consumeItem( $userId, $itemId, null, 3, [ 'include_frozen' => true, 'source_type' => 'test_consume_partial', 'source_id' => 67892 ] ); echo " 冻结5个,消耗3个,剩余2个,需要补足3个,但可用数量不足\n"; // 尝试解冻(应该失败,因为可用数量不足) echo " 尝试解冻(预期失败)...\n"; try { $unfreezeResult2 = ItemService::unfreezeItem($freezeLogId2); echo " 意外成功: " . json_encode($unfreezeResult2, JSON_UNESCAPED_UNICODE) . "\n"; } catch (Exception $e) { echo " 预期失败: " . $e->getMessage() . "\n"; } // 测试安全解冻 echo " 尝试安全解冻...\n"; try { $safeUnfreezeResult = ItemService::safeUnfreezeItem($freezeLogId2); echo " 安全解冻结果: " . json_encode($safeUnfreezeResult, JSON_UNESCAPED_UNICODE) . "\n"; } catch (Exception $e) { echo " 安全解冻失败: " . $e->getMessage() . "\n"; } } echo "\n=== 测试完成 ===\n"; DB::rollback(); echo "已回滚测试数据\n"; } catch (Exception $e) { DB::rollback(); echo "测试失败: " . $e->getMessage() . "\n"; echo "堆栈跟踪: " . $e->getTraceAsString() . "\n"; }