Files
live-hub-py/web/backend/services/douyu_goods_service.py
T

131 lines
4.4 KiB
Python

"""斗鱼活动商品的唯一解析与快照写入实现。"""
from __future__ import annotations
from collections.abc import Mapping
from datetime import UTC, datetime
from typing import Any
from sqlalchemy.orm import Session
from ..models import DouyuGoodsSnapshot
def _to_int(value: Any) -> int | None:
if value is None or value == "":
return None
try:
return int(float(value))
except (TypeError, ValueError):
return None
def _text(value: Any, *, max_length: int | None = None) -> str:
text = str(value or "").strip()
return text[:max_length] if max_length else text
def _json_safe(value: Any) -> Any:
if isinstance(value, dict):
return {str(key): _json_safe(item) for key, item in value.items()}
if isinstance(value, list):
return [_json_safe(item) for item in value]
if isinstance(value, (str, int, float, bool)) or value is None:
return value
return str(value)
def _first_value(data: Mapping[str, Any], *keys: str) -> Any:
for key in keys:
if key in data and data.get(key) is not None:
return data.get(key)
return None
def serialize_goods(raw: Mapping[str, Any]) -> dict[str, Any]:
"""把 storehome 原始商品映射成前端稳定字段。"""
commodity_id = _text(
_first_value(raw, "commodityId", "commodity_id"), max_length=64
)
name = _text(_first_value(raw, "commodityName", "name"), max_length=256)
open_status = _to_int(_first_value(raw, "openStatus", "open_status"))
mark = _text(_first_value(raw, "mark"), max_length=32)
status_value = _first_value(raw, "status")
return {
"commodity_id": commodity_id,
"name": name,
"score": _to_int(_first_value(raw, "score")),
"status": str(status_value) if status_value is not None else "",
"open_status": open_status,
"start_time": _to_int(_first_value(raw, "startTime", "start_time")),
"end_time": _to_int(_first_value(raw, "endTime", "end_time")),
"mark": mark,
"count": _to_int(_first_value(raw, "count")),
"elite_limited": _to_int(_first_value(raw, "eliteLimited")),
"is_limited": bool(mark) or open_status == 0,
"raw": _json_safe(dict(raw)),
}
def serialize_live_goods(goods: list[dict[str, Any]]) -> list[dict[str, Any]]:
return [serialize_goods(raw) for raw in goods if isinstance(raw, dict)]
def serialize_snapshot(row: DouyuGoodsSnapshot) -> dict[str, Any]:
raw = row.raw if isinstance(row.raw, dict) else {}
open_status = _to_int(_first_value(raw, "openStatus", "open_status"))
mark = _text(_first_value(raw, "mark"), max_length=32)
return {
"commodity_id": row.commodity_id,
"name": row.name,
"score": row.score,
"status": row.status,
"open_status": open_status,
"start_time": _to_int(_first_value(raw, "startTime", "start_time")),
"end_time": _to_int(_first_value(raw, "endTime", "end_time")),
"mark": mark,
"count": _to_int(_first_value(raw, "count")),
"elite_limited": _to_int(_first_value(raw, "eliteLimited")),
"is_limited": bool(mark) or open_status == 0,
"raw": raw,
}
def serialized_snapshots(db: Session) -> list[dict[str, Any]]:
rows = db.query(DouyuGoodsSnapshot).order_by(DouyuGoodsSnapshot.id.asc()).all()
return [serialize_snapshot(row) for row in rows]
def upsert_goods_snapshot(
db: Session,
goods: list[dict[str, Any]],
*,
commit: bool = True,
) -> None:
"""统一写入活动商品快照;零值字段必须保留。"""
now = datetime.now(UTC)
for raw in goods:
if not isinstance(raw, dict):
continue
commodity_id = _text(
_first_value(raw, "commodityId", "commodity_id"), max_length=64
)
if not commodity_id:
continue
row = (
db.query(DouyuGoodsSnapshot)
.filter(DouyuGoodsSnapshot.commodity_id == commodity_id)
.first()
)
if row is None:
row = DouyuGoodsSnapshot(commodity_id=commodity_id)
db.add(row)
row.name = _text(_first_value(raw, "commodityName", "name"), max_length=256)
row.score = _to_int(_first_value(raw, "score"))
status_value = _first_value(raw, "status")
row.status = str(status_value) if status_value is not None else ""
row.raw = _json_safe(raw)
row.updated_at = now
if commit:
db.commit()