manual_test_freeze_concurrency.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. $freezeResult1 = ItemService::freezeItem(
  36. $userId,
  37. $itemId,
  38. null,
  39. 30,
  40. FREEZE_REASON_TYPE::TRADE_ORDER->value,
  41. [
  42. 'source_id' => 12345,
  43. 'source_type' => 'test_order_1',
  44. 'operator_id' => 1
  45. ]
  46. );
  47. echo " 冻结30个成功: " . json_encode($freezeResult1, JSON_UNESCAPED_UNICODE) . "\n";
  48. // 检查冻结后状态
  49. echo "\n4. 检查冻结后的物品状态\n";
  50. $userItemsAfter = DB::table('item_users')
  51. ->where('user_id', $userId)
  52. ->where('item_id', $itemId)
  53. ->get();
  54. foreach ($userItemsAfter as $item) {
  55. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  56. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  57. }
  58. // 测试再次冻结
  59. echo "\n5. 测试再次冻结剩余物品\n";
  60. $freezeResult2 = ItemService::freezeItem(
  61. $userId,
  62. $itemId,
  63. null,
  64. 40,
  65. FREEZE_REASON_TYPE::TRADE_ORDER->value,
  66. [
  67. 'source_id' => 12346,
  68. 'source_type' => 'test_order_2',
  69. 'operator_id' => 1
  70. ]
  71. );
  72. echo " 冻结40个成功: " . json_encode($freezeResult2, JSON_UNESCAPED_UNICODE) . "\n";
  73. // 检查再次冻结后状态
  74. echo "\n6. 检查再次冻结后的物品状态\n";
  75. $userItemsAfter2 = DB::table('item_users')
  76. ->where('user_id', $userId)
  77. ->where('item_id', $itemId)
  78. ->get();
  79. foreach ($userItemsAfter2 as $item) {
  80. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  81. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  82. }
  83. // 测试边界情况:冻结全部剩余数量
  84. echo "\n7. 测试边界情况:冻结全部剩余数量\n";
  85. $freezeResult3 = ItemService::freezeItem(
  86. $userId,
  87. $itemId,
  88. null,
  89. 30,
  90. FREEZE_REASON_TYPE::TRADE_ORDER->value,
  91. [
  92. 'source_id' => 12347,
  93. 'source_type' => 'test_order_3',
  94. 'operator_id' => 1
  95. ]
  96. );
  97. echo " 冻结30个成功: " . json_encode($freezeResult3, JSON_UNESCAPED_UNICODE) . "\n";
  98. // 检查完全冻结后状态
  99. echo "\n8. 检查完全冻结后的物品状态\n";
  100. $userItemsFinal = DB::table('item_users')
  101. ->where('user_id', $userId)
  102. ->where('item_id', $itemId)
  103. ->get();
  104. foreach ($userItemsFinal as $item) {
  105. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  106. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  107. }
  108. // 测试数量不足的情况
  109. echo "\n9. 测试数量不足的情况\n";
  110. try {
  111. $freezeResult4 = ItemService::freezeItem(
  112. $userId,
  113. $itemId,
  114. null,
  115. 10,
  116. FREEZE_REASON_TYPE::TRADE_ORDER->value,
  117. [
  118. 'source_id' => 12348,
  119. 'source_type' => 'test_order_4',
  120. 'operator_id' => 1
  121. ]
  122. );
  123. echo " 意外成功: " . json_encode($freezeResult4, JSON_UNESCAPED_UNICODE) . "\n";
  124. } catch (Exception $e) {
  125. echo " 预期失败: " . $e->getMessage() . "\n";
  126. }
  127. // 测试解冻后再冻结
  128. echo "\n10. 测试解冻后再冻结\n";
  129. // 先解冻一个
  130. $freezeLogId = $freezeResult1['frozen_items'][0]['freeze_log_id'];
  131. $unfreezeResult = ItemService::unfreezeItem($freezeLogId);
  132. echo " 解冻成功: " . json_encode($unfreezeResult, JSON_UNESCAPED_UNICODE) . "\n";
  133. // 检查解冻后状态
  134. echo " 解冻后物品状态:\n";
  135. $userItemsUnfrozen = DB::table('item_users')
  136. ->where('user_id', $userId)
  137. ->where('item_id', $itemId)
  138. ->get();
  139. foreach ($userItemsUnfrozen as $item) {
  140. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  141. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  142. }
  143. // 再次冻结
  144. echo " 再次冻结部分物品:\n";
  145. $freezeResult5 = ItemService::freezeItem(
  146. $userId,
  147. $itemId,
  148. null,
  149. 15,
  150. FREEZE_REASON_TYPE::TRADE_ORDER->value,
  151. [
  152. 'source_id' => 12349,
  153. 'source_type' => 'test_order_5',
  154. 'operator_id' => 1
  155. ]
  156. );
  157. echo " 冻结15个成功: " . json_encode($freezeResult5, JSON_UNESCAPED_UNICODE) . "\n";
  158. // 检查最终状态
  159. echo "\n11. 检查最终物品状态\n";
  160. $userItemsEnd = DB::table('item_users')
  161. ->where('user_id', $userId)
  162. ->where('item_id', $itemId)
  163. ->get();
  164. foreach ($userItemsEnd as $item) {
  165. $frozenStatus = $item->is_frozen ? '冻结' : '可用';
  166. echo " 物品堆ID: {$item->id}, 数量: {$item->quantity}, 状态: {$frozenStatus}\n";
  167. }
  168. echo "\n=== 测试完成 ===\n";
  169. DB::rollback();
  170. echo "已回滚测试数据\n";
  171. } catch (Exception $e) {
  172. DB::rollback();
  173. echo "测试失败: " . $e->getMessage() . "\n";
  174. echo "堆栈跟踪: " . $e->getTraceAsString() . "\n";
  175. }