|
|
@@ -0,0 +1,204 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Module\Mex\Tests;
|
|
|
+
|
|
|
+use App\Module\Mex\Logic\MexMatchLogic;
|
|
|
+use App\Module\Mex\Models\MexOrder;
|
|
|
+use App\Module\Mex\Models\MexTransaction;
|
|
|
+use App\Module\Mex\Models\MexWarehouse;
|
|
|
+use App\Module\Mex\Models\MexPriceConfig;
|
|
|
+use App\Module\Mex\Enums\OrderStatus;
|
|
|
+use App\Module\Mex\Enums\OrderType;
|
|
|
+use App\Module\Mex\Enums\TransactionType;
|
|
|
+use App\Module\Fund\Enums\FUND_CURRENCY_TYPE;
|
|
|
+use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
+use Tests\TestCase;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Mex撮合逻辑Bug修复测试
|
|
|
+ *
|
|
|
+ * 测试修复后的撮合逻辑确保订单状态更新和成交记录创建的一致性
|
|
|
+ */
|
|
|
+class MexMatchLogicBugFixTest extends TestCase
|
|
|
+{
|
|
|
+ use RefreshDatabase;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 仓库账户ID
|
|
|
+ */
|
|
|
+ const WAREHOUSE_USER_ID = 15;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试用户ID
|
|
|
+ */
|
|
|
+ const TEST_USER_ID = 39999;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试商品ID
|
|
|
+ */
|
|
|
+ const TEST_ITEM_ID = 999;
|
|
|
+
|
|
|
+ protected function setUp(): void
|
|
|
+ {
|
|
|
+ parent::setUp();
|
|
|
+
|
|
|
+ // 创建测试数据
|
|
|
+ $this->createTestData();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建测试数据
|
|
|
+ */
|
|
|
+ private function createTestData()
|
|
|
+ {
|
|
|
+ // 创建价格配置
|
|
|
+ MexPriceConfig::create([
|
|
|
+ 'item_id' => self::TEST_ITEM_ID,
|
|
|
+ 'min_price' => 10.00000,
|
|
|
+ 'max_price' => 20.00000,
|
|
|
+ 'protection_threshold' => 1000,
|
|
|
+ 'is_enabled' => true,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 创建仓库库存(用于买入测试)
|
|
|
+ MexWarehouse::create([
|
|
|
+ 'item_id' => self::TEST_ITEM_ID,
|
|
|
+ 'quantity' => 1000,
|
|
|
+ 'total_buy_quantity' => 0,
|
|
|
+ 'total_buy_amount' => '0.00000',
|
|
|
+ 'total_sell_quantity' => 0,
|
|
|
+ 'total_sell_amount' => '0.00000',
|
|
|
+ 'last_transaction_at' => now(),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试用户买入物品订单撮合的数据一致性
|
|
|
+ */
|
|
|
+ public function testUserBuyItemOrderMatchConsistency()
|
|
|
+ {
|
|
|
+ // 创建买入订单
|
|
|
+ $order = MexOrder::create([
|
|
|
+ 'user_id' => self::TEST_USER_ID,
|
|
|
+ 'item_id' => self::TEST_ITEM_ID,
|
|
|
+ 'currency_type' => FUND_CURRENCY_TYPE::DIAMOND,
|
|
|
+ 'order_type' => OrderType::BUY,
|
|
|
+ 'quantity' => 100,
|
|
|
+ 'price' => 20.00000,
|
|
|
+ 'total_amount' => 2000.00000,
|
|
|
+ 'status' => OrderStatus::PENDING,
|
|
|
+ 'frozen_amount' => 2000.00000,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $warehouse = MexWarehouse::where('item_id', self::TEST_ITEM_ID)->first();
|
|
|
+
|
|
|
+ // 模拟撮合过程中的异常情况
|
|
|
+ DB::beginTransaction();
|
|
|
+ try {
|
|
|
+ // 执行撮合(这里会因为资金流转失败而抛出异常,但我们要确保数据一致性)
|
|
|
+ $result = MexMatchLogic::executeUserBuyItemOrderMatch($order, $warehouse);
|
|
|
+
|
|
|
+ // 由于没有实际的资金和物品服务,这里会失败
|
|
|
+ $this->assertFalse($result['success']);
|
|
|
+
|
|
|
+ // 重要:确保订单状态没有被错误更新
|
|
|
+ $order->refresh();
|
|
|
+ $this->assertEquals(OrderStatus::PENDING, $order->status);
|
|
|
+
|
|
|
+ // 确保没有创建成交记录
|
|
|
+ $transactionCount = MexTransaction::where('buy_order_id', $order->id)->count();
|
|
|
+ $this->assertEquals(0, $transactionCount);
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ // 异常情况下也要确保数据一致性
|
|
|
+ $order->refresh();
|
|
|
+ $this->assertEquals(OrderStatus::PENDING, $order->status);
|
|
|
+
|
|
|
+ $transactionCount = MexTransaction::where('buy_order_id', $order->id)->count();
|
|
|
+ $this->assertEquals(0, $transactionCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ DB::rollBack();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试用户卖出物品订单撮合的数据一致性
|
|
|
+ */
|
|
|
+ public function testUserSellItemOrderMatchConsistency()
|
|
|
+ {
|
|
|
+ // 创建卖出订单
|
|
|
+ $order = MexOrder::create([
|
|
|
+ 'user_id' => self::TEST_USER_ID,
|
|
|
+ 'item_id' => self::TEST_ITEM_ID,
|
|
|
+ 'currency_type' => FUND_CURRENCY_TYPE::DIAMOND,
|
|
|
+ 'order_type' => OrderType::SELL,
|
|
|
+ 'quantity' => 100,
|
|
|
+ 'price' => 10.00000,
|
|
|
+ 'total_amount' => 1000.00000,
|
|
|
+ 'status' => OrderStatus::PENDING,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 模拟撮合过程中的异常情况
|
|
|
+ DB::beginTransaction();
|
|
|
+ try {
|
|
|
+ // 执行撮合(这里会因为物品流转失败而抛出异常,但我们要确保数据一致性)
|
|
|
+ $result = MexMatchLogic::executeUserSellItemOrderMatch($order);
|
|
|
+
|
|
|
+ // 由于没有实际的资金和物品服务,这里会失败
|
|
|
+ $this->assertFalse($result['success']);
|
|
|
+
|
|
|
+ // 重要:确保订单状态没有被错误更新
|
|
|
+ $order->refresh();
|
|
|
+ $this->assertEquals(OrderStatus::PENDING, $order->status);
|
|
|
+
|
|
|
+ // 确保没有创建成交记录
|
|
|
+ $transactionCount = MexTransaction::where('sell_order_id', $order->id)->count();
|
|
|
+ $this->assertEquals(0, $transactionCount);
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ // 异常情况下也要确保数据一致性
|
|
|
+ $order->refresh();
|
|
|
+ $this->assertEquals(OrderStatus::PENDING, $order->status);
|
|
|
+
|
|
|
+ $transactionCount = MexTransaction::where('sell_order_id', $order->id)->count();
|
|
|
+ $this->assertEquals(0, $transactionCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ DB::rollBack();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试修复命令能正确识别数据不一致问题
|
|
|
+ */
|
|
|
+ public function testFixCommandCanIdentifyInconsistentData()
|
|
|
+ {
|
|
|
+ // 手动创建一个已完成但没有成交记录的订单(模拟bug情况)
|
|
|
+ $order = MexOrder::create([
|
|
|
+ 'user_id' => self::TEST_USER_ID,
|
|
|
+ 'item_id' => self::TEST_ITEM_ID,
|
|
|
+ 'currency_type' => FUND_CURRENCY_TYPE::DIAMOND,
|
|
|
+ 'order_type' => OrderType::SELL,
|
|
|
+ 'quantity' => 100,
|
|
|
+ 'price' => 10.00000,
|
|
|
+ 'total_amount' => 1000.00000,
|
|
|
+ 'status' => OrderStatus::COMPLETED, // 已完成
|
|
|
+ 'completed_quantity' => 100,
|
|
|
+ 'completed_amount' => 1000.00000,
|
|
|
+ 'completed_at' => now(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 验证订单没有对应的成交记录
|
|
|
+ $this->assertFalse($order->buyTransactions()->exists());
|
|
|
+ $this->assertFalse($order->sellTransactions()->exists());
|
|
|
+
|
|
|
+ // 使用修复命令的查询逻辑
|
|
|
+ $missingTransactionOrders = MexOrder::where('status', OrderStatus::COMPLETED)
|
|
|
+ ->whereDoesntHave('buyTransactions')
|
|
|
+ ->whereDoesntHave('sellTransactions')
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ $this->assertCount(1, $missingTransactionOrders);
|
|
|
+ $this->assertEquals($order->id, $missingTransactionOrders->first()->id);
|
|
|
+ }
|
|
|
+}
|