| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- /**
- * 实时金价 hooks(UniApp 单例模式实现)
- * @description 封装实时金价、黄金9999、铂金、白银、K金的价格获取与实时更新逻辑,通过单例WebSocket避免多页面连接超限,结合缓存减少接口请求
- *
- * 核心特性:
- * 1. 单例WebSocket:全局唯一连接,通过引用计数管理生命周期(页面全部隐藏时断开,任一页面显示时重连)
- * 2. 多金属支持:基础黄金(RTJ_Au)、黄金9999(AU9999)、铂金(RTJ_Pt)、白银(RTJ_Ag)、K金(自动计算,基础黄金75%)
- * 3. 数据自动处理:实时更新销售价/回收价/调整价,自动维护当日最高/最低价
- * 4. 缓存优化:接口请求结果缓存30秒,避免重复请求减轻服务器压力
- * 5. 错误容错:WebSocket断连自动重连(3秒延迟),数据解析异常捕获日志
- *
- * 依赖项:
- * - vue:ref/watch 响应式能力
- * - @dcloudio/uni-app:页面生命周期(onShow/onHide/onUnload)
- * - @/api/abacus:getCurrentPrice 接口(获取初始金价)
- * - pako:WebSocket消息解压(gzip格式)
- * - @/hooks/useSocket:WebSocket基础封装(需支持on/send/connect/disconnect方法)
- * - @/utils/weAtob:base64解码工具(处理WebSocket消息)
- *
- * 注意事项:
- * - WebSocket地址(wss://litews.jjh9999.com/wsv2/)、token(junit:test)
- * - 缓存时长(CACHE_DURATION)默认30秒,可根据需求调整
- * - K金价格依赖基础黄金自动计算(75%比例),无需额外接口请求
- */
- import { ref, watch } from "vue";
- import { onShow, onHide, onUnload } from "@dcloudio/uni-app";
- import { getCurrentPrice } from "@/api/abacus";
- import pako from "pako/dist/pako.min.js";
- import useWebSocket from "@/hooks/useSocket";
- import { weAtob } from "@/utils";
- // WebSocket单例核心变量
- let wsInstance = null;
- let connectCount = 0;
- let isConnected = false;
- const dataCallbacks = new Set();
- // 初始化WebSocket单例
- const initWSSingleton = () => {
- if (wsInstance) return wsInstance;
- // 创建WebSocket实例
- const ws = useWebSocket("wss://litews.jjh9999.com/wsv2/", {
- heartbeat: false,
- heartbeatInterval: 20000,
- });
- // 初始化参数
- ws.on("open", () => {
- console.log("WS单例连接成功");
- isConnected = true;
- ws.send({
- company_id: "2289041670615554",
- id: "2189041670615552",
- code: 1,
- token: "junit:test",
- type: 1,
- });
- });
- // 接收消息:统一解析并分发给所有注册的回调
- ws.on("message", (msg) => {
- try {
- const strData = weAtob(msg.data);
- const charData = strData.split("").map((x) => x.charCodeAt(0));
- const binData = new Uint8Array(charData);
- const tempData = pako.inflate(binData, { to: "string" });
- const { data } = JSON.parse(tempData);
- if (data && data?.code !== 0) {
- const itemData = JSON.parse(data);
- if (itemData?.priceDataNew) {
- const priceData = {
- itemAu: itemData.priceDataNew.find((v) => v.gmCode === "RTJ_Au"),
- itemAu9999: itemData.priceDataNew.find(
- (v) => v.gmCode === "AU9999"
- ),
- itemPt: itemData.priceDataNew.find((v) => v.gmCode === "RTJ_Pt"),
- itemAg: itemData.priceDataNew.find((v) => v.gmCode === "RTJ_Ag"),
- };
- dataCallbacks.forEach((callback) => callback(priceData));
- }
- }
- } catch (error) {
- console.error("WS单例消息处理错误:", error);
- }
- });
- // 连接关闭:若仍有页面使用,3秒后自动重连
- ws.on("close", () => {
- isConnected = false;
- if (connectCount > 0) {
- setTimeout(() => ws.connect(), 3000);
- }
- });
- ws.on("error", (error) => {
- console.error("WS单例连接错误:", error);
- isConnected = false;
- });
- wsInstance = ws;
- return ws;
- };
- const CACHE_DURATION = 30 * 1000; // 缓存有效期:30秒
- const fetchCache = new Map();
- export default function useRealGoldPrice(options) {
- const { realCode = "All" } = options || {};
- // 基础黄金(RTJ_Au)
- const realGoldprice = ref(0); // 实时销售价
- const realGoldRecyclePrice = ref(0); // 实时回收价
- const goldAdjustPrice = ref(0); // 调整价
- const goldTodayHigh = ref(0); // 今日最高价
- const goldTodayLow = ref(0); // 今日最低价
- // 黄金9999(AU9999)
- const realAu9999price = ref(0);
- const realAu9999RecyclePrice = ref(0);
- const Au9999AdjustPrice = ref(0);
- const au9999TodayHigh = ref(0);
- const au9999TodayLow = ref(0);
- // 铂金(RTJ_Pt)
- const realPtprice = ref(0);
- const realPtRecyclePrice = ref(0);
- const PtAdjustPrice = ref(0);
- const ptTodayHigh = ref(0);
- const ptTodayLow = ref(0);
- // 白银(RTJ_Ag)
- const realAgprice = ref(0);
- const realAgRecyclePrice = ref(0);
- const AgAdjustPrice = ref(0);
- const agTodayHigh = ref(0);
- const agTodayLow = ref(0);
- // K金(基础黄金75%自动计算)
- const realKGoldprice = ref(0);
- const realKGoldRecyclePrice = ref(0);
- const KGoldAdjustPrice = ref(0);
- const kGoldTodayHigh = ref(0);
- const kGoldTodayLow = ref(0);
- // 当前价(简化快捷访问)
- const currentGoldPrice = ref(0);
- const currentAu9999Price = ref(0);
- const currentPtPrice = ref(0);
- const currentAgPrice = ref(0);
- const currentKGoldPrice = ref(0);
- // 更新当日最高/最低价
- const updateHighLowPrices = (currentPrice, highRef, lowRef) => {
- const price = Number(currentPrice);
- if (isNaN(price)) return;
- // 首次设置价格时,同步初始化最高/最低价
- if (highRef.value === 0 && lowRef.value === 0) {
- highRef.value = price;
- lowRef.value = price;
- return;
- }
- highRef.value = Math.max(highRef.value, price);
- lowRef.value = Math.min(lowRef.value, price);
- };
- // 统一更新金属价格(销售价/回收价)
- const updatePrice = (
- item,
- sellPriceRef,
- recyclePriceRef,
- adjustPriceRef,
- highRef,
- lowRef
- ) => {
- if (item?.sellPrice?.price) {
- const sellPrice = (
- Number(item.sellPrice.price) + Number(adjustPriceRef.value)
- ).toFixed(2);
- sellPriceRef.value = sellPrice;
- updateHighLowPrices(sellPrice, highRef, lowRef);
- }
- if (item?.buyPrice?.price) {
- recyclePriceRef.value = (
- Number(item.buyPrice.price) + Number(adjustPriceRef.value)
- ).toFixed(2);
- }
- };
- watch(
- [realGoldprice, realGoldRecyclePrice, goldAdjustPrice],
- ([newSell, newRecycle, newAdjust]) => {
- const kSell = (Number(newSell) * 0.75).toFixed(2);
- realKGoldprice.value = kSell;
- realKGoldRecyclePrice.value = (Number(newRecycle) * 0.75).toFixed(2);
- KGoldAdjustPrice.value = (Number(newAdjust) * 0.75).toFixed(2);
- currentKGoldPrice.value = kSell;
- updateHighLowPrices(kSell, kGoldTodayHigh, kGoldTodayLow);
- },
- { immediate: true }
- );
- // WebSocket单例集成
- const ws = initWSSingleton();
- // 当前页面的价格更新回调(接收单例分发的数据)
- const handlePriceUpdate = (priceData) => {
- const { itemAu, itemAu9999, itemPt, itemAg } = priceData;
- // 根据realCode筛选需要更新的金属类型
- switch (realCode) {
- case "RTJ_Au":
- updatePrice(
- itemAu,
- realGoldprice,
- realGoldRecyclePrice,
- goldAdjustPrice,
- goldTodayHigh,
- goldTodayLow
- );
- currentGoldPrice.value = realGoldprice.value;
- break;
- case "AU9999":
- updatePrice(
- itemAu9999,
- realAu9999price,
- realAu9999RecyclePrice,
- Au9999AdjustPrice,
- au9999TodayHigh,
- au9999TodayLow
- );
- currentAu9999Price.value = realAu9999price.value;
- break;
- case "RTJ_Pt":
- updatePrice(
- itemPt,
- realPtprice,
- realPtRecyclePrice,
- PtAdjustPrice,
- ptTodayHigh,
- ptTodayLow
- );
- currentPtPrice.value = realPtprice.value;
- break;
- case "RTJ_Ag":
- updatePrice(
- itemAg,
- realAgprice,
- realAgRecyclePrice,
- AgAdjustPrice,
- agTodayHigh,
- agTodayLow
- );
- currentAgPrice.value = realAgprice.value;
- break;
- case "RTJ_KGold":
- break; // K金依赖基础黄金,无需额外处理
- case "All":
- updatePrice(
- itemAu,
- realGoldprice,
- realGoldRecyclePrice,
- goldAdjustPrice,
- goldTodayHigh,
- goldTodayLow
- );
- updatePrice(
- itemAu9999,
- realAu9999price,
- realAu9999RecyclePrice,
- Au9999AdjustPrice,
- au9999TodayHigh,
- au9999TodayLow
- );
- updatePrice(
- itemPt,
- realPtprice,
- realPtRecyclePrice,
- PtAdjustPrice,
- ptTodayHigh,
- ptTodayLow
- );
- updatePrice(
- itemAg,
- realAgprice,
- realAgRecyclePrice,
- AgAdjustPrice,
- agTodayHigh,
- agTodayLow
- );
- currentGoldPrice.value = realGoldprice.value;
- currentAu9999Price.value = realAu9999price.value;
- currentPtPrice.value = realPtprice.value;
- currentAgPrice.value = realAgprice.value;
- break;
- }
- };
- onShow(() => {
- connectCount++;
- if (connectCount === 1 && !isConnected) {
- ws.connect();
- }
- initPriceData();
- });
- onHide(() => {
- connectCount = Math.max(connectCount - 1, 0);
- if (connectCount === 0 && isConnected) {
- ws.disconnect();
- }
- });
- onUnload(() => {
- // 页面卸载时移除回调,避免内存泄漏
- dataCallbacks.delete(handlePriceUpdate);
- connectCount = Math.max(connectCount - 1, 0);
- if (connectCount === 0 && isConnected) {
- ws.disconnect();
- }
- });
- // 注册当前页面的价格更新回调
- dataCallbacks.add(handlePriceUpdate);
- // 主动获取金属价格(支持缓存)
- async function fetchGoldPrice(code = "RTJ_Au", tradeType = 3) {
- const cacheKey = `${code}_${tradeType}`;
- const now = Date.now();
- // 优先使用缓存(30秒内有效)
- const cached = fetchCache.get(cacheKey);
- if (cached && now - cached.lastFetchTime < CACHE_DURATION) {
- handleFetchData(cached.data, code, tradeType);
- return cached.data;
- }
- // 缓存失效,发起接口请求
- const params = { tradeType };
- if (code === "AU9999" || code === "RTJ_Pt" || code === "RTJ_Ag") {
- params.code = code;
- }
- try {
- const { data } = await getCurrentPrice(params);
- // 缓存请求结果
- fetchCache.set(cacheKey, { lastFetchTime: now, data });
- // 处理并更新价格
- handleFetchData(data, code, tradeType);
- return data;
- } catch (error) {
- console.error(`获取${code}价格失败:`, error);
- throw error;
- }
- }
- // 根据realCode初始化对应金属价格
- const initPriceData = () => {
- const tasks = [];
- if (realCode === "All" || realCode === "RTJ_Au")
- tasks.push(fetchGoldPrice("RTJ_Au"));
- if (realCode === "All" || realCode === "AU9999")
- tasks.push(fetchGoldPrice("AU9999"));
- if (realCode === "All" || realCode === "RTJ_Pt")
- tasks.push(fetchGoldPrice("RTJ_Pt"));
- if (realCode === "All" || realCode === "RTJ_Ag")
- tasks.push(fetchGoldPrice("RTJ_Ag"));
- Promise.all(tasks).catch((err) => console.error("初始化价格失败:", err));
- };
- // 处理接口返回的价格数据
- const handleFetchData = (data, code, tradeType) => {
- let sellRef, recycleRef, adjustRef, highRef, lowRef;
- // 匹配对应金属的响应式变量
- switch (code) {
- case "AU9999":
- [sellRef, recycleRef, adjustRef, highRef, lowRef] = [
- realAu9999price,
- realAu9999RecyclePrice,
- Au9999AdjustPrice,
- au9999TodayHigh,
- au9999TodayLow,
- ];
- break;
- case "RTJ_Pt":
- [sellRef, recycleRef, adjustRef, highRef, lowRef] = [
- realPtprice,
- realPtRecyclePrice,
- PtAdjustPrice,
- ptTodayHigh,
- ptTodayLow,
- ];
- break;
- case "RTJ_Ag":
- [sellRef, recycleRef, adjustRef, highRef, lowRef] = [
- realAgprice,
- realAgRecyclePrice,
- AgAdjustPrice,
- agTodayHigh,
- agTodayLow,
- ];
- break;
- default: // RTJ_Au(基础黄金)
- [sellRef, recycleRef, adjustRef, highRef, lowRef] = [
- realGoldprice,
- realGoldRecyclePrice,
- goldAdjustPrice,
- goldTodayHigh,
- goldTodayLow,
- ];
- break;
- }
- // 计算最终价格(调整价+基础价)
- const adjustPrice = tradeType === 3 ? 0 : data.adjustPrice;
- adjustRef.value = adjustPrice;
- const sellPrice = (Number(adjustPrice) + Number(data.sellPrice)).toFixed(2);
- sellRef.value = sellPrice;
- recycleRef.value = (
- Number(adjustPrice) + Number(data.buyPrice || 0)
- ).toFixed(2);
- updateHighLowPrices(sellPrice, highRef, lowRef);
- // 更新当前价快捷访问变量
- if (code === "RTJ_Au") currentGoldPrice.value = sellPrice;
- if (code === "AU9999") currentAu9999Price.value = sellPrice;
- if (code === "RTJ_Pt") currentPtPrice.value = sellPrice;
- if (code === "RTJ_Ag") currentAgPrice.value = sellPrice;
- };
- return {
- // 基础黄金相关
- realGoldprice,
- realGoldRecyclePrice,
- goldAdjustPrice,
- goldTodayHigh,
- goldTodayLow,
- // 黄金9999相关
- realAu9999price,
- realAu9999RecyclePrice,
- Au9999AdjustPrice,
- currentAu9999Price,
- au9999TodayHigh,
- au9999TodayLow,
- // 铂金相关
- realPtprice,
- realPtRecyclePrice,
- PtAdjustPrice,
- ptTodayHigh,
- ptTodayLow,
- // 白银相关
- realAgprice,
- realAgRecyclePrice,
- AgAdjustPrice,
- agTodayHigh,
- agTodayLow,
- // K金相关
- realKGoldprice,
- realKGoldRecyclePrice,
- KGoldAdjustPrice,
- kGoldTodayHigh,
- kGoldTodayLow,
- // 当前价快捷访问
- currentGoldPrice,
- currentPtPrice,
- currentAgPrice,
- currentKGoldPrice,
- // 主动获取价格方法
- fetchGoldPrice,
- };
- }
|