436b2a36da
代码 review 后端运营后台后修复 7 项: - admins: 降级/禁用 super_admin 前校验仍≥1 个 active 超管,防零超管死局;审计记 before/after - queries: 时间筛选统一 tz-aware(列为 timestamptz),比较绝对时刻、不再依赖 DB 会话时区 - price_report: approve/reject 加行锁,防并发/连点双倍发奖 - users/wallet: get_or_create_account 增可选 lock 参(默认关,不动 C 端);调金币/现金读余额加行锁防双写错位 - ad_audit: 复算排序补 id 次级键,时间戳并列时顺序确定 - withdraw: health-check 限 finance/super,不暴露密钥路径给全员 - schemas: 调账/拒绝 reason 加 trim 校验,拒纯空白 测试: admin 套件 37 passed,全量 140 passed,无新增失败。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
"""admin 用户管理 schemas。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
|
|
class AdminUserListItem(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
phone: str
|
|
nickname: str | None = None
|
|
register_channel: str
|
|
status: str
|
|
debug_trace_enabled: bool = False
|
|
wechat_openid: str | None = None
|
|
created_at: datetime
|
|
last_login_at: datetime
|
|
|
|
|
|
class AdminUserOverview(BaseModel):
|
|
"""用户 360 概览:基础资料 + 钱包余额 + 各项 count(历史明细走各自分页接口)。"""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
user: AdminUserListItem
|
|
coin_balance: int
|
|
cash_balance_cents: int
|
|
total_coin_earned: int
|
|
comparison_total: int
|
|
comparison_success: int
|
|
withdraw_total: int
|
|
withdraw_success_cents: int
|
|
feedback_total: int
|
|
|
|
|
|
def _strip_reason(v: str) -> str:
|
|
# min_length=1 放过纯空白(" "),trim 后再校验非空,避免审计记到空原因
|
|
if not v.strip():
|
|
raise ValueError("操作原因不能为空")
|
|
return v.strip()
|
|
|
|
|
|
class GrantCoinsRequest(BaseModel):
|
|
mode: Literal["delta", "set"] = Field(
|
|
"delta", description="delta=增减(amount 为变动量) / set=设为(amount 为目标值,须≥0)"
|
|
)
|
|
amount: int = Field(
|
|
...,
|
|
description="delta 模式:金币变动(正=增加,负=扣减,不可为 0);set 模式:目标金币值(须≥0)",
|
|
)
|
|
reason: str = Field(..., min_length=1, max_length=128, description="操作原因(必填,入审计)")
|
|
|
|
_v_reason = field_validator("reason")(_strip_reason)
|
|
|
|
|
|
class GrantCashRequest(BaseModel):
|
|
mode: Literal["delta", "set"] = Field(
|
|
"delta", description="delta=增减(amount_cents 为变动量) / set=设为(amount_cents 为目标值,须≥0)"
|
|
)
|
|
amount_cents: int = Field(
|
|
...,
|
|
description="delta 模式:现金变动(分,正=增加,负=扣减,不可为 0);set 模式:目标现金值(分,须≥0)",
|
|
)
|
|
reason: str = Field(..., min_length=1, max_length=128, description="操作原因(必填,入审计)")
|
|
|
|
_v_reason = field_validator("reason")(_strip_reason)
|
|
|
|
|
|
class SetUserStatusRequest(BaseModel):
|
|
status: Literal["active", "disabled"] = Field(
|
|
..., description="active=解封 / disabled=封禁(注销 deleted 不走此接口)"
|
|
)
|
|
|
|
|
|
class SetDebugTraceRequest(BaseModel):
|
|
enabled: bool = Field(..., description="是否给该用户开「复制调试链接」权限")
|