Bladeren bron

feat: paginate odds history games

flyzto 16 uur geleden
bovenliggende
commit
20fa09b772

+ 2 - 2
server/models/GamesPs.js

@@ -1371,8 +1371,8 @@ const getOddsHistory = async (eventId) => {
 /**
  * 获取赔率历史比赛列表
  */
-const getOddsHistoryGames = async () => {
-  return OddsHistory.getOddsHistoryGames();
+const getOddsHistoryGames = async (options) => {
+  return OddsHistory.getOddsHistoryGames(options);
 }
 
 /**

+ 35 - 4
server/models/OddsHistory.js

@@ -151,13 +151,42 @@ const getGameOddsHistory = async (eventId) => {
   };
 }
 
-const getOddsHistoryGames = async () => {
-  const histories = await OddsHistory.find({ eventId: { $gt: 0 } })
+const getOddsHistoryGames = async ({ page = 1, pageSize = 50, status = -1, keyword } = {}) => {
+  page = Math.max(1, +page || 1);
+  pageSize = Math.min(50, Math.max(1, +pageSize || 50));
+  status = +status;
+
+  const now = Date.now();
+  const query = { eventId: { $gt: 0 } };
+  if (status === 1) {
+    query.endTime = { $gte: now };
+  }
+  else if (status === 2) {
+    query.endTime = { $lt: now };
+  }
+  if (keyword) {
+    const pattern = new RegExp(String(keyword).trim(), 'i');
+    query.$or = [
+      { leagueName: pattern },
+      { teamHomeName: pattern },
+      { teamAwayName: pattern },
+    ];
+    if (/^\d+$/.test(String(keyword).trim())) {
+      query.$or.push({ eventId: +keyword });
+    }
+  }
+
+  const [histories, total] = await Promise.all([
+    OddsHistory.find(query)
     .select('eventId leagueName teamHomeName teamAwayName startTime endTime markets updatedAt')
     .sort({ startTime: -1 })
-    .lean({ flattenMaps: true });
+    .skip((page - 1) * pageSize)
+    .limit(pageSize)
+    .lean({ flattenMaps: true }),
+    OddsHistory.countDocuments(query),
+  ]);
 
-  return histories.map(history => ({
+  const list = histories.map(history => ({
     eventId: history.eventId,
     leagueName: history.leagueName,
     teamHomeName: history.teamHomeName,
@@ -167,6 +196,8 @@ const getOddsHistoryGames = async () => {
     marketCount: Object.values(history.markets ?? {}).filter(points => points?.length).length,
     updatedAt: history.updatedAt,
   }));
+
+  return { list, page, pageSize, total };
 }
 
 const cleanupExpiredHistory = async () => {

+ 2 - 1
server/routes/pstery.js

@@ -192,7 +192,8 @@ router.get('/get_odds_history', (req, res) => {
  * 获取赔率历史比赛列表
  */
 router.get('/get_odds_history_games', (req, res) => {
-  Games.getOddsHistoryGames()
+  const { page, page_size, status, keyword } = req.query;
+  Games.getOddsHistoryGames({ page, pageSize: page_size, status, keyword })
   .then(games => {
     res.sendSuccess(games);
   })

+ 55 - 25
web/apps/web-antd/src/views/match/odds-curve/index.vue

@@ -11,6 +11,7 @@ import {
   Empty,
   Input,
   message,
+  Pagination,
   Radio,
   RadioGroup,
   Spin,
@@ -47,6 +48,13 @@ type HistoryGame = {
   updatedAt?: string;
 };
 
+type HistoryGamesResponse = {
+  list: HistoryGame[];
+  page: number;
+  pageSize: number;
+  total: number;
+};
+
 const MARKET_GROUPS = [
   {
     keys: [
@@ -101,7 +109,11 @@ const selectedMarkets = ref<string[]>([]);
 const loadingGames = ref(false);
 const loadingHistory = ref(false);
 const marketType = ref(-1);
+const pageSize = 50;
+const currentPage = ref(1);
 const searchValue = ref('');
+const searchTimer = ref<ReturnType<typeof setTimeout>>();
+const totalGames = ref(0);
 
 const availableMarketKeys = computed(() => {
   const markets = history.value?.markets ?? {};
@@ -114,26 +126,6 @@ const selectedGame = computed(() => {
   return games.value.find((item) => item.eventId === selectedEventId.value);
 });
 
-const filteredGames = computed(() => {
-  const keyword = searchValue.value.trim();
-  const now = Date.now();
-  return games.value.filter((item) => {
-    if (marketType.value === 1 && item.endTime < now) {
-      return false;
-    }
-    if (marketType.value === 2 && item.endTime >= now) {
-      return false;
-    }
-    if (keyword) {
-      const text = `${item.eventId} ${item.leagueName} ${item.teamHomeName} ${item.teamAwayName}`;
-      if (!text.includes(keyword)) {
-        return false;
-      }
-    }
-    return true;
-  });
-});
-
 const historyTitle = computed(() => {
   const source = history.value ?? selectedGame.value;
   if (!source) {
@@ -296,8 +288,16 @@ const renderChart = () => {
 const fetchGames = async () => {
   loadingGames.value = true;
   try {
-    const data = await requestClient.get<HistoryGame[]>('/pstery/get_odds_history_games');
-    games.value = data ?? [];
+    const data = await requestClient.get<HistoryGamesResponse>('/pstery/get_odds_history_games', {
+      params: {
+        keyword: searchValue.value.trim() || undefined,
+        page: currentPage.value,
+        page_size: pageSize,
+        status: marketType.value,
+      },
+    });
+    games.value = data?.list ?? [];
+    totalGames.value = data?.total ?? 0;
     if (!selectedEventId.value && games.value.length) {
       selectedEventId.value = games.value[0]?.eventId;
     }
@@ -344,6 +344,20 @@ const selectGame = (id: number) => {
 };
 
 watch(marketType, () => {
+  selectedEventId.value = undefined;
+  history.value = null;
+  selectedMarkets.value = [];
+  currentPage.value = 1;
+  fetchGames();
+});
+
+watch(searchValue, () => {
+  currentPage.value = 1;
+  clearTimeout(searchTimer.value);
+  searchTimer.value = setTimeout(fetchGames, 300);
+});
+
+watch(currentPage, () => {
   selectedEventId.value = undefined;
   history.value = null;
   selectedMarkets.value = [];
@@ -377,9 +391,9 @@ onMounted(() => {
         placeholder="搜索联赛/球队/赛事ID"
       />
       <Spin :spinning="loadingGames">
-        <div class="match-list" v-if="filteredGames.length">
+        <div class="match-list" v-if="games.length">
           <button
-            v-for="game in filteredGames"
+            v-for="game in games"
             :key="game.eventId"
             class="match-item"
             :class="{ active: game.eventId === selectedEventId }"
@@ -396,6 +410,15 @@ onMounted(() => {
         </div>
         <Empty v-else class="list-empty" description="暂无比赛" />
       </Spin>
+      <Pagination
+        v-if="totalGames > pageSize"
+        v-model:current="currentPage"
+        class="match-pagination"
+        :page-size="pageSize"
+        :show-size-changer="false"
+        size="small"
+        :total="totalGames"
+      />
     </aside>
 
     <main class="curve-panel">
@@ -471,11 +494,18 @@ onMounted(() => {
 }
 
 .match-list {
-  height: calc(100vh - 230px);
+  height: calc(100vh - 274px);
   overflow: auto;
   padding: 0 10px 10px;
 }
 
+.match-pagination {
+  display: flex;
+  justify-content: center;
+  padding: 10px;
+  border-top: 1px solid hsl(var(--border));
+}
+
 .match-item {
   display: grid;
   width: 100%;