PriceTrendChart.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <?php
  2. namespace App\Module\Mex\Metrics;
  3. use App\Module\Fund\Enums\FUND_CURRENCY_TYPE;
  4. use App\Module\Mex\Models\MexDailyPriceTrend;
  5. use Dcat\Admin\Widgets\Metrics\Line;
  6. use Illuminate\Http\Request;
  7. /**
  8. * 农贸市场价格趋势图表 - 多线图(最低,最高,均价)
  9. */
  10. class PriceTrendChart extends Line
  11. {
  12. /**
  13. * 商品ID
  14. */
  15. protected $itemId;
  16. /**
  17. * 商品名称
  18. */
  19. protected $itemName;
  20. /**
  21. * 构造函数
  22. */
  23. public function __construct($itemId = null, $itemName = null)
  24. {
  25. $this->itemId = $itemId;
  26. $this->itemName = $itemName;
  27. parent::__construct();
  28. }
  29. /**
  30. * 初始化卡片内容
  31. *
  32. * @return void
  33. */
  34. protected function init()
  35. {
  36. parent::init();
  37. $title = $this->itemName ? "{$this->itemName} - 价格趋势" : '价格趋势';
  38. $this->title($title);
  39. $this->height(400);
  40. $this->chartHeight(300);
  41. // 设置下拉选项
  42. $this->dropdown([
  43. '7' => '最近 7 天',
  44. '14' => '最近 14 天',
  45. '30' => '最近 30 天',
  46. '90' => '最近 90 天',
  47. ]);
  48. }
  49. /**
  50. * 处理请求
  51. *
  52. * @param Request $request
  53. * @return mixed|void
  54. */
  55. public function handle(Request $request)
  56. {
  57. $days = (int) $request->get('option', 7);
  58. $itemId = $this->itemId ?? $request->get('item_id');
  59. $currencyType = $request->get('currency_type', FUND_CURRENCY_TYPE::ZUANSHI->value);
  60. $data = $this->getMultiLinePriceTrendData($days, $itemId, $currencyType);
  61. // 卡片内容 - 显示最新收盘价
  62. $latestPrice = $data['latest_close_price'] ?? 0;
  63. $priceChange = $data['price_change'] ?? 0;
  64. $changePercent = $data['change_percent'] ?? 0;
  65. $changeText = $priceChange >= 0 ? "+{$changePercent}%" : "{$changePercent}%";
  66. $this->withContent(number_format($latestPrice, 5), $changeText);
  67. // 添加详细信息
  68. $dataCount = count($data['dates']);
  69. $this->subTitle("数据点: {$dataCount} | 时间范围: {$days}天");
  70. // 图表数据 - 多线图
  71. $this->withMultiLineChart($data);
  72. }
  73. /**
  74. * 获取多线价格趋势数据
  75. *
  76. * @param int $days
  77. * @param int|null $itemId
  78. * @param int $currencyType
  79. * @return array
  80. */
  81. protected function getMultiLinePriceTrendData(int $days, ?int $itemId = null, int $currencyType = 2): array
  82. {
  83. $startDate = now()->subDays($days - 1)->toDateString();
  84. $endDate = now()->toDateString();
  85. if ($itemId) {
  86. // 如果指定了商品ID,显示该商品的价格趋势
  87. $trends = MexDailyPriceTrend::whereBetween('trade_date', [$startDate, $endDate])
  88. ->where('currency_type', $currencyType)
  89. ->where('item_id', $itemId)
  90. ->orderBy('trade_date')
  91. ->get();
  92. } else {
  93. // 如果没有指定商品ID,显示所有商品的平均价格趋势
  94. $trends = MexDailyPriceTrend::selectRaw('
  95. trade_date,
  96. AVG(open_price) as avg_open_price,
  97. AVG(close_price) as avg_close_price,
  98. AVG(high_price) as avg_high_price,
  99. AVG(low_price) as avg_low_price
  100. ')
  101. ->whereBetween('trade_date', [$startDate, $endDate])
  102. ->where('currency_type', $currencyType)
  103. ->groupBy('trade_date')
  104. ->orderBy('trade_date')
  105. ->get();
  106. }
  107. $dates = [];
  108. $openPrices = [];
  109. $closePrices = [];
  110. $highPrices = [];
  111. $lowPrices = [];
  112. $latestClosePrice = 0;
  113. $firstClosePrice = 0;
  114. foreach ($trends as $trend) {
  115. // 确保 trade_date 被正确解析为 Carbon 对象
  116. $tradeDate = $trend->trade_date instanceof \Carbon\Carbon
  117. ? $trend->trade_date
  118. : \Carbon\Carbon::parse($trend->trade_date);
  119. $dates[] = $tradeDate->format('m-d');
  120. if ($itemId) {
  121. // 单个商品的数据
  122. $openPrices[] = (float) $trend->open_price;
  123. $closePrices[] = (float) $trend->close_price;
  124. $highPrices[] = (float) $trend->high_price;
  125. $lowPrices[] = (float) $trend->low_price;
  126. if (!$firstClosePrice) {
  127. $firstClosePrice = (float) $trend->close_price;
  128. }
  129. $latestClosePrice = (float) $trend->close_price;
  130. } else {
  131. // 平均价格数据
  132. $openPrices[] = (float) $trend->avg_open_price;
  133. $closePrices[] = (float) $trend->avg_close_price;
  134. $highPrices[] = (float) $trend->avg_high_price;
  135. $lowPrices[] = (float) $trend->avg_low_price;
  136. if (!$firstClosePrice) {
  137. $firstClosePrice = (float) $trend->avg_close_price;
  138. }
  139. $latestClosePrice = (float) $trend->avg_close_price;
  140. }
  141. }
  142. // 计算价格变化
  143. $priceChange = $latestClosePrice - $firstClosePrice;
  144. $changePercent = $firstClosePrice > 0 ? round(($priceChange / $firstClosePrice) * 100, 2) : 0;
  145. return [
  146. 'dates' => $dates,
  147. 'open_prices' => $openPrices,
  148. 'close_prices' => $closePrices,
  149. 'high_prices' => $highPrices,
  150. 'low_prices' => $lowPrices,
  151. 'latest_close_price' => $latestClosePrice,
  152. 'price_change' => $priceChange,
  153. 'change_percent' => $changePercent,
  154. ];
  155. }
  156. /**
  157. * 设置多线图表数据
  158. *
  159. * @param array $data
  160. * @return $this
  161. */
  162. public function withMultiLineChart(array $data)
  163. {
  164. $series = [
  165. [
  166. 'name' => '最低价',
  167. 'data' => $data['low_prices'],
  168. ],
  169. [
  170. 'name' => '最高价',
  171. 'data' => $data['high_prices'],
  172. ],
  173. [
  174. 'name' => '收盘价',
  175. 'data' => $data['close_prices'],
  176. ],
  177. ];
  178. // 定义不同线条的颜色
  179. $colors = [
  180. '#52c41a', // 绿色 - 最低价
  181. '#f5222d', // 红色 - 最高价
  182. '#1890ff', // 蓝色 - 收盘价
  183. ];
  184. return $this->chart([
  185. 'series' => $series,
  186. 'colors' => $colors,
  187. 'xaxis' => [
  188. 'categories' => $data['dates'],
  189. 'labels' => [
  190. 'show' => true,
  191. ],
  192. ],
  193. 'legend' => [
  194. 'show' => true,
  195. 'position' => 'bottom',
  196. 'horizontalAlign' => 'center'
  197. ],
  198. 'tooltip' => [
  199. 'shared' => true,
  200. 'intersect' => false,
  201. ],
  202. ]);
  203. }
  204. /**
  205. * 设置图表数据(兼容旧方法)
  206. *
  207. * @param array $data
  208. * @param array $categories
  209. * @return $this
  210. */
  211. public function withChart(array $data, array $categories)
  212. {
  213. return $this->chart([
  214. 'series' => [
  215. [
  216. 'name' => '平均价格',
  217. 'data' => $data,
  218. ],
  219. ],
  220. 'xaxis' => [
  221. 'categories' => $categories,
  222. ],
  223. ]);
  224. }
  225. }