MexTransactionLogic.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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. $result[] = [
  34. 'id' => $transaction->id,
  35. 'item_id' => $transaction->item_id,
  36. 'quantity' => $transaction->quantity,
  37. 'price' => $transaction->price,
  38. 'total_amount' => $transaction->total_amount,
  39. 'transaction_type' => $transaction->transaction_type->value,
  40. 'transaction_type_desc' => $transaction->transaction_type->getDescription(),
  41. 'created_at' => $transaction->created_at,
  42. ];
  43. }
  44. return [
  45. 'transactions' => $result,
  46. 'total' => $transactions->total(),
  47. 'page' => $page,
  48. 'page_size' => $pageSize,
  49. ];
  50. }
  51. /**
  52. * 获取用户成交记录
  53. *
  54. * @param int $userId 用户ID
  55. * @param int $page 页码
  56. * @param int $pageSize 每页数量
  57. * @return array 成交记录列表
  58. */
  59. public static function getUserTransactions(int $userId, int $page = 1, int $pageSize = 20): array
  60. {
  61. $transactions = MexTransaction::where(function ($query) use ($userId) {
  62. $query->where('buyer_id', $userId)
  63. ->orWhere('seller_id', $userId);
  64. })
  65. ->where('is_admin_operation', false)
  66. ->orderBy('created_at', 'desc')
  67. ->paginate($pageSize, ['*'], 'page', $page);
  68. $result = [];
  69. foreach ($transactions->items() as $transaction) {
  70. $isUserBuyer = $transaction->buyer_id === $userId;
  71. $isUserSeller = $transaction->seller_id === $userId;
  72. $result[] = [
  73. 'id' => $transaction->id,
  74. 'item_id' => $transaction->item_id,
  75. 'quantity' => $transaction->quantity,
  76. 'price' => $transaction->price,
  77. 'total_amount' => $transaction->total_amount,
  78. 'transaction_type' => $transaction->transaction_type->value,
  79. 'transaction_type_desc' => $transaction->transaction_type->getDescription(),
  80. 'user_role' => $isUserBuyer ? 'buyer' : ($isUserSeller ? 'seller' : 'unknown'),
  81. 'user_role_desc' => $isUserBuyer ? '买方' : ($isUserSeller ? '卖方' : '未知'),
  82. 'counterparty_id' => $isUserBuyer ? $transaction->seller_id : $transaction->buyer_id,
  83. 'created_at' => $transaction->created_at,
  84. ];
  85. }
  86. return [
  87. 'transactions' => $result,
  88. 'total' => $transactions->total(),
  89. 'page' => $page,
  90. 'page_size' => $pageSize,
  91. ];
  92. }
  93. /**
  94. * 获取商品成交统计
  95. *
  96. * @param int $itemId 商品ID
  97. * @param int $days 统计天数
  98. * @return array 统计信息
  99. */
  100. public static function getItemTransactionStats(int $itemId, int $days = 7): array
  101. {
  102. $startDate = now()->subDays($days)->startOfDay();
  103. $stats = MexTransaction::where('item_id', $itemId)
  104. ->where('created_at', '>=', $startDate)
  105. ->selectRaw('
  106. COUNT(*) as total_transactions,
  107. SUM(quantity) as total_quantity,
  108. SUM(total_amount) as total_amount,
  109. AVG(price) as avg_price,
  110. MIN(price) as min_price,
  111. MAX(price) as max_price
  112. ')
  113. ->first();
  114. $latestPrice = self::getLatestPrice($itemId);
  115. return [
  116. 'item_id' => $itemId,
  117. 'days' => $days,
  118. 'total_transactions' => $stats->total_transactions ?? 0,
  119. 'total_quantity' => $stats->total_quantity ?? 0,
  120. 'total_amount' => $stats->total_amount ?? '0.00000',
  121. 'avg_price' => $stats->avg_price ? number_format($stats->avg_price, 5) : '0.00000',
  122. 'min_price' => $stats->min_price ?? '0.00000',
  123. 'max_price' => $stats->max_price ?? '0.00000',
  124. 'latest_price' => $latestPrice,
  125. 'start_date' => $startDate,
  126. 'end_date' => now(),
  127. ];
  128. }
  129. /**
  130. * 获取市场成交统计
  131. *
  132. * @param int $days 统计天数
  133. * @return array 统计信息
  134. */
  135. public static function getMarketStats(int $days = 7): array
  136. {
  137. $startDate = now()->subDays($days)->startOfDay();
  138. $stats = MexTransaction::where('created_at', '>=', $startDate)
  139. ->selectRaw('
  140. COUNT(*) as total_transactions,
  141. COUNT(DISTINCT item_id) as active_items,
  142. SUM(quantity) as total_quantity,
  143. SUM(total_amount) as total_amount,
  144. AVG(price) as avg_price
  145. ')
  146. ->first();
  147. // 按交易类型统计
  148. $typeStats = MexTransaction::where('created_at', '>=', $startDate)
  149. ->groupBy('transaction_type')
  150. ->selectRaw('
  151. transaction_type,
  152. COUNT(*) as count,
  153. SUM(quantity) as quantity,
  154. SUM(total_amount) as amount
  155. ')
  156. ->get();
  157. $typeStatsFormatted = [];
  158. foreach ($typeStats as $typeStat) {
  159. $typeStatsFormatted[$typeStat->transaction_type] = [
  160. 'count' => $typeStat->count,
  161. 'quantity' => $typeStat->quantity,
  162. 'amount' => $typeStat->amount,
  163. ];
  164. }
  165. return [
  166. 'days' => $days,
  167. 'total_transactions' => $stats->total_transactions ?? 0,
  168. 'active_items' => $stats->active_items ?? 0,
  169. 'total_quantity' => $stats->total_quantity ?? 0,
  170. 'total_amount' => $stats->total_amount ?? '0.00000',
  171. 'avg_price' => $stats->avg_price ? number_format($stats->avg_price, 5) : '0.00000',
  172. 'type_stats' => $typeStatsFormatted,
  173. 'start_date' => $startDate,
  174. 'end_date' => now(),
  175. ];
  176. }
  177. /**
  178. * 获取商品最新成交价格
  179. *
  180. * @param int $itemId 商品ID
  181. * @return string|null 最新价格
  182. */
  183. public static function getLatestPrice(int $itemId): ?string
  184. {
  185. $transaction = MexTransaction::where('item_id', $itemId)
  186. ->orderBy('created_at', 'desc')
  187. ->first();
  188. return $transaction ? $transaction->price : null;
  189. }
  190. /**
  191. * 创建成交记录
  192. *
  193. * @param array $data 成交数据
  194. * @return MexTransaction|null 成交记录
  195. */
  196. public static function createTransaction(array $data): ?MexTransaction
  197. {
  198. try {
  199. $transaction = MexTransaction::create([
  200. 'buy_order_id' => $data['buy_order_id'] ?? null,
  201. 'sell_order_id' => $data['sell_order_id'] ?? null,
  202. 'buyer_id' => $data['buyer_id'],
  203. 'seller_id' => $data['seller_id'],
  204. 'item_id' => $data['item_id'],
  205. 'quantity' => $data['quantity'],
  206. 'price' => $data['price'],
  207. 'total_amount' => $data['total_amount'],
  208. 'transaction_type' => $data['transaction_type'],
  209. 'is_admin_operation' => $data['is_admin_operation'] ?? false,
  210. 'admin_user_id' => $data['admin_user_id'] ?? null,
  211. ]);
  212. // 触发成交记录创建事件
  213. if ($transaction) {
  214. event(new TransactionCreatedEvent($transaction));
  215. }
  216. return $transaction;
  217. } catch (\Exception $e) {
  218. return null;
  219. }
  220. }
  221. /**
  222. * 获取商品价格趋势
  223. *
  224. * @param int $itemId 商品ID
  225. * @param int $days 统计天数
  226. * @return array 价格趋势数据
  227. */
  228. public static function getItemPriceTrend(int $itemId, int $days = 7): array
  229. {
  230. $startDate = now()->subDays($days)->startOfDay();
  231. $transactions = MexTransaction::where('item_id', $itemId)
  232. ->where('created_at', '>=', $startDate)
  233. ->orderBy('created_at', 'asc')
  234. ->select('price', 'created_at')
  235. ->get();
  236. $trend = [];
  237. foreach ($transactions as $transaction) {
  238. $date = $transaction->created_at->format('Y-m-d');
  239. if (!isset($trend[$date])) {
  240. $trend[$date] = [];
  241. }
  242. $trend[$date][] = $transaction->price;
  243. }
  244. // 计算每日平均价格
  245. $result = [];
  246. foreach ($trend as $date => $prices) {
  247. $avgPrice = array_sum($prices) / count($prices);
  248. $result[] = [
  249. 'date' => $date,
  250. 'avg_price' => number_format($avgPrice, 5),
  251. 'min_price' => min($prices),
  252. 'max_price' => max($prices),
  253. 'transaction_count' => count($prices),
  254. ];
  255. }
  256. return $result;
  257. }
  258. }