manual_test_consume_concurrency.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <?php
  2. /**
  3. * 手动测试脚本:验证物品消耗过程中的并发安全性
  4. *
  5. * 测试lock for update是否能正确处理消耗操作的并发场景
  6. */
  7. require_once __DIR__ . '/../vendor/autoload.php';
  8. use App\Module\GameItems\Services\ItemService;
  9. use App\Module\GameItems\Enums\FREEZE_REASON_TYPE;
  10. use Illuminate\Support\Facades\DB;
  11. // 启动Laravel应用
  12. $app = require_once __DIR__ . '/../bootstrap/app.php';
  13. $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
  14. echo "=== 物品消耗并发安全性测试 ===\n\n";
  15. try {
  16. DB::beginTransaction();
  17. $userId = 1001;
  18. $itemId = 1;
  19. echo "1. 准备测试数据\n";
  20. // 添加物品
  21. $addResult = ItemService::addItem($userId, $itemId, 100);
  22. echo " 添加物品: " . json_encode($addResult, JSON_UNESCAPED_UNICODE) . "\n";
  23. // 检查初始状态
  24. echo "\n2. 检查初始物品状态\n";
  25. $userItems = DB::table('item_users')
  26. ->where('user_id', $userId)
  27. ->where('item_id', $itemId)
  28. ->get();
  29. foreach ($userItems as $item) {
  30. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  31. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  32. }
  33. // 测试正常消耗
  34. echo "\n3. 测试正常消耗(锁定机制)\n";
  35. $consumeResult1 = ItemService::consumeItem(
  36. $userId,
  37. $itemId,
  38. null,
  39. 30,
  40. [
  41. 'include_frozen' => false,
  42. 'source_type' => 'test_consume_1',
  43. 'source_id' => 1001
  44. ]
  45. );
  46. echo " 消耗30个成功: " . json_encode($consumeResult1, JSON_UNESCAPED_UNICODE) . "\n";
  47. // 检查消耗后状态
  48. echo "\n4. 检查消耗后的物品状态\n";
  49. $userItemsAfter = DB::table('item_users')
  50. ->where('user_id', $userId)
  51. ->where('item_id', $itemId)
  52. ->get();
  53. foreach ($userItemsAfter as $item) {
  54. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  55. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  56. }
  57. // 测试边界情况:消耗全部剩余数量
  58. echo "\n5. 测试边界情况:消耗全部剩余数量\n";
  59. $consumeResult2 = ItemService::consumeItem(
  60. $userId,
  61. $itemId,
  62. null,
  63. 70,
  64. [
  65. 'include_frozen' => false,
  66. 'source_type' => 'test_consume_2',
  67. 'source_id' => 1002
  68. ]
  69. );
  70. echo " 消耗70个成功: " . json_encode($consumeResult2, JSON_UNESCAPED_UNICODE) . "\n";
  71. // 检查完全消耗后状态
  72. echo "\n6. 检查完全消耗后的物品状态\n";
  73. $userItemsFinal = DB::table('item_users')
  74. ->where('user_id', $userId)
  75. ->where('item_id', $itemId)
  76. ->get();
  77. foreach ($userItemsFinal as $item) {
  78. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  79. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  80. }
  81. // 测试数量不足的情况
  82. echo "\n7. 测试数量不足的情况\n";
  83. try {
  84. $consumeResult3 = ItemService::consumeItem(
  85. $userId,
  86. $itemId,
  87. null,
  88. 10,
  89. [
  90. 'include_frozen' => false,
  91. 'source_type' => 'test_consume_3',
  92. 'source_id' => 1003
  93. ]
  94. );
  95. echo " 意外成功: " . json_encode($consumeResult3, JSON_UNESCAPED_UNICODE) . "\n";
  96. } catch (Exception $e) {
  97. echo " 预期失败: " . $e->getMessage() . "\n";
  98. }
  99. // 测试包含冻结物品的消耗
  100. echo "\n8. 测试包含冻结物品的消耗\n";
  101. // 先添加更多物品并冻结一部分
  102. $addResult2 = ItemService::addItem($userId, $itemId, 50);
  103. echo " 添加50个物品\n";
  104. $freezeResult = ItemService::freezeItem(
  105. $userId,
  106. $itemId,
  107. null,
  108. 20,
  109. FREEZE_REASON_TYPE::TRADE_ORDER->value,
  110. [
  111. 'source_id' => 12345,
  112. 'source_type' => 'test_order',
  113. 'operator_id' => 1
  114. ]
  115. );
  116. echo " 冻结20个物品\n";
  117. // 检查冻结后状态
  118. echo " 冻结后物品状态:\n";
  119. $userItemsWithFrozen = DB::table('item_users')
  120. ->where('user_id', $userId)
  121. ->where('item_id', $itemId)
  122. ->where('quantity', '>', 0)
  123. ->get();
  124. foreach ($userItemsWithFrozen as $item) {
  125. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  126. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  127. }
  128. // 消耗包含冻结物品
  129. echo " 消耗包含冻结物品(优先消耗冻结物品):\n";
  130. $consumeResult4 = ItemService::consumeItem(
  131. $userId,
  132. $itemId,
  133. null,
  134. 35,
  135. [
  136. 'include_frozen' => true,
  137. 'source_type' => 'test_consume_4',
  138. 'source_id' => 1004
  139. ]
  140. );
  141. echo " 消耗35个成功: " . json_encode($consumeResult4, JSON_UNESCAPED_UNICODE) . "\n";
  142. // 检查最终状态
  143. echo "\n9. 检查最终物品状态\n";
  144. $userItemsEnd = DB::table('item_users')
  145. ->where('user_id', $userId)
  146. ->where('item_id', $itemId)
  147. ->get();
  148. foreach ($userItemsEnd as $item) {
  149. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  150. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  151. }
  152. echo "\n=== 测试完成 ===\n";
  153. DB::rollback();
  154. echo "已回滚测试数据\n";
  155. } catch (Exception $e) {
  156. DB::rollback();
  157. echo "测试失败: " . $e->getMessage() . "\n";
  158. echo "堆栈跟踪: " . $e->getTraceAsString() . "\n";
  159. }