c9578f5ba2
轮播 feed(marquee_seed,全新): - 新增 marquee_seed 表 + admin CRUD/批量生成/预览;真实比价记录(success 且 0<省额≤300元、按 user 去重)优先,种子兜底混播 - 种子为「生成规则」:用户名可空(空则按脱敏格式随机合成、避开同屏撞名),金额改 [min,max] 区间随机,feed 公平随机抽取(不看 sort_order) - 新增 GET /api/v1/platform/savings-feed(无鉴权);展示时间统一刷新为相对当前的最近时刻 三统计配置(platform_stat_display)增强: - 自增长新增「绝对增量」方式(random_kind=add,每周期 +[step_min,step_max]) - 真实值模式加基数偏移(real_offset,展示=真实+偏移) - 只增不减护栏(allow_decrease 默认关,real/manual 不回退防门面缩水) - 累计节省 real 口径只计 0<单条≤300元防虚高;倍率上限 5.0→1.5;PATCH 支持 apply_now 立即更新 迁移:marquee_seed_table / marquee_seed_range / platform_stat_growth_offset(均可逆,已验证) 文档:API/DB 对应文档同步更新 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
51 lines
2.5 KiB
Python
51 lines
2.5 KiB
Python
"""marquee_seed 升级为「生成规则」:用户名可空 + 金额拆 min/max 区间
|
|
|
|
把种子从「死记录」升级:
|
|
- masked_user 改 nullable(留空 → feed 随机合成脱敏用户名)
|
|
- saved_amount_cents(单值)拆成 min_cents / max_cents(feed 在区间内随机取值;固定金额则相等)
|
|
现有 6 条种子:min_cents = max_cents = 原 saved_amount_cents,masked_user 原样保留。
|
|
|
|
Revision ID: marquee_seed02
|
|
Revises: marquee_seed01
|
|
Create Date: 2026-06-07 11:00:00.000000
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision: str = 'marquee_seed02'
|
|
down_revision: Union[str, Sequence[str], None] = 'marquee_seed01'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# 1) 先加可空新列,使现有行合法
|
|
with op.batch_alter_table('marquee_seed', schema=None) as batch_op:
|
|
batch_op.add_column(sa.Column('min_cents', sa.Integer(), nullable=True))
|
|
batch_op.add_column(sa.Column('max_cents', sa.Integer(), nullable=True))
|
|
# 2) 用旧单值回填区间(固定金额)
|
|
op.execute('UPDATE marquee_seed SET min_cents = saved_amount_cents, max_cents = saved_amount_cents')
|
|
# 3) 收紧:区间非空、用户名可空、删旧列
|
|
with op.batch_alter_table('marquee_seed', schema=None) as batch_op:
|
|
batch_op.alter_column('min_cents', existing_type=sa.Integer(), nullable=False)
|
|
batch_op.alter_column('max_cents', existing_type=sa.Integer(), nullable=False)
|
|
batch_op.alter_column('masked_user', existing_type=sa.String(length=64), nullable=True)
|
|
batch_op.drop_column('saved_amount_cents')
|
|
|
|
|
|
def downgrade() -> None:
|
|
# 反向:恢复单值列(取下限),用户名回非空
|
|
with op.batch_alter_table('marquee_seed', schema=None) as batch_op:
|
|
batch_op.add_column(sa.Column('saved_amount_cents', sa.Integer(), nullable=True))
|
|
op.execute('UPDATE marquee_seed SET saved_amount_cents = min_cents')
|
|
# 自动合成名的种子 masked_user 为 NULL,降级前补占位符以满足非空
|
|
op.execute("UPDATE marquee_seed SET masked_user = '用户********000' WHERE masked_user IS NULL")
|
|
with op.batch_alter_table('marquee_seed', schema=None) as batch_op:
|
|
batch_op.alter_column('saved_amount_cents', existing_type=sa.Integer(), nullable=False)
|
|
batch_op.alter_column('masked_user', existing_type=sa.String(length=64), nullable=False)
|
|
batch_op.drop_column('max_cents')
|
|
batch_op.drop_column('min_cents')
|