07c0b7502c
提现人工审核(提现不再即时打款): - models/wallet.py: WithdrawOrder 状态机改为 reviewing →(审核通过)→ pending → success/failed;reviewing →(驳回)→ rejected(已退款), 默认态 pending → reviewing(扣现金建单但不打款);新增 user_name 列(实名, 微信达额转账要求) - admin/routers/withdraw.py + admin/schemas/wallet.py: 运营后台审核接口(通过触发微信转账 / 驳回退款) - api/v1/wallet.py + repositories/wallet.py: 发起提现进 reviewing 态, 驳回退回现金并写 withdraw_refund - schemas/welfare.py: 提现出参增 reviewing/rejected 状态 + fail_reason 看广告每日时长限流(防刷主闸 + 次数兜底): - 新增 models/ad_watch_log.py: 按 (user_id, watch_date) 聚合当日观看秒数 - 新增 repositories/ad_watch.py: watched_seconds_today / add_watch_seconds(夹 [0, MAX_SINGLE_WATCH_SECONDS]) - api/v1/ad.py: 新增 POST /ad/watch-report(JWT 取 user, 落时长返当日累计); /ad/reward-status 增 watched_seconds_today / limit / remaining - schemas/ad.py: WatchReportIn/Out - core/rewards.py: 新增 DAILY_AD_WATCH_SECONDS_LIMIT=50 分钟(主闸)+ MAX_SINGLE_WATCH_SECONDS=120; DAILY_AD_REWARD_LIMIT 20→200 改作次数兜底(防前端少报时长绕过时长闸, 又不误伤看短广告的正常用户) - repositories/ad_reward.py + models/__init__.py: 接入新表与时长闸 alembic: withdraw_review_and_ad_watch 迁移(withdraw_order.user_name 列 + ad_watch_log 表) tests: 补 test_ad_reward / test_withdraw Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: pure <pure@192.168.0.104> Reviewed-on: #15
37 lines
1.5 KiB
Python
37 lines
1.5 KiB
Python
"""看激励视频观看时长 CRUD(每日总时长防刷)。
|
|
|
|
前端 onAdClose 上报本次观看秒数 → add_watch_seconds 落库 + 返回当日累计;
|
|
watched_seconds_today 给"时长闸"(发奖 / 展示前)判断当天是否已达 50 分钟上限。
|
|
鉴权接口已确保 user 存在(JWT),故不做 UnknownUser 校验。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.rewards import MAX_SINGLE_WATCH_SECONDS, cn_today
|
|
from app.models.ad_watch_log import AdWatchLog
|
|
|
|
|
|
def watched_seconds_today(db: Session, user_id: int, *, today: str | None = None) -> int:
|
|
"""该用户今日(北京时间)累计观看秒数。coalesce 防无记录时 SUM 返 None。"""
|
|
d = today or cn_today().isoformat()
|
|
return db.execute(
|
|
select(func.coalesce(func.sum(AdWatchLog.watch_seconds), 0)).where(
|
|
AdWatchLog.user_id == user_id,
|
|
AdWatchLog.watch_date == d,
|
|
)
|
|
).scalar_one()
|
|
|
|
|
|
def add_watch_seconds(db: Session, user_id: int, seconds: int) -> int:
|
|
"""记一次观看时长,返回当日累计秒数。
|
|
|
|
seconds 夹 [0, MAX_SINGLE_WATCH_SECONDS] 防前端报异常大值(刷时长无意义,但夹紧更稳)。
|
|
"""
|
|
clamped = max(0, min(int(seconds), MAX_SINGLE_WATCH_SECONDS))
|
|
today = cn_today().isoformat()
|
|
db.add(AdWatchLog(user_id=user_id, watch_seconds=clamped, watch_date=today))
|
|
db.commit()
|
|
return watched_seconds_today(db, user_id, today=today)
|