From 49f82f7c73fb6f806733fe0873e5c8488df06750 Mon Sep 17 00:00:00 2001 From: peijunlei Date: Mon, 7 Sep 2026 13:15:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(stock):=20=E5=A4=84=E7=90=86=E8=82=A1?= =?UTF-8?q?=E7=A5=A8=E4=BF=A1=E6=81=AF=E7=BC=96=E7=A0=81=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BB=A5=E7=A1=AE=E4=BF=9D=E6=95=B0=E6=8D=AE=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在获取股票信息时,增加对缓存数据的编码检查和转换,确保名称字段为UTF-8编码 - 在从新浪接口获取数据时,添加编码转换以避免JSON解析错误 - 更新前端代码以确保所有股票信息在JSON中可正确编码 --- app/common.php | 18 ++++++++++++++++-- app/controller/frontend/StockController.php | 6 ++++++ public/themes/dist_static/static/js/common.js | 16 ++++++++++++---- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/app/common.php b/app/common.php index f3885a6..a7d6507 100644 --- a/app/common.php +++ b/app/common.php @@ -1758,7 +1758,12 @@ if (!function_exists('getStockInfo')) { // 尝试从缓存获取 $cacheKey = 'stock_info_' . $stockCode; $cached = \think\facade\Cache::get($cacheKey); - if (!empty($cached)) { + if (!empty($cached) && is_array($cached)) { + // 清理历史缓存里可能残留的非 UTF-8 字段 + if (!empty($cached['name']) && !mb_check_encoding((string)$cached['name'], 'UTF-8')) { + $cached['name'] = mb_convert_encoding((string)$cached['name'], 'UTF-8', 'GBK'); + \think\facade\Cache::set($cacheKey, $cached, 60 * 15 - 10); + } return $cached; } @@ -1807,6 +1812,11 @@ if (!function_exists('fetchStockFromSina')) { return null; } + // 新浪接口默认 GBK,转 UTF-8 后再解析,避免 JSON 报 Malformed UTF-8 + if (!mb_check_encoding($response, 'UTF-8')) { + $response = mb_convert_encoding($response, 'UTF-8', 'GBK'); + } + // 解析新浪数据格式:var hq_str_sh603933="名称,开盘价,昨收,当前价,最高,最低,..."; if (preg_match('/="(.+)"/', $response, $matches)) { $fields = explode(',', $matches[1]); @@ -1815,10 +1825,14 @@ if (!function_exists('fetchStockFromSina')) { $prevClose = floatval($fields[2]); $change = round($price - $prevClose, 2); $changePercent = $prevClose > 0 ? round(($change / $prevClose) * 100, 2) : 0; + $name = $fields[0]; + if (!mb_check_encoding($name, 'UTF-8')) { + $name = mb_convert_encoding($name, 'UTF-8', 'GBK'); + } return [ 'code' => $stockCode, - 'name' => $fields[0], + 'name' => $name, 'price' => $price > 0 ? number_format($price, 2) : '--', 'change' => $price > 0 ? (($change >= 0 ? '+' : '') . number_format($change, 2)) : '--', 'changePercent' => $price > 0 ? (($changePercent >= 0 ? '+' : '') . number_format($changePercent, 2) . '%') : '--', diff --git a/app/controller/frontend/StockController.php b/app/controller/frontend/StockController.php index 1b05e30..83a226c 100644 --- a/app/controller/frontend/StockController.php +++ b/app/controller/frontend/StockController.php @@ -16,6 +16,12 @@ class StockController extends BaseController } $stock = getStockInfo($code); + // 确保 JSON 可编码(防止缓存脏数据) + foreach ($stock as $k => $v) { + if (is_string($v) && $v !== '' && !mb_check_encoding($v, 'UTF-8')) { + $stock[$k] = mb_convert_encoding($v, 'UTF-8', 'GBK'); + } + } $change = (string)($stock['change'] ?? '--'); $color = ''; if ($change !== '--' && $change !== '') { diff --git a/public/themes/dist_static/static/js/common.js b/public/themes/dist_static/static/js/common.js index 2bd5ab8..454efe1 100644 --- a/public/themes/dist_static/static/js/common.js +++ b/public/themes/dist_static/static/js/common.js @@ -283,9 +283,16 @@ $(function() { timeEl.textContent = `截止 ${data.updateTime}*报价有十五分钟或以上延迟。`; } - const chartEl = document.querySelector('[data-stock="chart"]'); - if (chartEl && data.chart) { - chartEl.src = `${data.chart}${data.chart.includes("?") ? "&" : "?"}t=${Date.now()}`; + const chartEl = document.querySelector('#stock-chart, [data-stock="chart"]'); + if (chartEl) { + const code = + document.querySelector("[data-stock-quote]")?.getAttribute("data-stock-code") || + data.code || + "603933"; + const market = String(code).charAt(0) === "6" ? "sh" : "sz"; + const raw = String(data.chart || `https://image.sinajs.cn/newchart/min/n/${market}${code}.gif`); + const base = raw.split("?")[0]; + chartEl.setAttribute("src", `${base}?t=${Date.now()}`); } }; @@ -314,8 +321,9 @@ $(function() { timer = null; }; - if (!document.querySelector("[data-stock-quote], [data-stock='chart']")) return; + if (!document.querySelector("[data-stock-quote], [data-stock='chart'], #stock-chart")) return; + fetchQuote(); start(); document.addEventListener("visibilitychange", () => { if (document.hidden) {