diff --git a/backend/app/services/news_service.py b/backend/app/services/news_service.py index f815007..9bb1027 100755 --- a/backend/app/services/news_service.py +++ b/backend/app/services/news_service.py @@ -488,8 +488,12 @@ class NewsPlatformService: return valid logger.info(f"[广场新闻] {user.account} 今日文章校验后全部无效,转历史") - # ── Phase 2: 无今日新文章 → 历史随机翻页(热度+新鲜度加权)──────── - logger.info(f"[广场新闻] {user.account} 无今日新文章,随机历史翻页") + # ── Phase 2: 无今日新文章 → 从最新到旧顺序翻页(随机从某页开始)──── + # 规则:从第1页(最新)开始依次往后,轮到哪页由当前小时决定, + # 保证不同时段覆盖不同页,模拟"从新到旧"逐步互动 + logger.info(f"[广场新闻] {user.account} 无今日新文章,从最新向旧顺序翻页") + + # 获取总页数 total_pages = 1 try: async with httpx.AsyncClient(timeout=10) as _c: @@ -505,58 +509,48 @@ class NewsPlatformService: except Exception: pass - rand_page = _rand.randint(1, min(total_pages, 10)) - items = [] - try: - async with httpx.AsyncClient(timeout=15) as c: - r = await c.get( - f"{biz}/business/member/square/list", - headers=self._bearer(token), - params=_build(rand_page), - ) - if r.status_code == 200: - d = r.json() - if d.get("code") in [0, 200]: - nd = d.get("data", {}) - items = nd.get("data") or nd.get("list") or nd.get("records") or [] - items = _filter(items) - except Exception as e: - logger.error(f"[广场新闻-历史] {user.account}: {e}") + max_pages = min(total_pages, 10) # 最多翻前10页 + # 用小时数取模决定起始页,保证同一小时内不同用户分散在不同页 + # 从最新(page=1)开始往旧的方向走 + hour_slot = _dt.now().hour % max_pages + start_page = hour_slot + 1 # 1-based - logger.info(f"[广场新闻] {user.account} 历史第{rand_page}页获取到 {len(items)} 条") + items = [] + # 尝试从 start_page 开始,若该页为空则顺序往后再往前找 + pages_to_try = list(range(start_page, max_pages + 1)) + list(range(1, start_page)) + tried_page = start_page + for page in pages_to_try: + try: + async with httpx.AsyncClient(timeout=15) as c: + r = await c.get( + f"{biz}/business/member/square/list", + headers=self._bearer(token), + params=_build(page), + ) + if r.status_code == 200: + d = r.json() + if d.get("code") in [0, 200]: + nd = d.get("data", {}) + _items = nd.get("data") or nd.get("list") or nd.get("records") or [] + _items = _filter(_items) + if _items: + items = _items + tried_page = page + break + except Exception as e: + logger.error(f"[广场新闻-历史] {user.account} page={page}: {e}") + + logger.info(f"[广场新闻] {user.account} 历史第{tried_page}页获取到 {len(items)} 条") if not items: return [] - # 热度 + 新鲜度加权采样 - def _hot_weight(a): - hot = (int(a.get("commentNum") or 0) * 3 + - int(a.get("praiseNum") or 0) * 2 + - int(a.get("readNum") or 0)) - freshness = 1.0 - t = a.get("createTime") or a.get("publishTime") or "" - if t: - try: - pub = _dt.strptime(t[:19], "%Y-%m-%d %H:%M:%S") - h = (_dt.now() - pub).total_seconds() / 3600 - freshness = max(1.0, 3.0 - h / 36.0) - except Exception: - pass - return max(1.0, (hot + 1) * freshness) - - weights = [_hot_weight(a) for a in items] - pool_idx = list(range(len(items))) - selected = [] - for _ in range(min(count * 2, len(items))): - if not pool_idx: - break - ci = _rand.choices(pool_idx, weights=[weights[i] for i in pool_idx], k=1)[0] - selected.append(items[ci]) - pool_idx.remove(ci) + # 在该页内随机打散(同一页内不需要排序,保持自然顺序即可) + _rand.shuffle(items) + selected = items[:min(count * 2, len(items))] # 有效性校验 valid = [] - remaining = [i for i in pool_idx] for a in selected: if len(valid) >= count: break @@ -564,11 +558,10 @@ class NewsPlatformService: if await self.validate_article(db, user, aid): valid.append(a) - # 不够则从剩余池补充 - for ri in remaining: + # 不够则从剩余补充 + for a in items[len(selected):]: if len(valid) >= count: break - a = items[ri] aid = str(a.get("recordId") or a.get("id", "")) if await self.validate_article(db, user, aid): valid.append(a)