|
|
@@ -9,13 +9,18 @@ use App\Module\Mex\Models\MexPriceConfig;
|
|
|
use App\Module\Mex\Enums\OrderType;
|
|
|
use App\Module\Mex\Enums\OrderStatus;
|
|
|
use App\Module\Mex\Enums\TransactionType;
|
|
|
+use App\Module\Fund\Services\FundService;
|
|
|
+use App\Module\Fund\Enums\FUND_TYPE;
|
|
|
+use App\Module\GameItems\Services\ItemService;
|
|
|
+use App\Module\GameItems\Enums\FREEZE_REASON_TYPE;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
|
|
/**
|
|
|
* 农贸市场撮合逻辑
|
|
|
- *
|
|
|
+ *
|
|
|
* 处理撮合相关的核心业务逻辑
|
|
|
+ * 根据文档要求分离用户买入物品和用户卖出物品的撮合逻辑
|
|
|
*/
|
|
|
class MexMatchLogic
|
|
|
{
|
|
|
@@ -25,13 +30,18 @@ class MexMatchLogic
|
|
|
private const WAREHOUSE_USER_ID = 15;
|
|
|
|
|
|
/**
|
|
|
- * 执行撮合任务
|
|
|
- *
|
|
|
+ * 调控账户ID
|
|
|
+ */
|
|
|
+ private const CONTROL_USER_ID = 16;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行用户买入物品撮合任务
|
|
|
+ *
|
|
|
* @param int|null $itemId 指定商品ID,null表示处理所有商品
|
|
|
* @param int $batchSize 批处理大小
|
|
|
* @return array 撮合结果
|
|
|
*/
|
|
|
- public static function executeMatch(?int $itemId = null, int $batchSize = 100): array
|
|
|
+ public static function executeUserBuyItemMatch(?int $itemId = null, int $batchSize = 100): array
|
|
|
{
|
|
|
$startTime = microtime(true);
|
|
|
$totalMatched = 0;
|
|
|
@@ -42,7 +52,7 @@ class MexMatchLogic
|
|
|
try {
|
|
|
if ($itemId) {
|
|
|
// 处理指定商品
|
|
|
- $result = self::executeItemMatch($itemId, $batchSize);
|
|
|
+ $result = self::executeUserBuyItemMatchForItem($itemId, $batchSize);
|
|
|
$processedItems[] = $itemId;
|
|
|
$totalMatched += $result['matched_orders'];
|
|
|
$totalAmount = bcadd($totalAmount, $result['total_amount'], 5);
|
|
|
@@ -50,7 +60,7 @@ class MexMatchLogic
|
|
|
$errors[] = "商品 {$itemId}: " . $result['message'];
|
|
|
}
|
|
|
} else {
|
|
|
- // 处理所有有待撮合订单的商品
|
|
|
+ // 处理所有有待撮合的用户买入物品订单的商品
|
|
|
$itemIds = MexOrder::where('order_type', OrderType::BUY)
|
|
|
->where('status', OrderStatus::PENDING)
|
|
|
->distinct()
|
|
|
@@ -58,11 +68,92 @@ class MexMatchLogic
|
|
|
->toArray();
|
|
|
|
|
|
foreach ($itemIds as $currentItemId) {
|
|
|
- $result = self::executeItemMatch($currentItemId, $batchSize);
|
|
|
+ $result = self::executeUserBuyItemMatchForItem($currentItemId, $batchSize);
|
|
|
+ $processedItems[] = $currentItemId;
|
|
|
+ $totalMatched += $result['matched_orders'];
|
|
|
+ $totalAmount = bcadd($totalAmount, $result['total_amount'], 5);
|
|
|
+
|
|
|
+ if (!$result['success']) {
|
|
|
+ $errors[] = "商品 {$currentItemId}: " . $result['message'];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $endTime = microtime(true);
|
|
|
+ $executionTime = round(($endTime - $startTime) * 1000, 2); // 毫秒
|
|
|
+
|
|
|
+ Log::info('Mex用户买入物品撮合任务执行完成', [
|
|
|
+ 'processed_items' => $processedItems,
|
|
|
+ 'total_matched' => $totalMatched,
|
|
|
+ 'total_amount' => $totalAmount,
|
|
|
+ 'execution_time_ms' => $executionTime,
|
|
|
+ 'errors' => $errors,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => '用户买入物品撮合任务执行完成',
|
|
|
+ 'processed_items' => $processedItems,
|
|
|
+ 'total_matched' => $totalMatched,
|
|
|
+ 'total_amount' => $totalAmount,
|
|
|
+ 'execution_time_ms' => $executionTime,
|
|
|
+ 'errors' => $errors,
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::error('Mex用户买入物品撮合任务执行失败', [
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ 'trace' => $e->getTraceAsString(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '用户买入物品撮合任务执行失败:' . $e->getMessage(),
|
|
|
+ 'processed_items' => $processedItems,
|
|
|
+ 'total_matched' => $totalMatched,
|
|
|
+ 'total_amount' => $totalAmount,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行用户卖出物品撮合任务
|
|
|
+ *
|
|
|
+ * @param int|null $itemId 指定商品ID,null表示处理所有商品
|
|
|
+ * @param int $batchSize 批处理大小
|
|
|
+ * @return array 撮合结果
|
|
|
+ */
|
|
|
+ public static function executeUserSellItemMatch(?int $itemId = null, int $batchSize = 100): array
|
|
|
+ {
|
|
|
+ $startTime = microtime(true);
|
|
|
+ $totalMatched = 0;
|
|
|
+ $totalAmount = '0.00000';
|
|
|
+ $processedItems = [];
|
|
|
+ $errors = [];
|
|
|
+
|
|
|
+ try {
|
|
|
+ if ($itemId) {
|
|
|
+ // 处理指定商品
|
|
|
+ $result = self::executeUserSellItemMatchForItem($itemId, $batchSize);
|
|
|
+ $processedItems[] = $itemId;
|
|
|
+ $totalMatched += $result['matched_orders'];
|
|
|
+ $totalAmount = bcadd($totalAmount, $result['total_amount'], 5);
|
|
|
+ if (!$result['success']) {
|
|
|
+ $errors[] = "商品 {$itemId}: " . $result['message'];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 处理所有有待撮合的用户卖出物品订单的商品
|
|
|
+ $itemIds = MexOrder::where('order_type', OrderType::SELL)
|
|
|
+ ->where('status', OrderStatus::PENDING)
|
|
|
+ ->distinct()
|
|
|
+ ->pluck('item_id')
|
|
|
+ ->toArray();
|
|
|
+
|
|
|
+ foreach ($itemIds as $currentItemId) {
|
|
|
+ $result = self::executeUserSellItemMatchForItem($currentItemId, $batchSize);
|
|
|
$processedItems[] = $currentItemId;
|
|
|
$totalMatched += $result['matched_orders'];
|
|
|
$totalAmount = bcadd($totalAmount, $result['total_amount'], 5);
|
|
|
-
|
|
|
+
|
|
|
if (!$result['success']) {
|
|
|
$errors[] = "商品 {$currentItemId}: " . $result['message'];
|
|
|
}
|
|
|
@@ -72,7 +163,7 @@ class MexMatchLogic
|
|
|
$endTime = microtime(true);
|
|
|
$executionTime = round(($endTime - $startTime) * 1000, 2); // 毫秒
|
|
|
|
|
|
- Log::info('Mex撮合任务执行完成', [
|
|
|
+ Log::info('Mex用户卖出物品撮合任务执行完成', [
|
|
|
'processed_items' => $processedItems,
|
|
|
'total_matched' => $totalMatched,
|
|
|
'total_amount' => $totalAmount,
|
|
|
@@ -82,7 +173,7 @@ class MexMatchLogic
|
|
|
|
|
|
return [
|
|
|
'success' => true,
|
|
|
- 'message' => '撮合任务执行完成',
|
|
|
+ 'message' => '用户卖出物品撮合任务执行完成',
|
|
|
'processed_items' => $processedItems,
|
|
|
'total_matched' => $totalMatched,
|
|
|
'total_amount' => $totalAmount,
|
|
|
@@ -90,14 +181,14 @@ class MexMatchLogic
|
|
|
'errors' => $errors,
|
|
|
];
|
|
|
} catch (\Exception $e) {
|
|
|
- Log::error('Mex撮合任务执行失败', [
|
|
|
+ Log::error('Mex用户卖出物品撮合任务执行失败', [
|
|
|
'error' => $e->getMessage(),
|
|
|
'trace' => $e->getTraceAsString(),
|
|
|
]);
|
|
|
|
|
|
return [
|
|
|
'success' => false,
|
|
|
- 'message' => '撮合任务执行失败:' . $e->getMessage(),
|
|
|
+ 'message' => '用户卖出物品撮合任务执行失败:' . $e->getMessage(),
|
|
|
'processed_items' => $processedItems,
|
|
|
'total_matched' => $totalMatched,
|
|
|
'total_amount' => $totalAmount,
|
|
|
@@ -106,18 +197,18 @@ class MexMatchLogic
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 执行单个商品的撮合
|
|
|
- *
|
|
|
+ * 执行单个商品的用户买入物品撮合
|
|
|
+ *
|
|
|
* @param int $itemId 商品ID
|
|
|
* @param int $batchSize 批处理大小
|
|
|
* @return array 撮合结果
|
|
|
*/
|
|
|
- public static function executeItemMatch(int $itemId, int $batchSize = 100): array
|
|
|
+ public static function executeUserBuyItemMatchForItem(int $itemId, int $batchSize = 100): array
|
|
|
{
|
|
|
try {
|
|
|
return DB::transaction(function () use ($itemId, $batchSize) {
|
|
|
- // 检查撮合条件
|
|
|
- $conditionCheck = self::checkMatchConditions($itemId);
|
|
|
+ // 检查用户买入物品撮合条件
|
|
|
+ $conditionCheck = self::checkUserBuyItemMatchConditions($itemId);
|
|
|
if (!$conditionCheck['can_match']) {
|
|
|
return [
|
|
|
'success' => false,
|
|
|
@@ -130,21 +221,21 @@ class MexMatchLogic
|
|
|
$warehouse = $conditionCheck['warehouse'];
|
|
|
$priceConfig = $conditionCheck['price_config'];
|
|
|
|
|
|
- // 获取待撮合的买入订单(三级排序)
|
|
|
+ // 获取待撮合的用户买入物品订单(MySQL查询时完成筛选和二级排序)
|
|
|
$buyOrders = MexOrder::where('item_id', $itemId)
|
|
|
->where('order_type', OrderType::BUY)
|
|
|
->where('status', OrderStatus::PENDING)
|
|
|
- ->where('quantity', '<=', $priceConfig->protection_threshold) // 过滤大额订单
|
|
|
+ ->where('price', '>=', $priceConfig->max_price) // 价格验证:价格≥最高价
|
|
|
+ ->where('quantity', '<=', $priceConfig->protection_threshold) // 数量保护:数量≤保护阈值
|
|
|
->orderBy('price', 'desc') // 价格优先(高价优先)
|
|
|
->orderBy('created_at', 'asc') // 时间优先(早下单优先)
|
|
|
- ->orderBy('quantity', 'asc') // 数量优先(小单优先)
|
|
|
->limit($batchSize)
|
|
|
->get();
|
|
|
|
|
|
if ($buyOrders->isEmpty()) {
|
|
|
return [
|
|
|
'success' => true,
|
|
|
- 'message' => '没有符合条件的买入订单',
|
|
|
+ 'message' => '没有符合条件的用户买入物品订单',
|
|
|
'matched_orders' => 0,
|
|
|
'total_amount' => '0.00000',
|
|
|
];
|
|
|
@@ -157,16 +248,17 @@ class MexMatchLogic
|
|
|
foreach ($buyOrders as $order) {
|
|
|
// 检查库存是否充足(整单匹配原则)
|
|
|
if ($currentStock < $order->quantity) {
|
|
|
- continue; // 库存不足,跳过此订单
|
|
|
+ // 库存不足时结束本次撮合处理,避免无效循环
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
- // 执行撮合
|
|
|
- $matchResult = self::executeOrderMatch($order, $warehouse);
|
|
|
+ // 执行用户买入物品订单撮合
|
|
|
+ $matchResult = self::executeUserBuyItemOrderMatch($order, $warehouse);
|
|
|
if ($matchResult['success']) {
|
|
|
$matchedOrders++;
|
|
|
$totalAmount = bcadd($totalAmount, $matchResult['total_amount'], 5);
|
|
|
$currentStock -= $order->quantity;
|
|
|
-
|
|
|
+
|
|
|
// 更新仓库对象的库存(用于后续订单判断)
|
|
|
$warehouse->quantity = $currentStock;
|
|
|
}
|
|
|
@@ -174,7 +266,7 @@ class MexMatchLogic
|
|
|
|
|
|
return [
|
|
|
'success' => true,
|
|
|
- 'message' => "成功撮合 {$matchedOrders} 个订单",
|
|
|
+ 'message' => "成功撮合 {$matchedOrders} 个用户买入物品订单",
|
|
|
'matched_orders' => $matchedOrders,
|
|
|
'total_amount' => $totalAmount,
|
|
|
];
|
|
|
@@ -182,7 +274,7 @@ class MexMatchLogic
|
|
|
} catch (\Exception $e) {
|
|
|
return [
|
|
|
'success' => false,
|
|
|
- 'message' => '撮合执行失败:' . $e->getMessage(),
|
|
|
+ 'message' => '用户买入物品撮合执行失败:' . $e->getMessage(),
|
|
|
'matched_orders' => 0,
|
|
|
'total_amount' => '0.00000',
|
|
|
];
|
|
|
@@ -190,13 +282,87 @@ class MexMatchLogic
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 执行单个订单的撮合
|
|
|
- *
|
|
|
- * @param MexOrder $order 买入订单
|
|
|
+ * 执行单个商品的用户卖出物品撮合
|
|
|
+ *
|
|
|
+ * @param int $itemId 商品ID
|
|
|
+ * @param int $batchSize 批处理大小
|
|
|
+ * @return array 撮合结果
|
|
|
+ */
|
|
|
+ public static function executeUserSellItemMatchForItem(int $itemId, int $batchSize = 100): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ return DB::transaction(function () use ($itemId, $batchSize) {
|
|
|
+ // 检查用户卖出物品撮合条件
|
|
|
+ $conditionCheck = self::checkUserSellItemMatchConditions($itemId);
|
|
|
+ if (!$conditionCheck['can_match']) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => $conditionCheck['message'],
|
|
|
+ 'matched_orders' => 0,
|
|
|
+ 'total_amount' => '0.00000',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ $priceConfig = $conditionCheck['price_config'];
|
|
|
+
|
|
|
+ // 获取待撮合的用户卖出物品订单
|
|
|
+ $sellOrders = MexOrder::where('item_id', $itemId)
|
|
|
+ ->where('order_type', OrderType::SELL)
|
|
|
+ ->where('status', OrderStatus::PENDING)
|
|
|
+ ->limit($batchSize)
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ if ($sellOrders->isEmpty()) {
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => '没有待撮合的用户卖出物品订单',
|
|
|
+ 'matched_orders' => 0,
|
|
|
+ 'total_amount' => '0.00000',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ $matchedOrders = 0;
|
|
|
+ $totalAmount = '0.00000';
|
|
|
+
|
|
|
+ foreach ($sellOrders as $order) {
|
|
|
+ // 价格验证:用户卖出物品价格≤最低价
|
|
|
+ if (bccomp($order->price, $priceConfig->min_price, 5) > 0) {
|
|
|
+ continue; // 价格不符合条件,跳过此订单
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行用户卖出物品订单撮合
|
|
|
+ $matchResult = self::executeUserSellItemOrderMatch($order);
|
|
|
+ if ($matchResult['success']) {
|
|
|
+ $matchedOrders++;
|
|
|
+ $totalAmount = bcadd($totalAmount, $matchResult['total_amount'], 5);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => "成功撮合 {$matchedOrders} 个用户卖出物品订单",
|
|
|
+ 'matched_orders' => $matchedOrders,
|
|
|
+ 'total_amount' => $totalAmount,
|
|
|
+ ];
|
|
|
+ });
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '用户卖出物品撮合执行失败:' . $e->getMessage(),
|
|
|
+ 'matched_orders' => 0,
|
|
|
+ 'total_amount' => '0.00000',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行单个用户买入物品订单的撮合
|
|
|
+ *
|
|
|
+ * @param MexOrder $order 用户买入物品订单
|
|
|
* @param MexWarehouse $warehouse 仓库信息
|
|
|
* @return array 撮合结果
|
|
|
*/
|
|
|
- private static function executeOrderMatch(MexOrder $order, MexWarehouse $warehouse): array
|
|
|
+ private static function executeUserBuyItemOrderMatch(MexOrder $order, MexWarehouse $warehouse): array
|
|
|
{
|
|
|
try {
|
|
|
// 计算成交金额
|
|
|
@@ -231,9 +397,18 @@ class MexMatchLogic
|
|
|
'is_admin_operation' => false,
|
|
|
]);
|
|
|
|
|
|
- // TODO: 这里应该调用账户流转逻辑
|
|
|
+ // 执行账户流转逻辑
|
|
|
// 1. 用户冻结资金转入仓库账户
|
|
|
+ $fundResult = self::transferFrozenFundsToWarehouse($order->user_id, $totalAmount, $order->id);
|
|
|
+ if (!$fundResult['success']) {
|
|
|
+ throw new \Exception('资金流转失败:' . $fundResult['message']);
|
|
|
+ }
|
|
|
+
|
|
|
// 2. 仓库账户物品转出到用户账户
|
|
|
+ $itemResult = self::transferItemsFromWarehouseToUser($order->user_id, $order->item_id, $order->quantity, $order->id);
|
|
|
+ if (!$itemResult['success']) {
|
|
|
+ throw new \Exception('物品流转失败:' . $itemResult['message']);
|
|
|
+ }
|
|
|
|
|
|
return [
|
|
|
'success' => true,
|
|
|
@@ -252,13 +427,99 @@ class MexMatchLogic
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行单个用户卖出物品订单的撮合
|
|
|
+ *
|
|
|
+ * @param MexOrder $order 用户卖出物品订单
|
|
|
+ * @return array 撮合结果
|
|
|
+ */
|
|
|
+ private static function executeUserSellItemOrderMatch(MexOrder $order): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // 计算成交金额
|
|
|
+ $totalAmount = bcmul($order->price, $order->quantity, 5);
|
|
|
+
|
|
|
+ // 更新订单状态
|
|
|
+ $order->update([
|
|
|
+ 'status' => OrderStatus::COMPLETED,
|
|
|
+ 'completed_quantity' => $order->quantity,
|
|
|
+ 'completed_amount' => $totalAmount,
|
|
|
+ 'completed_at' => now(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 更新仓库库存
|
|
|
+ $warehouse = MexWarehouse::where('item_id', $order->item_id)->first();
|
|
|
+ if (!$warehouse) {
|
|
|
+ // 如果仓库记录不存在,创建新记录
|
|
|
+ $warehouse = MexWarehouse::create([
|
|
|
+ 'item_id' => $order->item_id,
|
|
|
+ 'quantity' => $order->quantity,
|
|
|
+ 'total_buy_amount' => $totalAmount,
|
|
|
+ 'total_buy_quantity' => $order->quantity,
|
|
|
+ 'last_transaction_at' => now(),
|
|
|
+ ]);
|
|
|
+ } else {
|
|
|
+ // 更新现有仓库记录
|
|
|
+ $warehouse->quantity += $order->quantity;
|
|
|
+ $warehouse->total_buy_quantity += $order->quantity;
|
|
|
+ $warehouse->total_buy_amount = bcadd($warehouse->total_buy_amount, $totalAmount, 5);
|
|
|
+ $warehouse->last_transaction_at = now();
|
|
|
+ $warehouse->save();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建成交记录
|
|
|
+ $transaction = MexTransaction::create([
|
|
|
+ 'buy_order_id' => null,
|
|
|
+ 'sell_order_id' => $order->id,
|
|
|
+ 'buyer_id' => self::WAREHOUSE_USER_ID, // 仓库账户作为买方
|
|
|
+ 'seller_id' => $order->user_id,
|
|
|
+ 'item_id' => $order->item_id,
|
|
|
+ 'quantity' => $order->quantity,
|
|
|
+ 'price' => $order->price,
|
|
|
+ 'total_amount' => $totalAmount,
|
|
|
+ 'transaction_type' => TransactionType::USER_SELL,
|
|
|
+ 'is_admin_operation' => false,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 执行账户流转逻辑
|
|
|
+ // 1. 用户冻结物品转入仓库账户
|
|
|
+ $itemResult = self::transferFrozenItemsToWarehouse($order->user_id, $order->item_id, $order->quantity, $order->id);
|
|
|
+ if (!$itemResult['success']) {
|
|
|
+ throw new \Exception('物品流转失败:' . $itemResult['message']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 仓库账户资金转出到用户账户
|
|
|
+ $fundResult = self::transferFundsFromWarehouseToUser($order->user_id, $totalAmount, $order->id);
|
|
|
+ if (!$fundResult['success']) {
|
|
|
+ throw new \Exception('资金流转失败:' . $fundResult['message']);
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => '用户卖出物品订单撮合成功',
|
|
|
+ 'order_id' => $order->id,
|
|
|
+ 'transaction_id' => $transaction->id,
|
|
|
+ 'total_amount' => $totalAmount,
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '用户卖出物品订单撮合失败:' . $e->getMessage(),
|
|
|
+ 'order_id' => $order->id,
|
|
|
+ 'total_amount' => '0.00000',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * 检查撮合条件
|
|
|
- *
|
|
|
+ * 检查用户买入物品撮合条件
|
|
|
+ *
|
|
|
* @param int $itemId 商品ID
|
|
|
* @return array 检查结果
|
|
|
*/
|
|
|
- public static function checkMatchConditions(int $itemId): array
|
|
|
+ public static function checkUserBuyItemMatchConditions(int $itemId): array
|
|
|
{
|
|
|
// 检查价格配置
|
|
|
$priceConfig = MexPriceConfig::where('item_id', $itemId)->where('is_enabled', true)->first();
|
|
|
@@ -278,23 +539,24 @@ class MexMatchLogic
|
|
|
];
|
|
|
}
|
|
|
|
|
|
- // 检查是否有待撮合的买入订单
|
|
|
+ // 检查是否有符合条件的待撮合用户买入物品订单
|
|
|
$pendingBuyOrders = MexOrder::where('item_id', $itemId)
|
|
|
->where('order_type', OrderType::BUY)
|
|
|
->where('status', OrderStatus::PENDING)
|
|
|
- ->where('quantity', '<=', $priceConfig->protection_threshold)
|
|
|
+ ->where('price', '>=', $priceConfig->max_price) // 价格≥最高价
|
|
|
+ ->where('quantity', '<=', $priceConfig->protection_threshold) // 数量≤保护阈值
|
|
|
->count();
|
|
|
|
|
|
if ($pendingBuyOrders === 0) {
|
|
|
return [
|
|
|
'can_match' => false,
|
|
|
- 'message' => '没有符合条件的待撮合买入订单',
|
|
|
+ 'message' => '没有符合条件的待撮合用户买入物品订单',
|
|
|
];
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
'can_match' => true,
|
|
|
- 'message' => '撮合条件满足',
|
|
|
+ 'message' => '用户买入物品撮合条件满足',
|
|
|
'warehouse' => $warehouse,
|
|
|
'price_config' => $priceConfig,
|
|
|
'pending_orders' => $pendingBuyOrders,
|
|
|
@@ -302,13 +564,51 @@ class MexMatchLogic
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取撮合统计信息
|
|
|
- *
|
|
|
+ * 检查用户卖出物品撮合条件
|
|
|
+ *
|
|
|
+ * @param int $itemId 商品ID
|
|
|
+ * @return array 检查结果
|
|
|
+ */
|
|
|
+ public static function checkUserSellItemMatchConditions(int $itemId): array
|
|
|
+ {
|
|
|
+ // 检查价格配置
|
|
|
+ $priceConfig = MexPriceConfig::where('item_id', $itemId)->where('is_enabled', true)->first();
|
|
|
+ if (!$priceConfig) {
|
|
|
+ return [
|
|
|
+ 'can_match' => false,
|
|
|
+ 'message' => '商品未配置价格信息或已禁用',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有待撮合的用户卖出物品订单
|
|
|
+ $pendingSellOrders = MexOrder::where('item_id', $itemId)
|
|
|
+ ->where('order_type', OrderType::SELL)
|
|
|
+ ->where('status', OrderStatus::PENDING)
|
|
|
+ ->count();
|
|
|
+
|
|
|
+ if ($pendingSellOrders === 0) {
|
|
|
+ return [
|
|
|
+ 'can_match' => false,
|
|
|
+ 'message' => '没有待撮合的用户卖出物品订单',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'can_match' => true,
|
|
|
+ 'message' => '用户卖出物品撮合条件满足',
|
|
|
+ 'price_config' => $priceConfig,
|
|
|
+ 'pending_orders' => $pendingSellOrders,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户买入物品撮合统计信息
|
|
|
+ *
|
|
|
* @return array 统计信息
|
|
|
*/
|
|
|
- public static function getMatchStats(): array
|
|
|
+ public static function getUserBuyItemMatchStats(): array
|
|
|
{
|
|
|
- // 获取待撮合订单统计
|
|
|
+ // 获取待撮合用户买入物品订单统计
|
|
|
$pendingStats = MexOrder::where('order_type', OrderType::BUY)
|
|
|
->where('status', OrderStatus::PENDING)
|
|
|
->selectRaw('
|
|
|
@@ -319,7 +619,7 @@ class MexMatchLogic
|
|
|
')
|
|
|
->first();
|
|
|
|
|
|
- // 获取今日撮合统计
|
|
|
+ // 获取今日用户买入物品撮合统计
|
|
|
$todayStats = MexTransaction::where('transaction_type', TransactionType::USER_BUY)
|
|
|
->whereDate('created_at', today())
|
|
|
->selectRaw('
|
|
|
@@ -344,4 +644,264 @@ class MexMatchLogic
|
|
|
'stats_time' => now(),
|
|
|
];
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户卖出物品撮合统计信息
|
|
|
+ *
|
|
|
+ * @return array 统计信息
|
|
|
+ */
|
|
|
+ public static function getUserSellItemMatchStats(): array
|
|
|
+ {
|
|
|
+ // 获取待撮合用户卖出物品订单统计
|
|
|
+ $pendingStats = MexOrder::where('order_type', OrderType::SELL)
|
|
|
+ ->where('status', OrderStatus::PENDING)
|
|
|
+ ->selectRaw('
|
|
|
+ COUNT(*) as total_pending,
|
|
|
+ COUNT(DISTINCT item_id) as pending_items,
|
|
|
+ SUM(quantity) as total_quantity,
|
|
|
+ SUM(total_amount) as total_amount
|
|
|
+ ')
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ // 获取今日用户卖出物品撮合统计
|
|
|
+ $todayStats = MexTransaction::where('transaction_type', TransactionType::USER_SELL)
|
|
|
+ ->whereDate('created_at', today())
|
|
|
+ ->selectRaw('
|
|
|
+ COUNT(*) as today_matched,
|
|
|
+ SUM(quantity) as today_quantity,
|
|
|
+ SUM(total_amount) as today_amount
|
|
|
+ ')
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'pending_orders' => $pendingStats->total_pending ?? 0,
|
|
|
+ 'pending_items' => $pendingStats->pending_items ?? 0,
|
|
|
+ 'pending_quantity' => $pendingStats->total_quantity ?? 0,
|
|
|
+ 'pending_amount' => $pendingStats->total_amount ?? '0.00000',
|
|
|
+ 'today_matched' => $todayStats->today_matched ?? 0,
|
|
|
+ 'today_quantity' => $todayStats->today_quantity ?? 0,
|
|
|
+ 'today_amount' => $todayStats->today_amount ?? '0.00000',
|
|
|
+ 'stats_time' => now(),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保留旧方法以兼容现有代码
|
|
|
+ /**
|
|
|
+ * 获取撮合统计信息(已废弃,请使用getUserBuyItemMatchStats)
|
|
|
+ *
|
|
|
+ * @deprecated 请使用getUserBuyItemMatchStats或getUserSellItemMatchStats
|
|
|
+ * @return array 统计信息
|
|
|
+ */
|
|
|
+ public static function getMatchStats(): array
|
|
|
+ {
|
|
|
+ return self::getUserBuyItemMatchStats();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查撮合条件(已废弃,请使用checkUserBuyItemMatchConditions)
|
|
|
+ *
|
|
|
+ * @deprecated 请使用checkUserBuyItemMatchConditions或checkUserSellItemMatchConditions
|
|
|
+ * @param int $itemId 商品ID
|
|
|
+ * @return array 检查结果
|
|
|
+ */
|
|
|
+ public static function checkMatchConditions(int $itemId): array
|
|
|
+ {
|
|
|
+ return self::checkUserBuyItemMatchConditions($itemId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行撮合任务(已废弃,请使用executeUserBuyItemMatch)
|
|
|
+ *
|
|
|
+ * @deprecated 请使用executeUserBuyItemMatch或executeUserSellItemMatch
|
|
|
+ * @param int|null $itemId 指定商品ID,null表示处理所有商品
|
|
|
+ * @param int $batchSize 批处理大小
|
|
|
+ * @return array 撮合结果
|
|
|
+ */
|
|
|
+ public static function executeMatch(?int $itemId = null, int $batchSize = 100): array
|
|
|
+ {
|
|
|
+ return self::executeUserBuyItemMatch($itemId, $batchSize);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行单个商品的撮合(已废弃,请使用executeUserBuyItemMatchForItem)
|
|
|
+ *
|
|
|
+ * @deprecated 请使用executeUserBuyItemMatchForItem或executeUserSellItemMatchForItem
|
|
|
+ * @param int $itemId 商品ID
|
|
|
+ * @param int $batchSize 批处理大小
|
|
|
+ * @return array 撮合结果
|
|
|
+ */
|
|
|
+ public static function executeItemMatch(int $itemId, int $batchSize = 100): array
|
|
|
+ {
|
|
|
+ return self::executeUserBuyItemMatchForItem($itemId, $batchSize);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将用户冻结资金转入仓库账户
|
|
|
+ *
|
|
|
+ * @param int $userId 用户ID
|
|
|
+ * @param string $amount 金额
|
|
|
+ * @param int $orderId 订单ID
|
|
|
+ * @return array 转移结果
|
|
|
+ */
|
|
|
+ private static function transferFrozenFundsToWarehouse(int $userId, string $amount, int $orderId): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // TODO: 这里需要根据实际的资金类型进行调整
|
|
|
+ // 假设使用钻石币种(FUND_TYPE::FUND2)和冻结账户(FUND_TYPE::FUND3)
|
|
|
+
|
|
|
+ // 从用户冻结账户转移到仓库账户
|
|
|
+ $fundService = new FundService($userId, FUND_TYPE::FUND3->value); // 冻结账户
|
|
|
+ $result = $fundService->trade(
|
|
|
+ self::WAREHOUSE_USER_ID,
|
|
|
+ (int)bcmul($amount, '100', 0), // 转换为整数存储
|
|
|
+ 'MEX_ORDER',
|
|
|
+ $orderId,
|
|
|
+ '用户买入物品撮合-资金转移'
|
|
|
+ );
|
|
|
+
|
|
|
+ if (is_string($result)) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => $result,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => '资金转移成功',
|
|
|
+ 'data' => $result,
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '资金转移异常:' . $e->getMessage(),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将仓库账户物品转出到用户账户
|
|
|
+ *
|
|
|
+ * @param int $userId 用户ID
|
|
|
+ * @param int $itemId 物品ID
|
|
|
+ * @param int $quantity 数量
|
|
|
+ * @param int $orderId 订单ID
|
|
|
+ * @return array 转移结果
|
|
|
+ */
|
|
|
+ private static function transferItemsFromWarehouseToUser(int $userId, int $itemId, int $quantity, int $orderId): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // 添加物品到用户账户
|
|
|
+ $result = ItemService::addItem($userId, $itemId, $quantity, [
|
|
|
+ 'source' => 'mex_order',
|
|
|
+ 'source_id' => $orderId,
|
|
|
+ 'remark' => '用户买入物品撮合-物品转移',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ if (!$result || !isset($result['success']) || !$result['success']) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '物品添加失败:' . ($result['message'] ?? '未知错误'),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => '物品转移成功',
|
|
|
+ 'data' => $result,
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '物品转移异常:' . $e->getMessage(),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将用户冻结物品转入仓库账户
|
|
|
+ *
|
|
|
+ * @param int $userId 用户ID
|
|
|
+ * @param int $itemId 物品ID
|
|
|
+ * @param int $quantity 数量
|
|
|
+ * @param int $orderId 订单ID
|
|
|
+ * @return array 转移结果
|
|
|
+ */
|
|
|
+ private static function transferFrozenItemsToWarehouse(int $userId, int $itemId, int $quantity, int $orderId): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // TODO: 这里需要实现从用户冻结物品转移到仓库的逻辑
|
|
|
+ // 由于物品冻结功能比较复杂,这里先返回成功,后续完善
|
|
|
+
|
|
|
+ // 消耗用户物品(包括冻结的物品)
|
|
|
+ $result = ItemService::consumeItem($userId, $itemId, null, $quantity, [
|
|
|
+ 'source' => 'mex_order',
|
|
|
+ 'source_id' => $orderId,
|
|
|
+ 'remark' => '用户卖出物品撮合-物品转移',
|
|
|
+ 'include_frozen' => true, // 包括冻结的物品
|
|
|
+ ]);
|
|
|
+
|
|
|
+ if (!$result || !isset($result['success']) || !$result['success']) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '物品消耗失败:' . ($result['message'] ?? '未知错误'),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => '物品转移成功',
|
|
|
+ 'data' => $result,
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '物品转移异常:' . $e->getMessage(),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将仓库账户资金转出到用户账户
|
|
|
+ *
|
|
|
+ * @param int $userId 用户ID
|
|
|
+ * @param string $amount 金额
|
|
|
+ * @param int $orderId 订单ID
|
|
|
+ * @return array 转移结果
|
|
|
+ */
|
|
|
+ private static function transferFundsFromWarehouseToUser(int $userId, string $amount, int $orderId): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // TODO: 这里需要根据实际的资金类型进行调整
|
|
|
+ // 假设使用钻石币种(FUND_TYPE::FUND2)
|
|
|
+
|
|
|
+ // 从仓库账户转移到用户账户
|
|
|
+ $fundService = new FundService(self::WAREHOUSE_USER_ID, FUND_TYPE::FUND2->value); // 仓库账户
|
|
|
+ $result = $fundService->trade(
|
|
|
+ $userId,
|
|
|
+ (int)bcmul($amount, '100', 0), // 转换为整数存储
|
|
|
+ 'MEX_ORDER',
|
|
|
+ $orderId,
|
|
|
+ '用户卖出物品撮合-资金转移'
|
|
|
+ );
|
|
|
+
|
|
|
+ if (is_string($result)) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => $result,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'message' => '资金转移成功',
|
|
|
+ 'data' => $result,
|
|
|
+ ];
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'message' => '资金转移异常:' . $e->getMessage(),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|