From 3cab75b6acebd663a3eae9ccc05a0330dfa74264 Mon Sep 17 00:00:00 2001 From: zhuzihao Date: Sun, 21 Jun 2026 23:41:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin-withdraw):=20=E6=8F=90=E7=8E=B0?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=87=91=E5=B8=81=E8=AE=B0=E5=BD=95=E5=88=86?= =?UTF-8?q?=E9=A1=B5=20+=20=E9=A3=8E=E9=99=A9=E6=A0=87=E7=AD=BE=E6=8B=86?= =?UTF-8?q?=E5=88=86=20(#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - user_coin_records 增返 total(三源 granted 计数和),支持前端页码分页 - 风险标签「历史异常提现」拆成「提现拒绝N笔」「提现失败N笔」(拒绝=人工驳回退款,失败=打款失败退款) Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: zzhyyyyy <2685922758@qq.com> Reviewed-on: https://gitea.shaguabijia.com/WonderableAI/shaguabijia-app-server/pulls/64 Co-authored-by: zhuzihao Co-committed-by: zhuzihao --- app/admin/repositories/queries.py | 40 +++++++++++++++++++++++++------ app/admin/routers/users.py | 4 ++-- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/app/admin/repositories/queries.py b/app/admin/repositories/queries.py index 4e7f2d2..9c05600 100644 --- a/app/admin/repositories/queries.py +++ b/app/admin/repositories/queries.py @@ -493,15 +493,19 @@ def withdraw_risk_flags( created_at = user.created_at.replace(tzinfo=timezone.utc) if user.created_at.tzinfo is None else user.created_at if datetime.now(timezone.utc) - created_at < timedelta(hours=24): flags.append("新注册用户") - failed_or_rejected = sum(1 for item in recent_withdraws if item.status in {"failed", "rejected"}) - if failed_or_rejected: - flags.append(f"历史异常提现{failed_or_rejected}笔") + # 历史异常提现拆「拒绝」「失败」两类(口径不同:拒绝=人工驳回退款,失败=打款失败退款) + rejected_n = sum(1 for item in recent_withdraws if item.status == "rejected") + failed_n = sum(1 for item in recent_withdraws if item.status == "failed") + if rejected_n: + flags.append(f"提现拒绝{rejected_n}笔") + if failed_n: + flags.append(f"提现失败{failed_n}笔") recent_reviewing = sum(1 for item in recent_withdraws if item.status == "reviewing") if recent_reviewing >= 3: flags.append(f"待审核提现偏多:{recent_reviewing}笔") if cash_balance_cents < 0: flags.append("现金余额为负") - score = min(100, len(flags) * 20 + failed_or_rejected * 10) + score = min(100, len(flags) * 20 + (rejected_n + failed_n) * 10) return flags, score @@ -688,8 +692,8 @@ def user_coin_records( date_to: datetime | None = None, limit: int = 20, cursor: int | None = None, -) -> tuple[list[dict], int | None]: - """金币发放记录(提现详情底部表),按时间倒序、offset 游标分页。 +) -> tuple[list[dict], int | None, int]: + """金币发放记录(提现详情底部表),按时间倒序、offset 游标分页 + 总数。 合并三类来源(Phase1 信息流不分比价/领券): - 激励视频 / 签到膨胀 = ad_reward_record(granted) @@ -758,7 +762,29 @@ def user_coin_records( rows.sort(key=lambda r: r["created_at"], reverse=True) has_more = len(rows) > offset + limit - return rows[offset:offset + limit], (offset + limit if has_more else None) + + # 总数 = 三源在窗口内 granted 计数之和(供前端页码分页渲染页码/共 N 条) + def _count(model, *conds) -> int: + return int(db.execute(select(func.count()).select_from(model).where(*conds)).scalar_one()) + + total = ( + _count( + AdRewardRecord, AdRewardRecord.user_id == user_id, + AdRewardRecord.status == "granted", + *_window_conds(AdRewardRecord.created_at, date_from, date_to), + ) + + _count( + AdFeedRewardRecord, AdFeedRewardRecord.user_id == user_id, + AdFeedRewardRecord.status == "granted", + *_window_conds(AdFeedRewardRecord.created_at, date_from, date_to), + ) + + _count( + CoinTransaction, CoinTransaction.user_id == user_id, + CoinTransaction.biz_type == "signin", + *_window_conds(CoinTransaction.created_at, date_from, date_to), + ) + ) + return rows[offset:offset + limit], (offset + limit if has_more else None), total def list_price_reports( diff --git a/app/admin/routers/users.py b/app/admin/routers/users.py index 2e0a017..4b98bea 100644 --- a/app/admin/routers/users.py +++ b/app/admin/routers/users.py @@ -100,11 +100,11 @@ def get_user_coin_records( limit: Annotated[int, Query(ge=1, le=100)] = 20, cursor: Annotated[int | None, Query()] = None, ) -> CursorPage[UserCoinRecord]: - items, next_cursor = queries.user_coin_records( + items, next_cursor, total = queries.user_coin_records( db, user_id, date_from=date_from, date_to=date_to, limit=limit, cursor=cursor, ) return CursorPage( - items=[UserCoinRecord(**r) for r in items], next_cursor=next_cursor, + items=[UserCoinRecord(**r) for r in items], next_cursor=next_cursor, total=total, )