MexTransactionLogic.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <?php
  2. namespace App\Module\Mex\Logic;
  3. use App\Module\Mex\Models\MexTransaction;
  4. use App\Module\Mex\Enums\TransactionType;
  5. use App\Module\Mex\Events\TransactionCreatedEvent;
  6. use Illuminate\Support\Facades\DB;
  7. /**
  8. * 农贸市场成交记录逻辑
  9. *
  10. * 处理成交记录相关的核心业务逻辑
  11. */
  12. class MexTransactionLogic
  13. {
  14. /**
  15. * 获取交易大厅成交记录
  16. *
  17. * @param int $page 页码
  18. * @param int $pageSize 每页数量
  19. * @param int|null $itemId 商品ID筛选
  20. * @return array 成交记录列表
  21. */
  22. public static function getPublicTransactions(int $page = 1, int $pageSize = 20, ?int $itemId = null): array
  23. {
  24. $query = MexTransaction::query()
  25. ->where('is_admin_operation', false) // 只显示用户交易,不显示管理员操作
  26. ->orderBy('created_at', 'desc');
  27. if ($itemId) {
  28. $query->where('item_id', $itemId);
  29. }
  30. $transactions = $query->paginate($pageSize, ['*'], 'page', $page);
  31. $result = [];
  32. foreach ($transactions->items() as $transaction) {
  33. // 根据交易类型确定显示的用户ID
  34. // USER_SELL表示用户卖出,显示卖方ID;USER_BUY表示用户买入,显示买方ID
  35. $userId = ($transaction->transaction_type->value === 'USER_SELL')
  36. ? $transaction->seller_id
  37. : $transaction->buyer_id;
  38. $result[] = [
  39. 'id' => $transaction->id,
  40. 'user_id' => $userId,
  41. 'item_id' => $transaction->item_id,
  42. 'quantity' => $transaction->quantity,
  43. 'price' => $transaction->price,
  44. 'total_amount' => $transaction->total_amount,
  45. 'transaction_type' => $transaction->transaction_type->value,
  46. 'transaction_type_desc' => $transaction->transaction_type->getDescription(),
  47. 'created_at' => $transaction->created_at,
  48. ];
  49. }
  50. return [
  51. 'transactions' => $result,
  52. 'total' => $transactions->total(),
  53. 'page' => $page,
  54. 'page_size' => $pageSize,
  55. ];
  56. }
  57. /**
  58. * 获取用户成交记录
  59. *
  60. * @param int $userId 用户ID
  61. * @param int $page 页码
  62. * @param int $pageSize 每页数量
  63. * @return array 成交记录列表
  64. */
  65. public static function getUserTransactions(int $userId, int $page = 1, int $pageSize = 20): array
  66. {
  67. $transactions = MexTransaction::where(function ($query) use ($userId) {
  68. $query->where('buyer_id', $userId)
  69. ->orWhere('seller_id', $userId);
  70. })
  71. ->where('is_admin_operation', false)
  72. ->orderBy('created_at', 'desc')
  73. ->paginate($pageSize, ['*'], 'page', $page);
  74. $result = [];
  75. foreach ($transactions->items() as $transaction) {
  76. $isUserBuyer = $transaction->buyer_id === $userId;
  77. $isUserSeller = $transaction->seller_id === $userId;
  78. $result[] = [
  79. 'id' => $transaction->id,
  80. 'item_id' => $transaction->item_id,
  81. 'quantity' => $transaction->quantity,
  82. 'price' => $transaction->price,
  83. 'total_amount' => $transaction->total_amount,
  84. 'transaction_type' => $transaction->transaction_type->value,
  85. 'transaction_type_desc' => $transaction->transaction_type->getDescription(),
  86. 'user_role' => $isUserBuyer ? 'buyer' : ($isUserSeller ? 'seller' : 'unknown'),
  87. 'user_role_desc' => $isUserBuyer ? '买方' : ($isUserSeller ? '卖方' : '未知'),
  88. 'counterparty_id' => $isUserBuyer ? $transaction->seller_id : $transaction->buyer_id,
  89. 'created_at' => $transaction->created_at,
  90. ];
  91. }
  92. return [
  93. 'transactions' => $result,
  94. 'total' => $transactions->total(),
  95. 'page' => $page,
  96. 'page_size' => $pageSize,
  97. ];
  98. }
  99. /**
  100. * 获取商品成交统计
  101. *
  102. * @param int $itemId 商品ID
  103. * @param int $days 统计天数
  104. * @return array 统计信息
  105. */
  106. public static function getItemTransactionStats(int $itemId, int $days = 7): array
  107. {
  108. $startDate = now()->subDays($days)->startOfDay();
  109. $stats = MexTransaction::where('item_id', $itemId)
  110. ->where('created_at', '>=', $startDate)
  111. ->selectRaw('
  112. COUNT(*) as total_transactions,
  113. SUM(quantity) as total_quantity,
  114. SUM(total_amount) as total_amount,
  115. AVG(price) as avg_price,
  116. MIN(price) as min_price,
  117. MAX(price) as max_price
  118. ')
  119. ->first();
  120. $latestPrice = self::getLatestPrice($itemId);
  121. return [
  122. 'item_id' => $itemId,
  123. 'days' => $days,
  124. 'total_transactions' => $stats->total_transactions ?? 0,
  125. 'total_quantity' => $stats->total_quantity ?? 0,
  126. 'total_amount' => $stats->total_amount ?? '0.00000',
  127. 'avg_price' => $stats->avg_price ? number_format($stats->avg_price, 5) : '0.00000',
  128. 'min_price' => $stats->min_price ?? '0.00000',
  129. 'max_price' => $stats->max_price ?? '0.00000',
  130. 'latest_price' => $latestPrice,
  131. 'start_date' => $startDate,
  132. 'end_date' => now(),
  133. ];
  134. }
  135. /**
  136. * 获取市场成交统计
  137. *
  138. * @param int $days 统计天数
  139. * @return array 统计信息
  140. */
  141. public static function getMarketStats(int $days = 7): array
  142. {
  143. $startDate = now()->subDays($days)->startOfDay();
  144. $stats = MexTransaction::where('created_at', '>=', $startDate)
  145. ->selectRaw('
  146. COUNT(*) as total_transactions,
  147. COUNT(DISTINCT item_id) as active_items,
  148. SUM(quantity) as total_quantity,
  149. SUM(total_amount) as total_amount,
  150. AVG(price) as avg_price
  151. ')
  152. ->first();
  153. // 按交易类型统计
  154. $typeStats = MexTransaction::where('created_at', '>=', $startDate)
  155. ->groupBy('transaction_type')
  156. ->selectRaw('
  157. transaction_type,
  158. COUNT(*) as count,
  159. SUM(quantity) as quantity,
  160. SUM(total_amount) as amount
  161. ')
  162. ->get();
  163. $typeStatsFormatted = [];
  164. foreach ($typeStats as $typeStat) {
  165. $typeStatsFormatted[$typeStat->transaction_type] = [
  166. 'count' => $typeStat->count,
  167. 'quantity' => $typeStat->quantity,
  168. 'amount' => $typeStat->amount,
  169. ];
  170. }
  171. return [
  172. 'days' => $days,
  173. 'total_transactions' => $stats->total_transactions ?? 0,
  174. 'active_items' => $stats->active_items ?? 0,
  175. 'total_quantity' => $stats->total_quantity ?? 0,
  176. 'total_amount' => $stats->total_amount ?? '0.00000',
  177. 'avg_price' => $stats->avg_price ? number_format($stats->avg_price, 5) : '0.00000',
  178. 'type_stats' => $typeStatsFormatted,
  179. 'start_date' => $startDate,
  180. 'end_date' => now(),
  181. ];
  182. }
  183. /**
  184. * 获取商品最新成交价格
  185. *
  186. * @param int $itemId 商品ID
  187. * @return string|null 最新价格
  188. */
  189. public static function getLatestPrice(int $itemId): ?string
  190. {
  191. $transaction = MexTransaction::where('item_id', $itemId)
  192. ->orderBy('created_at', 'desc')
  193. ->first();
  194. return $transaction ? $transaction->price : null;
  195. }
  196. /**
  197. * 创建成交记录
  198. *
  199. * @param array $data 成交数据
  200. * @return MexTransaction|null 成交记录
  201. */
  202. public static function createTransaction(array $data): ?MexTransaction
  203. {
  204. try {
  205. $transaction = MexTransaction::create([
  206. 'buy_order_id' => $data['buy_order_id'] ?? null,
  207. 'sell_order_id' => $data['sell_order_id'] ?? null,
  208. 'buyer_id' => $data['buyer_id'],
  209. 'seller_id' => $data['seller_id'],
  210. 'item_id' => $data['item_id'],
  211. 'currency_type' => $data['currency_type'] ?? 2, // 默认使用钻石币种
  212. 'quantity' => $data['quantity'],
  213. 'price' => $data['price'],
  214. 'total_amount' => $data['total_amount'],
  215. 'transaction_type' => $data['transaction_type'],
  216. 'is_admin_operation' => $data['is_admin_operation'] ?? false,
  217. 'admin_user_id' => $data['admin_user_id'] ?? null,
  218. ]);
  219. // 触发成交记录创建事件
  220. if ($transaction) {
  221. event(new TransactionCreatedEvent($transaction));
  222. }
  223. return $transaction;
  224. } catch (\Exception $e) {
  225. return null;
  226. }
  227. }
  228. /**
  229. * 获取最后撮合时间信息(使用撮合日志)
  230. *
  231. * @return array 最后撮合时间信息
  232. */
  233. public static function getLastMatchTimes(): array
  234. {
  235. // 使用撮合日志服务获取最后撮合时间
  236. return \App\Module\Mex\Services\MexMatchLogService::getLastMatchTimes();
  237. }
  238. /**
  239. * 获取商品价格趋势
  240. *
  241. * @param int $itemId 商品ID
  242. * @param int $days 统计天数
  243. * @return array 价格趋势数据
  244. */
  245. public static function getItemPriceTrend(int $itemId, int $days = 7): array
  246. {
  247. $startDate = now()->subDays($days)->startOfDay();
  248. $transactions = MexTransaction::where('item_id', $itemId)
  249. ->where('created_at', '>=', $startDate)
  250. ->orderBy('created_at', 'asc')
  251. ->select('price', 'created_at')
  252. ->get();
  253. $trend = [];
  254. foreach ($transactions as $transaction) {
  255. $date = $transaction->created_at->format('Y-m-d');
  256. if (!isset($trend[$date])) {
  257. $trend[$date] = [];
  258. }
  259. $trend[$date][] = $transaction->price;
  260. }
  261. // 计算每日平均价格
  262. $result = [];
  263. foreach ($trend as $date => $prices) {
  264. $avgPrice = array_sum($prices) / count($prices);
  265. $result[] = [
  266. 'date' => $date,
  267. 'avg_price' => number_format($avgPrice, 5),
  268. 'min_price' => min($prices),
  269. 'max_price' => max($prices),
  270. 'transaction_count' => count($prices),
  271. ];
  272. }
  273. return $result;
  274. }
  275. }