f15ca74a22
Reviewed-on: #56
111 lines
4.0 KiB
Python
111 lines
4.0 KiB
Python
"""平台店铺映射内部上报端点(pricebot → app-server)。
|
|
|
|
pricebot 在淘宝比价拿到 shopId 后,把这一行跨平台店铺映射 POST 到这里落库。
|
|
**不是给客户端的接口**:不走用户 JWT,靠 server 间共享密钥头 `X-Internal-Secret` 校验
|
|
(复用 price.py 的 _check_secret,与 price-observation 同一密钥)。
|
|
|
|
与 price.py 的 /internal/price-observation 平行:那个落价格事实,这个落店铺身份映射。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Header
|
|
|
|
from app.api.deps import DbSession
|
|
from app.api.internal.price import _check_secret
|
|
from app.repositories import store_mapping as repo
|
|
from app.schemas.store_mapping import (
|
|
StoreMappingIn,
|
|
StoreMappingInvalidateIn,
|
|
StoreMappingInvalidateOut,
|
|
StoreMappingOut,
|
|
)
|
|
|
|
logger = logging.getLogger("shagua.internal.store")
|
|
|
|
router = APIRouter(prefix="/internal", tags=["internal"])
|
|
|
|
|
|
@router.get(
|
|
"/store-mapping/lookup",
|
|
summary="比价前按源平台店名反查各目标平台已沉淀的店铺 id(命中→pricebot 直接 deeplink)",
|
|
)
|
|
def lookup_store_mapping(
|
|
source_platform: str,
|
|
name: str,
|
|
db: DbSession,
|
|
lat: float | None = None,
|
|
lng: float | None = None,
|
|
x_internal_secret: Annotated[str | None, Header()] = None,
|
|
) -> dict:
|
|
_check_secret(x_internal_secret)
|
|
result = repo.lookup_nearest(db, source_platform, name, lat, lng)
|
|
if result:
|
|
hits = ", ".join(
|
|
f"{t}:row{v['row_id']}"
|
|
f"{'(' + str(v['dist_km']) + 'km)' if 'dist_km' in v else ''}"
|
|
f"→{v.get('deeplink') or '(无deeplink)'}"
|
|
for t, v in result.items()
|
|
)
|
|
logger.info(
|
|
"store_mapping lookup source=%s name=%r geo=(%s,%s) → 命中 %s",
|
|
source_platform, name, lat, lng, hits,
|
|
)
|
|
else:
|
|
logger.info(
|
|
"store_mapping lookup source=%s name=%r geo=(%s,%s) → MISS",
|
|
source_platform, name, lat, lng,
|
|
)
|
|
return result
|
|
|
|
|
|
@router.post(
|
|
"/store-mapping",
|
|
response_model=StoreMappingOut,
|
|
summary="平台店铺映射内部上报(pricebot→app-server,落 store_mapping)",
|
|
)
|
|
def report_store_mapping(
|
|
payload: StoreMappingIn,
|
|
db: DbSession,
|
|
x_internal_secret: Annotated[str | None, Header()] = None,
|
|
) -> StoreMappingOut:
|
|
_check_secret(x_internal_secret)
|
|
created, row_id = repo.upsert(db, payload)
|
|
logger.info(
|
|
"store_mapping trace=%s %s row_id=%s source=%s "
|
|
"taobao=(%s,%s) jd=(%s,%s) device=%s user=%s",
|
|
payload.trace_id, "新建" if created else "合并", row_id, payload.source_platform,
|
|
payload.id_taobao, payload.name_taobao, payload.id_jd, payload.name_jd,
|
|
payload.source_device_id, payload.source_user_id,
|
|
)
|
|
return StoreMappingOut(inserted=created, row_id=row_id)
|
|
|
|
|
|
@router.post(
|
|
"/store-mapping/invalidate",
|
|
response_model=StoreMappingInvalidateOut,
|
|
summary="标记某平台 shopId 的缓存 deeplink 失效(pricebot 撞错误页回退时上报,lookup 不再返回)",
|
|
)
|
|
def invalidate_store_mapping(
|
|
payload: StoreMappingInvalidateIn,
|
|
db: DbSession,
|
|
x_internal_secret: Annotated[str | None, Header()] = None,
|
|
) -> StoreMappingInvalidateOut:
|
|
_check_secret(x_internal_secret)
|
|
if payload.platform == "taobao":
|
|
affected = repo.mark_taobao_deeplink_invalid(db, payload.shop_id)
|
|
elif payload.platform == "jd":
|
|
affected = repo.mark_jd_deeplink_invalid(db, payload.shop_id)
|
|
else:
|
|
# 当前只接淘宝/京东; 其它平台先 no-op(affected=0), 不报错 — 向后兼容 pricebot 将来扩展。
|
|
logger.info("store_mapping invalidate 跳过: platform=%s 暂不支持", payload.platform)
|
|
return StoreMappingInvalidateOut(ok=True, affected=0)
|
|
logger.info(
|
|
"store_mapping invalidate platform=%s shop_id=%s → 标记失效 %d 行",
|
|
payload.platform, payload.shop_id, affected,
|
|
)
|
|
return StoreMappingInvalidateOut(ok=True, affected=affected)
|