ItemFreezeSplitStackSyncTest.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. namespace Tests\Unit\GameItems;
  3. use App\Module\Game\Logics\ItemTemp;
  4. use App\Module\GameItems\Enums\FREEZE_REASON_TYPE;
  5. use App\Module\GameItems\Events\ItemQuantityChanged;
  6. use App\Module\GameItems\Logics\ItemFreeze;
  7. use App\Module\GameItems\Models\ItemUser;
  8. use Illuminate\Support\Facades\DB;
  9. use Illuminate\Support\Facades\Event;
  10. use Tests\TestCase;
  11. /**
  12. * 物品冻结拆堆同步测试
  13. *
  14. * 测试物品冻结时拆堆场景下的状态同步问题修复
  15. */
  16. class ItemFreezeSplitStackSyncTest extends TestCase
  17. {
  18. protected function setUp(): void
  19. {
  20. parent::setUp();
  21. // 启用事件监听
  22. Event::fake([ItemQuantityChanged::class]);
  23. }
  24. /**
  25. * 测试拆堆冻结时的双事件触发和同步
  26. *
  27. * 验证当物品部分冻结需要拆堆时,能正确触发两个事件:
  28. * 1. 原堆叠数量减少事件
  29. * 2. 新冻结堆叠创建事件
  30. * 并且两个事件都能正确同步到lastdata
  31. */
  32. public function test_split_stack_freeze_dual_event_sync()
  33. {
  34. DB::beginTransaction();
  35. try {
  36. $userId = 1001;
  37. $itemId = 2; // 使用现有的萝卜物品
  38. $originalQuantity = 100;
  39. $freezeQuantity = 30;
  40. // 清理可能存在的临时数据
  41. ItemTemp::clearUserItemChanges($userId);
  42. // 创建测试用户物品记录
  43. $userItem = ItemUser::create([
  44. 'user_id' => $userId,
  45. 'item_id' => $itemId,
  46. 'instance_id' => null,
  47. 'quantity' => $originalQuantity,
  48. 'expire_at' => now()->addDays(30),
  49. 'is_frozen' => false,
  50. 'frozen_log_id' => null,
  51. ]);
  52. // 执行部分冻结操作(会触发拆堆)
  53. $result = ItemFreeze::freezeNormalItem(
  54. $userId,
  55. $itemId,
  56. $freezeQuantity,
  57. FREEZE_REASON_TYPE::TRADE_ORDER,
  58. 12345,
  59. 'test_order',
  60. null
  61. );
  62. // 验证冻结操作成功
  63. $this->assertTrue($result['success']);
  64. $this->assertEquals($freezeQuantity, $result['frozen_quantity']);
  65. // 验证触发了两个事件
  66. Event::assertDispatchedTimes(ItemQuantityChanged::class, 2);
  67. // 验证第一个事件:原堆叠数量减少
  68. Event::assertDispatched(ItemQuantityChanged::class, function ($event) use ($userId, $itemId, $userItem, $originalQuantity, $freezeQuantity) {
  69. return $event->userId === $userId
  70. && $event->itemId === $itemId
  71. && $event->userItemId === $userItem->id
  72. && $event->oldQuantity === $originalQuantity
  73. && $event->newQuantity === ($originalQuantity - $freezeQuantity)
  74. && $event->changeAmount === -$freezeQuantity
  75. && $event->oldFrozenStatus === false
  76. && $event->newFrozenStatus === false // 冻结状态不变
  77. && isset($event->options['change_type'])
  78. && $event->options['change_type'] === 'quantity_decrease';
  79. });
  80. // 验证第二个事件:新冻结堆叠创建
  81. Event::assertDispatched(ItemQuantityChanged::class, function ($event) use ($userId, $itemId, $freezeQuantity) {
  82. return $event->userId === $userId
  83. && $event->itemId === $itemId
  84. && $event->userItemId !== null // 新创建的堆叠ID
  85. && $event->oldQuantity === 0
  86. && $event->newQuantity === $freezeQuantity
  87. && $event->changeAmount === $freezeQuantity
  88. && $event->oldFrozenStatus === null // 新创建的物品
  89. && $event->newFrozenStatus === true
  90. && isset($event->options['change_type'])
  91. && $event->options['change_type'] === 'frozen_item_create';
  92. });
  93. // 手动触发事件处理(因为使用了Event::fake)
  94. $events = Event::dispatched(ItemQuantityChanged::class);
  95. foreach ($events as $eventData) {
  96. $event = $eventData[0]; // 获取事件实例
  97. ItemTemp::handleItemQuantityChanged($event);
  98. }
  99. // 验证临时数据中包含两个物品堆的变更信息
  100. $itemChanges = ItemTemp::getUserItemChanges($userId);
  101. $this->assertCount(2, $itemChanges, '应该有两个物品堆的变更记录');
  102. // 验证原堆叠的变更记录
  103. $originalStackChange = null;
  104. $frozenStackChange = null;
  105. foreach ($itemChanges as $change) {
  106. if ($change->userItemId === $userItem->id) {
  107. $originalStackChange = $change;
  108. } else {
  109. $frozenStackChange = $change;
  110. }
  111. }
  112. $this->assertNotNull($originalStackChange, '应该有原堆叠的变更记录');
  113. $this->assertNotNull($frozenStackChange, '应该有冻结堆叠的变更记录');
  114. // 验证原堆叠变更记录的正确性
  115. $this->assertEquals($itemId, $originalStackChange->itemId);
  116. $this->assertEquals($originalQuantity, $originalStackChange->oldQuantity);
  117. $this->assertEquals($originalQuantity - $freezeQuantity, $originalStackChange->newQuantity);
  118. $this->assertEquals(-$freezeQuantity, $originalStackChange->changeAmount);
  119. $this->assertFalse($originalStackChange->oldFrozenStatus);
  120. $this->assertFalse($originalStackChange->newFrozenStatus);
  121. // 验证冻结堆叠变更记录的正确性
  122. $this->assertEquals($itemId, $frozenStackChange->itemId);
  123. $this->assertEquals(0, $frozenStackChange->oldQuantity);
  124. $this->assertEquals($freezeQuantity, $frozenStackChange->newQuantity);
  125. $this->assertEquals($freezeQuantity, $frozenStackChange->changeAmount);
  126. $this->assertNull($frozenStackChange->oldFrozenStatus);
  127. $this->assertTrue($frozenStackChange->newFrozenStatus);
  128. } finally {
  129. DB::rollBack();
  130. }
  131. }
  132. /**
  133. * 测试全部冻结时的单事件触发和同步
  134. *
  135. * 验证当物品全部冻结不需要拆堆时,只触发一个冻结状态变更事件
  136. */
  137. public function test_full_freeze_single_event_sync()
  138. {
  139. DB::beginTransaction();
  140. try {
  141. $userId = 1002;
  142. $itemId = 3; // 使用现有的辣椒物品
  143. $quantity = 50;
  144. // 清理可能存在的临时数据
  145. ItemTemp::clearUserItemChanges($userId);
  146. // 创建测试用户物品记录
  147. $userItem = ItemUser::create([
  148. 'user_id' => $userId,
  149. 'item_id' => $itemId,
  150. 'instance_id' => null,
  151. 'quantity' => $quantity,
  152. 'expire_at' => now()->addDays(30),
  153. 'is_frozen' => false,
  154. 'frozen_log_id' => null,
  155. ]);
  156. // 执行全部冻结操作(不会拆堆)
  157. $result = ItemFreeze::freezeNormalItem(
  158. $userId,
  159. $itemId,
  160. $quantity, // 全部冻结
  161. FREEZE_REASON_TYPE::TRADE_ORDER,
  162. 12346,
  163. 'test_order',
  164. null
  165. );
  166. // 验证冻结操作成功
  167. $this->assertTrue($result['success']);
  168. $this->assertEquals($quantity, $result['frozen_quantity']);
  169. // 验证只触发了一个事件
  170. Event::assertDispatchedTimes(ItemQuantityChanged::class, 1);
  171. // 验证事件:冻结状态变更
  172. Event::assertDispatched(ItemQuantityChanged::class, function ($event) use ($userId, $itemId, $userItem, $quantity) {
  173. return $event->userId === $userId
  174. && $event->itemId === $itemId
  175. && $event->userItemId === $userItem->id
  176. && $event->oldQuantity === $quantity
  177. && $event->newQuantity === $quantity // 数量不变
  178. && $event->changeAmount === 0
  179. && $event->oldFrozenStatus === false
  180. && $event->newFrozenStatus === true
  181. && isset($event->options['change_type'])
  182. && $event->options['change_type'] === 'freeze_status_change';
  183. });
  184. // 手动触发事件处理
  185. $events = Event::dispatched(ItemQuantityChanged::class);
  186. foreach ($events as $eventData) {
  187. $event = $eventData[0];
  188. ItemTemp::handleItemQuantityChanged($event);
  189. }
  190. // 验证临时数据中只有一个物品堆的变更信息
  191. $itemChanges = ItemTemp::getUserItemChanges($userId);
  192. $this->assertCount(1, $itemChanges, '应该只有一个物品堆的变更记录');
  193. $change = $itemChanges[0];
  194. $this->assertEquals($userItem->id, $change->userItemId);
  195. $this->assertEquals($itemId, $change->itemId);
  196. $this->assertEquals($quantity, $change->oldQuantity);
  197. $this->assertEquals($quantity, $change->newQuantity);
  198. $this->assertEquals(0, $change->changeAmount);
  199. $this->assertFalse($change->oldFrozenStatus);
  200. $this->assertTrue($change->newFrozenStatus);
  201. } finally {
  202. DB::rollBack();
  203. }
  204. }
  205. }