diff --git a/app/admin/repositories/queries.py b/app/admin/repositories/queries.py index f041210..488bcf7 100644 --- a/app/admin/repositories/queries.py +++ b/app/admin/repositories/queries.py @@ -788,6 +788,7 @@ def get_user_overview(db: Session, user_id: int) -> dict | None: "user": user, "coin_balance": acc.coin_balance if acc else 0, "cash_balance_cents": acc.cash_balance_cents if acc else 0, + "invite_cash_balance_cents": acc.invite_cash_balance_cents if acc else 0, "total_coin_earned": acc.total_coin_earned if acc else 0, "comparison_total": _count(ComparisonRecord, ComparisonRecord.user_id == user_id), "comparison_success": _count( diff --git a/app/admin/routers/users.py b/app/admin/routers/users.py index 4b98bea..7e4a81b 100644 --- a/app/admin/routers/users.py +++ b/app/admin/routers/users.py @@ -210,22 +210,28 @@ def grant_user_cash( db: AdminDb, ) -> OkResponse: """给指定用户增/减或设值现金(分)。delta:正=发放、负=扣减;set:直接设为目标值。 + account=coin_cash(金币兑现金)/ invite_cash(邀请奖励金):两本账物理隔离、各调各的。 主要用于让无现金用户直接测试提现。""" user = user_repo.get_user_by_id(db, user_id) if user is None: raise HTTPException(status_code=404, detail="用户不存在") - # set=设为目标值:读当前余额算差值,仍复用 grant_cash 写一笔流水(沿用原子/审计/扣负保护) + # 按目标账户选「余额字段 + 变动入口」(grant_invite_cash 与 grant_cash 同构) + is_invite = body.account == "invite_cash" + balance_attr = "invite_cash_balance_cents" if is_invite else "cash_balance_cents" + grant_fn = wallet_repo.grant_invite_cash if is_invite else wallet_repo.grant_cash + acct_label = "邀请奖励金" if is_invite else "现金" + before: int | None = None + # set=设为目标值:读当前余额算差值,仍复用 grant 写一笔流水(沿用原子/审计/扣负保护) if body.mode == "set": if body.amount_cents < 0: - raise HTTPException(status_code=400, detail="目标现金值不能为负") + raise HTTPException(status_code=400, detail=f"目标{acct_label}值不能为负") # lock=True:锁账户行,防连点/并发各读同一 before 算同一 delta 双写,余额错位 - before = wallet_repo.get_or_create_account( - db, user_id, commit=False, lock=True - ).cash_balance_cents + acc_locked = wallet_repo.get_or_create_account(db, user_id, commit=False, lock=True) + before = getattr(acc_locked, balance_attr) delta = body.amount_cents - before if delta == 0: raise HTTPException( - status_code=400, detail=f"当前现金已为 {body.amount_cents} 分,无需调整" + status_code=400, detail=f"当前{acct_label}已为 {body.amount_cents} 分,无需调整" ) else: if body.amount_cents == 0: @@ -234,18 +240,20 @@ def grant_user_cash( # 负数扣减时不允许扣成负余额(运营误操作保护);lock=True 防并发扣穿 if delta < 0: acc_now = wallet_repo.get_or_create_account(db, user_id, commit=False, lock=True) - if acc_now.cash_balance_cents + delta < 0: + if getattr(acc_now, balance_attr) + delta < 0: raise HTTPException( - status_code=400, detail=f"扣减后现金为负(当前余额 {acc_now.cash_balance_cents} 分)" + status_code=400, + detail=f"扣减后{acct_label}为负(当前余额 {getattr(acc_now, balance_attr)} 分)", ) biz_type = "admin_grant" if delta > 0 else "admin_deduct" - # grant_cash 只 flush 不 commit;审计同 commit=False;最后一起 commit → 原子(改钱+留痕) - acc, _ = wallet_repo.grant_cash( + # grant 只 flush 不 commit;审计同 commit=False;最后一起 commit → 原子(改钱+留痕) + acc, _ = grant_fn( db, user_id, delta, biz_type=biz_type, remark=f"admin:{body.reason}"[:128], ) detail = { + "account": body.account, "amount_cents": delta, - "balance_after_cents": acc.cash_balance_cents, + "balance_after_cents": getattr(acc, balance_attr), "reason": body.reason, } if body.mode == "set": diff --git a/app/admin/schemas/user.py b/app/admin/schemas/user.py index fad0a62..8e7020e 100644 --- a/app/admin/schemas/user.py +++ b/app/admin/schemas/user.py @@ -29,6 +29,7 @@ class AdminUserOverview(BaseModel): user: AdminUserListItem coin_balance: int cash_balance_cents: int + invite_cash_balance_cents: int # 邀请奖励金余额(与 cash_balance_cents 物理隔离) total_coin_earned: int comparison_total: int comparison_success: int @@ -88,6 +89,11 @@ class GrantCoinsRequest(BaseModel): class GrantCashRequest(BaseModel): + # 目标账户:金币兑换的现金(cash_balance_cents)与邀请奖励金(invite_cash_balance_cents)物理隔离, + # 各调各的、不可串。默认 coin_cash 兼容旧调用。 + account: Literal["coin_cash", "invite_cash"] = Field( + "coin_cash", description="目标账户:coin_cash=金币兑现金账户 / invite_cash=邀请奖励金账户" + ) mode: Literal["delta", "set"] = Field( "delta", description="delta=增减(amount_cents 为变动量) / set=设为(amount_cents 为目标值,须≥0)" )