Przeglądaj źródła

增加OB数据记录

flyzto 1 tydzień temu
rodzic
commit
4bb7dc4604

+ 21 - 0
pinnacle/PINNACLE_PROXY_FRONTEND.md

@@ -2,6 +2,27 @@
 
 
 前端访问 `/api/pinnacle` 时,需要使用动态签名。请求中不会直接传密钥。
 前端访问 `/api/pinnacle` 时,需要使用动态签名。请求中不会直接传密钥。
 
 
+## 访问地址
+
+公网访问:
+
+```txt
+https://cb.long.bid
+```
+
+内网访问:
+
+```txt
+http://172.27.74.243
+```
+
+接口路径统一以 `/api/pinnacle` 开头,例如:
+
+```txt
+https://cb.long.bid/api/pinnacle/events
+http://172.27.74.243/api/pinnacle/events
+```
+
 ## 支持的接口
 ## 支持的接口
 
 
 本地数据接口:
 本地数据接口:

+ 8 - 1
server/models/GamesPs.js

@@ -465,7 +465,7 @@ const syncBaseEvents = ({ mk, games, outrights }) => {
     const { eventId, originId, stage, retime, score, wm, evtime, events } = game;
     const { eventId, originId, stage, retime, score, wm, evtime, events } = game;
     const baseGame = baseMap.get(eventId);
     const baseGame = baseMap.get(eventId);
     if (baseGame) {
     if (baseGame) {
-      OddsHistory.recordGameOdds({ game: { ...baseGame, originId }, events })
+      OddsHistory.recordGameOdds({ game: { ...baseGame, originId }, events, platform: 'pc' })
       .catch(err => {
       .catch(err => {
         Logs.out('record odds history failed, eventId %s, %s', eventId, err.message);
         Logs.out('record odds history failed, eventId %s, %s', eventId, err.message);
       });
       });
@@ -697,6 +697,13 @@ const updateGamesEvents = ({ platform, mk, games, outrights, timestamp, tp }) =>
       const baseEvents = baseMap.get(relatedGame.baseId)?.events ?? {};
       const baseEvents = baseMap.get(relatedGame.baseId)?.events ?? {};
       compareOdds(events, baseEvents, platform);
       compareOdds(events, baseEvents, platform);
       if (relatedGame) {
       if (relatedGame) {
+        const baseGame = baseMap.get(relatedGame.baseId);
+        if (platform == 'ob' && baseGame) {
+          OddsHistory.recordGameOdds({ game: { ...baseGame, originId }, events, platform })
+          .catch(err => {
+            Logs.out('record odds history failed, platform %s, eventId %s, %s', platform, relatedGame.baseId, err.message);
+          });
+        }
         if (typeof originId != 'undefined') {
         if (typeof originId != 'undefined') {
           relatedGame.originId = originId;
           relatedGame.originId = originId;
         }
         }

+ 78 - 11
server/models/OddsHistory.js

@@ -9,6 +9,9 @@ const TRACKED_IOR_KEYS = [
 ];
 ];
 
 
 const TRACKED_IOR_SET = new Set(TRACKED_IOR_KEYS);
 const TRACKED_IOR_SET = new Set(TRACKED_IOR_KEYS);
+const TRACKED_PLATFORMS = ['pc', 'ob'];
+const TRACKED_PLATFORM_SET = new Set(TRACKED_PLATFORMS);
+const DEFAULT_PLATFORM = 'pc';
 
 
 const ODDS_HISTORY_START_OFFSET = 2 * 60 * 60 * 1000;
 const ODDS_HISTORY_START_OFFSET = 2 * 60 * 60 * 1000;
 const ODDS_HISTORY_END_OFFSET = 3 * 60 * 60 * 1000;
 const ODDS_HISTORY_END_OFFSET = 3 * 60 * 60 * 1000;
@@ -41,6 +44,71 @@ const OddsHistory = mongoose.model('OddsHistory', oddsHistorySchema);
 
 
 const isTrackedKey = (key) => TRACKED_IOR_SET.has(key);
 const isTrackedKey = (key) => TRACKED_IOR_SET.has(key);
 
 
+const normalizePlatform = (platform) => {
+  return TRACKED_PLATFORM_SET.has(platform) ? platform : DEFAULT_PLATFORM;
+}
+
+const getMarketKey = (platform, ior) => {
+  return `${normalizePlatform(platform)}:${ior}`;
+}
+
+const parseMarketKey = (key) => {
+  if (isTrackedKey(key)) {
+    return { ior: key, platform: DEFAULT_PLATFORM };
+  }
+  const [platform, ior] = String(key).split(/[:.]/);
+  if (TRACKED_PLATFORM_SET.has(platform) && isTrackedKey(ior)) {
+    return { ior, platform };
+  }
+  return null;
+}
+
+const getMarketEntries = (markets = {}) => {
+  if (markets instanceof Map) {
+    return Array.from(markets.entries());
+  }
+  return Object.entries(markets);
+}
+
+const normalizeMarkets = (markets = {}) => {
+  const normalized = {};
+  getMarketEntries(markets).forEach(([key, points]) => {
+    const parsed = parseMarketKey(key);
+    if (!parsed || !points?.length) {
+      return;
+    }
+    const marketKey = getMarketKey(parsed.platform, parsed.ior);
+    normalized[marketKey] = [
+      ...(normalized[marketKey] ?? []),
+      ...points,
+    ].sort((a, b) => a.time - b.time);
+  });
+  return normalized;
+}
+
+const migrateLegacyMarkets = (markets) => {
+  getMarketEntries(markets).forEach(([key, points]) => {
+    const parsed = parseMarketKey(key);
+    if (!parsed || !points?.length) {
+      return;
+    }
+    const marketKey = getMarketKey(parsed.platform, parsed.ior);
+    if (key === marketKey) {
+      return;
+    }
+    if (!markets.has(marketKey)) {
+      markets.set(marketKey, points);
+    }
+    else {
+      markets.set(marketKey, [
+        ...markets.get(marketKey),
+        ...points,
+      ].sort((a, b) => a.time - b.time));
+    }
+    markets.delete(key);
+  });
+}
+
 const isInsideTrackWindow = (eventTime, recordTime = Date.now()) => {
 const isInsideTrackWindow = (eventTime, recordTime = Date.now()) => {
   if (!eventTime) {
   if (!eventTime) {
     return false;
     return false;
@@ -72,10 +140,11 @@ const appendChangedPoint = (markets, key, point, time) => {
   return true;
   return true;
 }
 }
 
 
-const recordGameOdds = async ({ game, events }) => {
+const recordGameOdds = async ({ game, events, platform = DEFAULT_PLATFORM }) => {
   const eventId = +game?.eventId;
   const eventId = +game?.eventId;
   const startTime = +game?.timestamp;
   const startTime = +game?.timestamp;
   const time = Date.now();
   const time = Date.now();
+  platform = normalizePlatform(platform);
 
 
   if (!eventId || !isInsideTrackWindow(startTime, time) || !events) {
   if (!eventId || !isInsideTrackWindow(startTime, time) || !events) {
     return { updated: false };
     return { updated: false };
@@ -88,20 +157,22 @@ const recordGameOdds = async ({ game, events }) => {
 
 
   const doc = await OddsHistory.findOne({ eventId });
   const doc = await OddsHistory.findOne({ eventId });
   const markets = doc?.markets ?? new Map();
   const markets = doc?.markets ?? new Map();
+  migrateLegacyMarkets(markets);
   let changed = false;
   let changed = false;
-  const currentKeys = new Set(trackedEntries.map(([key]) => key));
+  const currentKeys = new Set(trackedEntries.map(([key]) => getMarketKey(platform, key)));
 
 
   trackedEntries.forEach(([key, point]) => {
   trackedEntries.forEach(([key, point]) => {
-    if (appendChangedPoint(markets, key, point, time)) {
+    if (appendChangedPoint(markets, getMarketKey(platform, key), point, time)) {
       changed = true;
       changed = true;
     }
     }
   });
   });
 
 
   TRACKED_IOR_KEYS.forEach(key => {
   TRACKED_IOR_KEYS.forEach(key => {
-    if (currentKeys.has(key) || !markets.has(key)) {
+    const marketKey = getMarketKey(platform, key);
+    if (currentKeys.has(marketKey) || !markets.has(marketKey)) {
       return;
       return;
     }
     }
-    if (appendChangedPoint(markets, key, { value: 0 }, time)) {
+    if (appendChangedPoint(markets, marketKey, { value: 0 }, time)) {
       changed = true;
       changed = true;
     }
     }
   });
   });
@@ -137,10 +208,7 @@ const getGameOddsHistory = async (eventId) => {
   if (!history) {
   if (!history) {
     return null;
     return null;
   }
   }
-  const markets = Object.fromEntries(
-    Object.entries(history.markets ?? {})
-    .filter(([key, points]) => isTrackedKey(key) && points?.length),
-  );
+  const markets = normalizeMarkets(history.markets);
   return {
   return {
     ...history,
     ...history,
     markets,
     markets,
@@ -189,8 +257,7 @@ const getOddsHistoryGames = async ({ page = 1, pageSize = 50, status = -1, keywo
     teamAwayName: history.teamAwayName,
     teamAwayName: history.teamAwayName,
     startTime: history.startTime,
     startTime: history.startTime,
     endTime: history.endTime,
     endTime: history.endTime,
-    marketCount: Object.entries(history.markets ?? {})
-    .filter(([key, points]) => isTrackedKey(key) && points?.length).length,
+    marketCount: Object.keys(normalizeMarkets(history.markets)).length,
     updatedAt: history.updatedAt,
     updatedAt: history.updatedAt,
   }));
   }));
 
 

+ 54 - 9
web/apps/web-antd/src/views/match/odds-curve/index.vue

@@ -57,6 +57,10 @@ type HistoryGamesResponse = {
 };
 };
 
 
 const MARKET_KEYS = ['ior_ot_1', 'ior_ot_2', 'ior_ot_3', 'ior_ot_4'];
 const MARKET_KEYS = ['ior_ot_1', 'ior_ot_2', 'ior_ot_3', 'ior_ot_4'];
+const PLATFORMS = [
+  { label: 'PC', value: 'pc' },
+  { label: 'OB', value: 'ob' },
+];
 
 
 const MARKET_LABELS: Record<string, string> = {
 const MARKET_LABELS: Record<string, string> = {
   ior_ot_1: '总进球 1',
   ior_ot_1: '总进球 1',
@@ -65,6 +69,31 @@ const MARKET_LABELS: Record<string, string> = {
   ior_ot_4: '总进球 4',
   ior_ot_4: '总进球 4',
 };
 };
 
 
+const getMarketKey = (platform: string, key: string) => `${platform}:${key}`;
+
+const parseMarketKey = (key: string) => {
+  if (MARKET_KEYS.includes(key)) {
+    return { ior: key, platform: 'pc' };
+  }
+  const [platform, ior] = key.split(/[:.]/);
+  if (PLATFORMS.some((item) => item.value === platform) && MARKET_KEYS.includes(ior)) {
+    return { ior, platform };
+  }
+  return null;
+};
+
+const normalizeHistoryMarkets = (markets?: Record<string, MarketPoint[]>) => {
+  const normalized: Record<string, MarketPoint[]> = {};
+  Object.entries(markets ?? {}).forEach(([key, points]) => {
+    const parsed = parseMarketKey(key);
+    if (!parsed || !points?.length) {
+      return;
+    }
+    normalized[getMarketKey(parsed.platform, parsed.ior)] = points;
+  });
+  return normalized;
+};
+
 const chartRef = ref<EchartsUIType>();
 const chartRef = ref<EchartsUIType>();
 const { renderEcharts } = useEcharts(chartRef);
 const { renderEcharts } = useEcharts(chartRef);
 
 
@@ -83,8 +112,10 @@ const searchTimer = ref<ReturnType<typeof setTimeout>>();
 const totalGames = ref(0);
 const totalGames = ref(0);
 
 
 const availableMarketKeys = computed(() => {
 const availableMarketKeys = computed(() => {
-  const markets = history.value?.markets ?? {};
-  return MARKET_KEYS.filter((key) => markets[key]?.length);
+  const markets = normalizeHistoryMarkets(history.value?.markets);
+  return PLATFORMS.flatMap(({ value: platform }) => (
+    MARKET_KEYS.map((key) => getMarketKey(platform, key))
+  )).filter((key) => markets[key]?.length);
 });
 });
 
 
 const hasChartData = computed(() => !!history.value && availableMarketKeys.value.length > 0);
 const hasChartData = computed(() => !!history.value && availableMarketKeys.value.length > 0);
@@ -107,10 +138,15 @@ const isHalfEvent = (eventId?: number) => {
 
 
 const marketOptions = computed(() => {
 const marketOptions = computed(() => {
   const available = new Set(availableMarketKeys.value);
   const available = new Set(availableMarketKeys.value);
-  return MARKET_KEYS.filter((key) => available.has(key)).map((key) => ({
-    label: MARKET_LABELS[key] ?? key,
-    value: key,
-  }));
+  return PLATFORMS.flatMap(({ label: platformLabel, value: platform }) => (
+    MARKET_KEYS.map((key) => {
+      const value = getMarketKey(platform, key);
+      return {
+        label: `${platformLabel} ${MARKET_LABELS[key] ?? key}`,
+        value,
+      };
+    })
+  )).filter((option) => available.has(option.value));
 });
 });
 
 
 const selectedMarket = computed({
 const selectedMarket = computed({
@@ -146,6 +182,12 @@ const getMarketColor = (index: number) => {
   return colors[index % colors.length] ?? colors[0];
   return colors[index % colors.length] ?? colors[0];
 };
 };
 
 
+const getMarketLabel = (key: string) => {
+  const [platform, ior] = key.includes(':') || key.includes('.') ? key.split(/[:.]/) : ['pc', key];
+  const platformLabel = PLATFORMS.find((item) => item.value === platform)?.label ?? platform.toUpperCase();
+  return `${platformLabel} ${MARKET_LABELS[ior] ?? ior}`;
+};
+
 const formatSeriesData = (points: MarketPoint[] = []) => {
 const formatSeriesData = (points: MarketPoint[] = []) => {
   const data: Array<[number, null | number]> = [];
   const data: Array<[number, null | number]> = [];
   let lastValue: number | null = null;
   let lastValue: number | null = null;
@@ -168,7 +210,7 @@ const formatSeriesData = (points: MarketPoint[] = []) => {
 };
 };
 
 
 const renderChart = () => {
 const renderChart = () => {
-  const markets = history.value?.markets ?? {};
+  const markets = normalizeHistoryMarkets(history.value?.markets);
   const keys = selectedMarkets.value.filter((key) => markets[key]?.length);
   const keys = selectedMarkets.value.filter((key) => markets[key]?.length);
 
 
   if (!keys.length) {
   if (!keys.length) {
@@ -212,7 +254,7 @@ const renderChart = () => {
       lineStyle: {
       lineStyle: {
         width: 1,
         width: 1,
       },
       },
-      name: MARKET_LABELS[key] ?? key,
+      name: getMarketLabel(key),
       showSymbol: true,
       showSymbol: true,
       smooth: false,
       smooth: false,
       symbolSize: 2,
       symbolSize: 2,
@@ -281,7 +323,10 @@ const fetchHistory = async (eventId: number) => {
       params: { event_id: eventId },
       params: { event_id: eventId },
     });
     });
     history.value = data;
     history.value = data;
-    const firstKey = MARKET_KEYS.find((key) => data?.markets?.[key]?.length);
+    const markets = normalizeHistoryMarkets(data?.markets);
+    const firstKey = PLATFORMS.flatMap(({ value: platform }) => (
+      MARKET_KEYS.map((key) => getMarketKey(platform, key))
+    )).find((key) => markets[key]?.length);
     selectedMarkets.value = firstKey ? [firstKey] : [];
     selectedMarkets.value = firstKey ? [firstKey] : [];
     await nextTick();
     await nextTick();
     setTimeout(renderChart);
     setTimeout(renderChart);