feat(invite): 邀请奖励金账户(与金币隔离) + 比价发奖 + 奖励金提现
- 新增邀请奖励金独立账本: coin_account 加余额列 + invite_cash_transaction 流水表, 与金币现金物理隔离 - bind 改 v2: 绑定只发被邀请人新人金币; 邀请人改在好友首次成功比价时发 2 元奖励金(幂等只发一次) - 提现链路按 source 分流: 扣款/退款/对账/分页各认账户, 两本账不串 - /invite/me 返回奖励金余额/累计提现/7天倒计时 - 新增 migration(建表加列) + 比价发奖与提现隔离测试
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
"""invite cash account isolation + compare reward tracking (🅱-1)
|
||||
|
||||
Revision ID: invite_cash_account_and_compare_reward
|
||||
Revises: feedback_review_fields
|
||||
Create Date: 2026-06-23 00:00:00.000000
|
||||
|
||||
邀请功能 v2 账户隔离 + 比价发奖追踪:
|
||||
- coin_account 加 invite_cash_balance_cents(邀请奖励金独立余额,与金币兑换的 cash 物理隔离)
|
||||
- withdraw_order 加 source(标记提现扣哪个账户,退款退回对应账户;旧单默认 coin_cash)
|
||||
- invite_relation 加比价发奖追踪三列(好友比价多次只发一次)
|
||||
- 新增 invite_cash_transaction 表(邀请奖励金独立流水账本)
|
||||
|
||||
加列均 NOT NULL + server_default,存量行自动填默认值,安全。
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'invite_cash_account_and_compare_reward'
|
||||
down_revision: Union[str, Sequence[str], None] = 'feedback_review_fields'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# 1. 邀请奖励金独立余额(与金币兑换的 cash_balance_cents 物理隔离;红线:两本账不可累加)
|
||||
op.add_column(
|
||||
'coin_account',
|
||||
sa.Column('invite_cash_balance_cents', sa.Integer(), nullable=False, server_default='0'),
|
||||
)
|
||||
# 2. 提现单标记账户来源:coin_cash / invite_cash,退款退回对应账户(旧单默认 coin_cash)
|
||||
op.add_column(
|
||||
'withdraw_order',
|
||||
sa.Column('source', sa.String(length=16), nullable=False, server_default='coin_cash'),
|
||||
)
|
||||
# 3. 邀请关系加比价发奖追踪(好友"下载+登录+比价一次"→ 给邀请人发奖,只发一次)
|
||||
op.add_column(
|
||||
'invite_relation',
|
||||
sa.Column('compare_reward_granted', sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
)
|
||||
op.add_column(
|
||||
'invite_relation',
|
||||
sa.Column('compare_reward_cents', sa.Integer(), nullable=False, server_default='0'),
|
||||
)
|
||||
op.add_column(
|
||||
'invite_relation',
|
||||
sa.Column('compare_rewarded_at', sa.DateTime(timezone=True), nullable=True),
|
||||
)
|
||||
# 4. 邀请奖励金独立流水表(结构同 cash_transaction;balance_after 记 invite_cash_balance_cents)
|
||||
op.create_table(
|
||||
'invite_cash_transaction',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('amount_cents', sa.Integer(), nullable=False),
|
||||
sa.Column('balance_after_cents', sa.Integer(), nullable=False),
|
||||
sa.Column('biz_type', sa.String(length=32), nullable=False),
|
||||
sa.Column('ref_id', sa.String(length=64), nullable=True),
|
||||
sa.Column('remark', sa.String(length=128), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id']),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
)
|
||||
op.create_index('ix_invite_cash_transaction_user_id', 'invite_cash_transaction', ['user_id'])
|
||||
op.create_index('ix_invite_cash_transaction_created_at', 'invite_cash_transaction', ['created_at'])
|
||||
# 提现退款幂等:一个提现单只退一次(partial unique,对齐 cash_transaction 的 withdraw_refund 去重)
|
||||
op.create_index(
|
||||
'ux_invite_cash_txn_refund_ref',
|
||||
'invite_cash_transaction',
|
||||
['ref_id'],
|
||||
unique=True,
|
||||
sqlite_where=sa.text("biz_type = 'invite_withdraw_refund' AND ref_id IS NOT NULL"),
|
||||
postgresql_where=sa.text("biz_type = 'invite_withdraw_refund' AND ref_id IS NOT NULL"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index('ux_invite_cash_txn_refund_ref', table_name='invite_cash_transaction')
|
||||
op.drop_index('ix_invite_cash_transaction_created_at', table_name='invite_cash_transaction')
|
||||
op.drop_index('ix_invite_cash_transaction_user_id', table_name='invite_cash_transaction')
|
||||
op.drop_table('invite_cash_transaction')
|
||||
op.drop_column('invite_relation', 'compare_rewarded_at')
|
||||
op.drop_column('invite_relation', 'compare_reward_cents')
|
||||
op.drop_column('invite_relation', 'compare_reward_granted')
|
||||
op.drop_column('withdraw_order', 'source')
|
||||
op.drop_column('coin_account', 'invite_cash_balance_cents')
|
||||
Reference in New Issue
Block a user