比价结果状态

This commit is contained in:
陈世睿
2026-06-17 14:56:42 +08:00
62 changed files with 4568 additions and 128 deletions
+6 -3
View File
@@ -95,11 +95,14 @@ def invalidate_store_mapping(
x_internal_secret: Annotated[str | None, Header()] = None,
) -> StoreMappingInvalidateOut:
_check_secret(x_internal_secret)
if payload.platform != "taobao":
# 当前只接淘宝; 其它平台先 no-op(affected=0), 不报错 — 向后兼容 pricebot 将来扩展。
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)
affected = repo.mark_taobao_deeplink_invalid(db, payload.shop_id)
logger.info(
"store_mapping invalidate platform=%s shop_id=%s → 标记失效 %d",
payload.platform, payload.shop_id, affected,
+35 -1
View File
@@ -32,6 +32,8 @@ from app.schemas.ad import (
FeedRewardIn,
FeedRewardOut,
PangleCallbackOut,
RewardNoShowIn,
RewardNoShowOut,
TestGrantIn,
TestGrantOut,
WatchReportIn,
@@ -242,10 +244,12 @@ def ecpm_report(payload: EcpmReportIn, user: CurrentUser, db: DbSession) -> Ecpm
ad_type=payload.ad_type, ecpm_raw=payload.ecpm,
ad_session_id=payload.ad_session_id,
adn=payload.adn, slot_id=payload.slot_id,
app_env=payload.app_env, our_code_id=payload.our_code_id,
)
logger.info(
"ad ecpm report user_id=%d type=%s session=%s ecpm=%s adn=%s slot=%s",
"ad ecpm report user_id=%d type=%s session=%s ecpm=%s adn=%s slot=%s app=%s code=%s",
user.id, payload.ad_type, payload.ad_session_id, payload.ecpm, payload.adn, payload.slot_id,
payload.app_env, payload.our_code_id,
)
return EcpmReportOut(ok=True)
@@ -359,6 +363,9 @@ def feed_reward(payload: FeedRewardIn, user: CurrentUser, db: DbSession) -> Feed
ad_session_id=payload.ad_session_id,
adn=payload.adn,
slot_id=payload.slot_id,
app_env=payload.app_env,
our_code_id=payload.our_code_id,
aborted=payload.aborted,
)
logger.info(
"feed ad reward user_id=%d event=%s status=%s units=%d coin=%d",
@@ -371,3 +378,30 @@ def feed_reward(payload: FeedRewardIn, user: CurrentUser, db: DbSession) -> Feed
unit_count=rec.unit_count,
daily_limit=rewards.get_ad_daily_limit(db),
)
@router.post(
"/reward-noshow",
response_model=RewardNoShowOut,
summary="激励视频提前关闭/未发奖留痕",
dependencies=[Depends(rate_limit(120, 60, "ad-reward-noshow"))],
)
def reward_noshow(payload: RewardNoShowIn, user: CurrentUser, db: DbSession) -> RewardNoShowOut:
"""激励视频展示了但用户提前关/跳过、未触发 S2S 发奖时,客户端 best-effort 上报一条留痕,
让广告收益报表能呈现「有展示、没发金币」的原因。不发金币;同一 session 已发奖则跳过。
"""
rec = crud_ad.record_reward_noshow(
db,
user.id,
ad_session_id=payload.ad_session_id,
ecpm=payload.ecpm,
adn=payload.adn,
slot_id=payload.slot_id,
app_env=payload.app_env,
our_code_id=payload.our_code_id,
)
logger.info(
"ad reward noshow user_id=%d session=%s watched=%ds -> status=%s",
user.id, payload.ad_session_id, payload.watched_seconds, rec.status,
)
return RewardNoShowOut(ok=True, status=rec.status)
+14 -1
View File
@@ -18,7 +18,7 @@ import httpx
from fastapi import APIRouter, HTTPException, Request, status
from fastapi.concurrency import run_in_threadpool
from app.api.deps import DbSession
from app.api.deps import CurrentUser, DbSession
from app.core.config import settings
from app.core.pricebot_router import pick_pricebot
from app.db.session import SessionLocal
@@ -28,6 +28,7 @@ from app.schemas.coupon_state import (
CouponPromptDismissIn,
CouponPromptShouldShowOut,
CouponPromptShownIn,
CouponStatsOut,
)
logger = logging.getLogger("shagua.coupon")
@@ -268,3 +269,15 @@ def coupon_completed_today_reset(
与 /prompt/reset 配套:开发设置「重置今日领券弹窗状态」一键把今日状态全清。MVP 不鉴权。"""
coupon_repo.reset_today_completion(db, payload.device_id)
return {"ok": True}
@router.get(
"/stats",
response_model=CouponStatsOut,
summary="累计领券数(「我的」页战绩卡「领取优惠券 X 张」)",
)
def coupon_stats(user: CurrentUser, db: DbSession) -> CouponStatsOut:
"""该登录用户累计领到的券数(SUM(claimed_count),口径见 coupon_repo.sum_claimed_count)。
**鉴权(CurrentUser)**——区别于同文件不鉴权的 /step 透传与 /prompt 频控(那些按 device_id):
个人战绩按 user_id 聚合,必须有登录态。"""
return CouponStatsOut(coupon_count=coupon_repo.sum_claimed_count(db, user.id))
+39
View File
@@ -0,0 +1,39 @@
"""CPS 群发短链跳转:用户点 /c/{code} → 记一条点击 → 302 跳美团。
公网、无鉴权(群里任何人点都要能跳)。记点击失败绝不影响跳转(用户体验优先)。
"""
from __future__ import annotations
from fastapi import APIRouter, Depends, Request
from fastapi.responses import RedirectResponse
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.repositories import cps_link as cps_link_repo
router = APIRouter(tags=["cps-redirect"])
# code 不存在/失效时的兜底落地(避免用户看到报错)
_FALLBACK_URL = "https://www.meituan.com/"
def _client_ip(request: Request) -> str | None:
xff = request.headers.get("x-forwarded-for")
if xff:
return xff.split(",")[0].strip()
return request.client.host if request.client else None
@router.get("/c/{code}", summary="群发短链跳转(记点击 + 302)")
def cps_redirect(code: str, request: Request, db: Session = Depends(get_db)):
link = cps_link_repo.get_by_code(db, code)
if link is None:
return RedirectResponse(_FALLBACK_URL, status_code=302)
try:
cps_link_repo.record_click(
db, link=link, ip=_client_ip(request),
ua=request.headers.get("user-agent"),
)
except Exception:
pass # 记点击失败不阻断跳转
return RedirectResponse(link.target_url, status_code=302)
+1 -1
View File
@@ -21,7 +21,7 @@ logger = logging.getLogger("shagua.feedback")
router = APIRouter(prefix="/api/v1/feedback", tags=["feedback"])
_MAX_IMAGES = 6
_CONTENT_MAX = 2000
_CONTENT_MAX = 200
_CONTACT_MAX = 128