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
63 lines
2.8 KiB
Python
63 lines
2.8 KiB
Python
"""withdraw_order.user_name 列 + ad_watch_log 表
|
|
|
|
提现审核功能:WithdrawOrder 加 user_name(审核异步打款时传给微信的实名,达额转账要求)。
|
|
看广告时长防刷:新增 ad_watch_log 表(前端 onAdClose 上报观看秒数,按 (user_id, watch_date)
|
|
聚合做"每天 50 分钟"硬上限)。
|
|
|
|
Revision ID: withdraw_review_ad_watch
|
|
Revises: ebb6af5c0b56
|
|
Create Date: 2026-06-05 12:00:00.000000
|
|
|
|
注:本迁移原 down_revision=ad60a1b2c3d4(分支创建时的 head);rebase 合 main 后 main 侧已新增
|
|
app_config / price_report 等迁移并 merge 出 head=ebb6af5c0b56,故改挂到其上,保持单 head(本迁移
|
|
与那些表互不相干,叠加顺序无影响)。
|
|
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = 'withdraw_review_ad_watch'
|
|
down_revision: Union[str, Sequence[str], None] = 'ebb6af5c0b56'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# ① 提现单加实名列:审核通过后异步打款时传给微信(达额转账要求),发起提现时存下
|
|
with op.batch_alter_table('withdraw_order', schema=None) as batch_op:
|
|
batch_op.add_column(sa.Column('user_name', sa.String(length=64), nullable=True))
|
|
|
|
# ② 看广告观看时长表:前端上报观看秒数,(user_id, watch_date) 聚合做每日 50 分钟防刷主闸
|
|
op.create_table(
|
|
'ad_watch_log',
|
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
|
sa.Column('user_id', sa.Integer(), nullable=False),
|
|
sa.Column('watch_seconds', sa.Integer(), server_default='0', nullable=False),
|
|
sa.Column('watch_date', sa.String(length=10), nullable=False),
|
|
sa.Column(
|
|
'created_at', sa.DateTime(timezone=True),
|
|
server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False,
|
|
),
|
|
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
|
sa.PrimaryKeyConstraint('id'),
|
|
)
|
|
with op.batch_alter_table('ad_watch_log', schema=None) as batch_op:
|
|
batch_op.create_index(batch_op.f('ix_ad_watch_log_user_id'), ['user_id'], unique=False)
|
|
batch_op.create_index(batch_op.f('ix_ad_watch_log_watch_date'), ['watch_date'], unique=False)
|
|
batch_op.create_index(batch_op.f('ix_ad_watch_log_created_at'), ['created_at'], unique=False)
|
|
|
|
|
|
def downgrade() -> None:
|
|
with op.batch_alter_table('ad_watch_log', schema=None) as batch_op:
|
|
batch_op.drop_index(batch_op.f('ix_ad_watch_log_created_at'))
|
|
batch_op.drop_index(batch_op.f('ix_ad_watch_log_watch_date'))
|
|
batch_op.drop_index(batch_op.f('ix_ad_watch_log_user_id'))
|
|
op.drop_table('ad_watch_log')
|
|
|
|
with op.batch_alter_table('withdraw_order', schema=None) as batch_op:
|
|
batch_op.drop_column('user_name')
|