Browse Source

fix(stock): 处理股票信息编码问题以确保数据完整性

- 在获取股票信息时,增加对缓存数据的编码检查和转换,确保名称字段为UTF-8编码
- 在从新浪接口获取数据时,添加编码转换以避免JSON解析错误
- 更新前端代码以确保所有股票信息在JSON中可正确编码
master
peijunlei 2 weeks ago
parent
commit
49f82f7c73
  1. 18
      app/common.php
  2. 6
      app/controller/frontend/StockController.php
  3. 16
      public/themes/dist_static/static/js/common.js

18
app/common.php

@ -1758,7 +1758,12 @@ if (!function_exists('getStockInfo')) {
// 尝试从缓存获取 // 尝试从缓存获取
$cacheKey = 'stock_info_' . $stockCode; $cacheKey = 'stock_info_' . $stockCode;
$cached = \think\facade\Cache::get($cacheKey); $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; return $cached;
} }
@ -1807,6 +1812,11 @@ if (!function_exists('fetchStockFromSina')) {
return null; 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="名称,开盘价,昨收,当前价,最高,最低,..."; // 解析新浪数据格式:var hq_str_sh603933="名称,开盘价,昨收,当前价,最高,最低,...";
if (preg_match('/="(.+)"/', $response, $matches)) { if (preg_match('/="(.+)"/', $response, $matches)) {
$fields = explode(',', $matches[1]); $fields = explode(',', $matches[1]);
@ -1815,10 +1825,14 @@ if (!function_exists('fetchStockFromSina')) {
$prevClose = floatval($fields[2]); $prevClose = floatval($fields[2]);
$change = round($price - $prevClose, 2); $change = round($price - $prevClose, 2);
$changePercent = $prevClose > 0 ? round(($change / $prevClose) * 100, 2) : 0; $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 [ return [
'code' => $stockCode, 'code' => $stockCode,
'name' => $fields[0],
'name' => $name,
'price' => $price > 0 ? number_format($price, 2) : '--', 'price' => $price > 0 ? number_format($price, 2) : '--',
'change' => $price > 0 ? (($change >= 0 ? '+' : '') . number_format($change, 2)) : '--', 'change' => $price > 0 ? (($change >= 0 ? '+' : '') . number_format($change, 2)) : '--',
'changePercent' => $price > 0 ? (($changePercent >= 0 ? '+' : '') . number_format($changePercent, 2) . '%') : '--', 'changePercent' => $price > 0 ? (($changePercent >= 0 ? '+' : '') . number_format($changePercent, 2) . '%') : '--',

6
app/controller/frontend/StockController.php

@ -16,6 +16,12 @@ class StockController extends BaseController
} }
$stock = getStockInfo($code); $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'] ?? '--'); $change = (string)($stock['change'] ?? '--');
$color = ''; $color = '';
if ($change !== '--' && $change !== '') { if ($change !== '--' && $change !== '') {

16
public/themes/dist_static/static/js/common.js

@ -283,9 +283,16 @@ $(function() {
timeEl.textContent = `截止 ${data.updateTime}*报价有十五分钟或以上延迟。`; 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; 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(); start();
document.addEventListener("visibilitychange", () => { document.addEventListener("visibilitychange", () => {
if (document.hidden) { if (document.hidden) {

Loading…
Cancel
Save