73 lines
2.6 KiB
Python
73 lines
2.6 KiB
Python
"""通知(お知らせ)路由:从 Redis 读取 worker 抓取的账号通知。
|
|
|
|
worker(pruchase_jhw)定时抓取骏河屋 action_news 页面,把每条通知按 data_id
|
|
作为 field 写入 Redis Hash(key 见 redis_keys.news_hash)。本路由读取该 Hash,
|
|
按 data_id 倒序(新→旧)返回,只读不删除。Redis 读取沿用 session_store 复用的连接,
|
|
与 scrape 路由保持一致。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
|
|
from app.api.dependencies import get_container, require_bearer_token
|
|
from app.core.container import ServiceContainer
|
|
from app.models.scrape import ApiResponse, NewsItemData
|
|
from surugaya_common import redis_keys
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api", tags=["news"])
|
|
|
|
|
|
def _news_sort_key(item: dict[str, Any]) -> tuple[int, int]:
|
|
"""排序键:优先按 data_id 数值倒序,非数值退化到最小,保证排序稳定。"""
|
|
raw = str(item.get("data_id", "")).strip()
|
|
if raw.isdigit():
|
|
return (1, int(raw))
|
|
return (0, 0)
|
|
|
|
|
|
@router.get(
|
|
"/news",
|
|
response_model=ApiResponse[list[NewsItemData]],
|
|
dependencies=[Depends(require_bearer_token)],
|
|
)
|
|
async def list_news(
|
|
limit: int | None = Query(default=None, ge=1, le=500, description="返回条数上限,省略则返回全部"),
|
|
container: ServiceContainer = Depends(get_container),
|
|
) -> ApiResponse[list[NewsItemData]]:
|
|
"""读取通知列表(按 data_id 倒序,新→旧),只读不删除。"""
|
|
if container.session_store._redis is None:
|
|
return ApiResponse[list[NewsItemData]](
|
|
success=False,
|
|
msg="Redis 未配置",
|
|
data=None,
|
|
code=500,
|
|
)
|
|
|
|
# dev/prod 由本服务 app_env 推导,需与 worker 写入端(APP_ENV)保持一致。
|
|
is_dev = "1" if container.settings.app_env == "dev" else "0"
|
|
raw_map = await container.session_store._redis.hgetall(redis_keys.news_hash(is_dev))
|
|
|
|
items: list[dict[str, Any]] = []
|
|
for data_id, raw_value in raw_map.items():
|
|
try:
|
|
items.append(json.loads(raw_value))
|
|
except (json.JSONDecodeError, TypeError):
|
|
logger.warning("通知 data_id=%s 的 JSON 解析失败,已跳过。", data_id)
|
|
|
|
items.sort(key=_news_sort_key, reverse=True)
|
|
if limit is not None and limit > 0:
|
|
items = items[:limit]
|
|
|
|
return ApiResponse[list[NewsItemData]](
|
|
success=True,
|
|
msg="success",
|
|
data=[NewsItemData(**item) for item in items],
|
|
code=0,
|
|
)
|