VolumeTrendChart.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. namespace App\Module\Mex\Metrics;
  3. use App\Module\Fund\Enums\FUND_CURRENCY_TYPE;
  4. use App\Module\Mex\Models\MexTransaction;
  5. use App\Module\Mex\Enums\TransactionType;
  6. use Dcat\Admin\Widgets\Metrics\Line;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Support\Facades\DB;
  9. /**
  10. * 农贸市场成交趋势图表 - 多线图(买入,卖出)
  11. */
  12. class VolumeTrendChart extends Line
  13. {
  14. /**
  15. * 商品ID
  16. */
  17. protected $itemId;
  18. /**
  19. * 商品名称
  20. */
  21. protected $itemName;
  22. /**
  23. * 构造函数
  24. */
  25. public function __construct($itemId = null, $itemName = null)
  26. {
  27. $this->itemId = $itemId;
  28. $this->itemName = $itemName;
  29. parent::__construct();
  30. }
  31. /**
  32. * 初始化卡片内容
  33. *
  34. * @return void
  35. */
  36. protected function init()
  37. {
  38. parent::init();
  39. $title = $this->itemName ? "{$this->itemName} - 成交趋势" : '成交趋势';
  40. $this->title($title);
  41. $this->height(400);
  42. $this->chartHeight(300);
  43. // 设置下拉选项
  44. $this->dropdown([
  45. '7' => '最近 7 天',
  46. '14' => '最近 14 天',
  47. '30' => '最近 30 天',
  48. '90' => '最近 90 天',
  49. ]);
  50. }
  51. /**
  52. * 处理请求
  53. *
  54. * @param Request $request
  55. * @return mixed|void
  56. */
  57. public function handle(Request $request)
  58. {
  59. $days = (int) $request->get('option', 7);
  60. $itemId = $this->itemId ?? $request->get('item_id');
  61. $currencyType = $request->get('currency_type', FUND_CURRENCY_TYPE::ZUANSHI->value);
  62. $data = $this->getVolumeTrendData($days, $itemId, $currencyType);
  63. // 卡片内容 - 显示总成交量
  64. $totalVolume = $data['total_volume'] ?? 0;
  65. $volumeChange = $data['volume_change'] ?? 0;
  66. $changePercent = $data['change_percent'] ?? 0;
  67. $changeText = $volumeChange >= 0 ? "+{$changePercent}%" : "{$changePercent}%";
  68. $this->withContent(number_format($totalVolume), $changeText);
  69. // 添加详细信息
  70. $dataCount = count($data['dates']);
  71. $this->subTitle("数据点: {$dataCount} | 时间范围: {$days}天");
  72. // 图表数据 - 多线图
  73. $this->withMultiLineChart($data);
  74. }
  75. /**
  76. * 获取成交趋势数据
  77. *
  78. * @param int $days
  79. * @param int|null $itemId
  80. * @param int $currencyType
  81. * @return array
  82. */
  83. protected function getVolumeTrendData(int $days, ?int $itemId = null, int $currencyType = 2): array
  84. {
  85. $startDate = now()->subDays($days - 1)->toDateString();
  86. $endDate = now()->toDateString();
  87. // 构建基础查询
  88. $query = MexTransaction::selectRaw('
  89. DATE(created_at) as trade_date,
  90. transaction_type,
  91. SUM(quantity) as total_quantity,
  92. COUNT(*) as transaction_count
  93. ')
  94. ->whereBetween(DB::raw('DATE(created_at)'), [$startDate, $endDate])
  95. ->where('currency_type', $currencyType)
  96. ->groupBy(DB::raw('DATE(created_at)'), 'transaction_type')
  97. ->orderBy(DB::raw('DATE(created_at)'));
  98. if ($itemId) {
  99. $query->where('item_id', $itemId);
  100. }
  101. $transactions = $query->get();
  102. // 生成日期范围
  103. $dates = [];
  104. $current = now()->subDays($days - 1);
  105. for ($i = 0; $i < $days; $i++) {
  106. $dates[] = $current->format('m-d');
  107. $current->addDay();
  108. }
  109. // 初始化数据数组
  110. $buyVolumes = array_fill(0, $days, 0);
  111. $sellVolumes = array_fill(0, $days, 0);
  112. // 填充数据
  113. foreach ($transactions as $transaction) {
  114. $tradeDate = \Carbon\Carbon::parse($transaction->trade_date);
  115. $dateIndex = now()->subDays($days - 1)->diffInDays($tradeDate);
  116. if ($dateIndex >= 0 && $dateIndex < $days) {
  117. if ($transaction->transaction_type == TransactionType::USER_BUY->value) {
  118. $buyVolumes[$dateIndex] = (int) $transaction->total_quantity;
  119. } elseif ($transaction->transaction_type == TransactionType::USER_SELL->value) {
  120. $sellVolumes[$dateIndex] = (int) $transaction->total_quantity;
  121. }
  122. }
  123. }
  124. // 计算总成交量和变化
  125. $totalVolume = array_sum($buyVolumes) + array_sum($sellVolumes);
  126. $recentVolume = end($buyVolumes) + end($sellVolumes);
  127. $firstVolume = $buyVolumes[0] + $sellVolumes[0];
  128. $volumeChange = $recentVolume - $firstVolume;
  129. $changePercent = $firstVolume > 0 ? round(($volumeChange / $firstVolume) * 100, 2) : 0;
  130. return [
  131. 'dates' => $dates,
  132. 'buy_volumes' => $buyVolumes,
  133. 'sell_volumes' => $sellVolumes,
  134. 'total_volume' => $totalVolume,
  135. 'volume_change' => $volumeChange,
  136. 'change_percent' => $changePercent,
  137. ];
  138. }
  139. /**
  140. * 设置多线图表数据
  141. *
  142. * @param array $data
  143. * @return $this
  144. */
  145. public function withMultiLineChart(array $data)
  146. {
  147. $series = [
  148. [
  149. 'name' => '买入量',
  150. 'data' => $data['buy_volumes'],
  151. ],
  152. [
  153. 'name' => '卖出量',
  154. 'data' => $data['sell_volumes'],
  155. ],
  156. ];
  157. // 定义不同线条的颜色
  158. $colors = [
  159. '#52c41a', // 绿色 - 买入量
  160. '#f5222d', // 红色 - 卖出量
  161. ];
  162. return $this->chart([
  163. 'series' => $series,
  164. 'colors' => $colors,
  165. 'xaxis' => [
  166. 'categories' => $data['dates'],
  167. 'labels' => [
  168. 'show' => true,
  169. ],
  170. ],
  171. 'legend' => [
  172. 'show' => true,
  173. 'position' => 'bottom',
  174. 'horizontalAlign' => 'center'
  175. ],
  176. 'tooltip' => [
  177. 'shared' => true,
  178. 'intersect' => false,
  179. ],
  180. 'yaxis' => [
  181. 'title' => [
  182. 'text' => '成交量'
  183. ]
  184. ],
  185. ]);
  186. }
  187. }