Files
cursor/plans/全局活动轮次_0e1173f0.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

13 KiB
Raw Permalink Blame History

name, overview, todos, isProject
name overview todos isProject
全局活动轮次 Lucky Rewards 活动配置与轮次均改为全平台唯一:`lucky_reward_config`、`lucky_reward_tier_config`、`lucky_reward_cycle` 移除 `source`;转盘奖项已迁至 `reward_pool_item`(无 sourcelegacy `lucky_reward_prize_config` 确认删除C 端仍用 `lucky_reward_player.source` 记录用户注册渠道。
id content status
ddl-migration 新增 lucky_reward_drop_source.sqlconfig/tier/cycle 删 source确认 legacy prize_config 已 DROP同步 lucky_reward.sql 与需求文档 completed
id content status
config-model-service LuckyRewardConfigModel 改为 findEnabled() 单例ConfigService requireEnabledConfig/resolveActiveCycle/createNextCycle 去 source 参数 completed
id content status
cycle-model-rollover LuckyRewardCycleModel 全局 findActive/maxCycleNoCycleRolloverService 去 sourceTierConfig 去 source completed
id content status
console-callers Logic/Service 配置查询改 findEnabled();轮次查询改 findActive()player 写入仍保留 resolvedSource completed
id content status
admin-crud slot_admin Config/Tier Logic/Validate/Controller 去 source 字段;主配置改为单例校验 completed
id content status
tests-verify 更新集成测试 seed全局 config+cycle补跨渠道同 config/cycle 用例phpunit + verify-slot-backend.sh completed
false

Lucky Rewards 配置与轮次全局化(不区分渠道)

目标与边界

维度 变更后
lucky_reward_config 全平台唯一主配置,移除 source(表内仅保留一行或业务层强制单例)
lucky_reward_tier_config 移除 source,仅通过 config_id 关联主配置
lucky_reward_prize_config 已废弃;奖项 SSOT 为 reward_pool + reward_pool_itempool_code=lucky_reward本身无 source)。若环境仍残留 legacy 表,执行 DROP无需对其 ALTER 删列
lucky_reward_cycle 全平台唯一进行中轮次,移除 source
lucky_reward_player.source 等业务表 保留,表示用户注册/归属渠道,供后台统计筛选,与活动配置无关
wheel 短码 biz_id=cycle_id 不变,全渠道共用同一 cycle_id
flowchart TB
  subgraph before [现状]
    ConfigA[config source=A]
    ConfigB[config source=B]
    ConfigA --> CycleA[cycle source=A]
    ConfigB --> CycleB[cycle source=B]
  end
  subgraph after [改造后]
    GlobalConfig[单一 lucky_reward_config]
    GlobalCycle[单一 active cycle]
    PlayerA[player source=渠道A]
    PlayerB[player source=渠道B]
    GlobalConfig --> GlobalCycle
    GlobalCycle --> PlayerA
    GlobalCycle --> PlayerB
  end

00_整体技术方案.md 一致cycle 表本无 source;配置亦改为全局单套参数。


1. DDL 与数据迁移

新增 backend/slot_admin/db/lucky_reward_drop_source.sql(合并原 cycle 脚本,一次迁移三表):

1.1 lucky_reward_config

  1. 合并多行配置(若存在按渠道多行)

    • 保留 source='default'status=1 的行;若无 default 启用行则保留 id 最小且 status=1 的行
    • 将其余行的 lucky_reward_tier_config.config_id 重指向保留行
    • 删除其余 config 行
  2. 改表

    • DROP INDEX idx_source
    • DROP COLUMN source

1.2 lucky_reward_tier_config

  • DROP COLUMN source(已通过 config_id 关联列冗余admin Tier Logic/Validate 同步去 source 字段)

1.3 lucky_reward_prize_configlegacy

现状:转盘奖项已迁移至 reward_pool.sqlreward_pool_itemconsole 通过 LuckyRewardConfigService::listLuckyRewardPoolItems()pool_code=lucky_reward无渠道维度

迁移步骤(写入 lucky_reward_drop_source.sql 末尾或独立段落):

  1. lucky_reward_prize_config 表仍存在 且尚未迁移:先跑 reward_pool_migrate_lucky_reward_prize.sql(迁移脚本内 @config_id 改为取合并后的唯一 config 行,不再 WHERE source='default'
  2. 验证 reward_pool_item 数据完整后,执行 reward_pool_drop_legacy_prize_config.sqlDROP TABLE IF EXISTS lucky_reward_prize_config
  3. 无 PHP Model/Logic 改动(已无 LuckyRewardPrizeConfig* 代码路径;后台菜单已指向 RewardPoolItemController

文档同步:04_slot_console_活动主流程方案.md 中将 lucky_reward_prize_config 表述改为 reward_pool_item

1.4 lucky_reward_cycle

  1. 合并重复 status=1:保留 id 最大一行,其余改 STATUS_ENDED=2(不合并 player 行)
  2. DROP INDEX idx_source_statusDROP COLUMN sourceADD KEY idx_status (status)

1.5 同步基线 DDL


2. Model 层

2.1 LuckyRewardConfigModelconsole + admin 同步)

  • 删除 @property string $source
  • 删除 findEnabledBySourceWithFallback(string $source)
  • 新增 findEnabled(): ?selfstatus=STATUS_ENABLEDorder id asc,取唯一启用配置
  • admin Model 去掉 search 按 source 的 scope若有

2.2 LuckyRewardTierConfigModel

  • 删除 @property string $source
  • listEnabledByConfigId(int $configId) 不变

2.3 LuckyRewardCycleModel

  • 删除 sourcefindActiveBySource / maxCycleNo(string $source)
  • 新增 findActive(): ?selfmaxCycleNo(): int

3. Service 层slot_console

3.1 LuckyRewardConfigService

方法 改造
requireEnabledConfig() 无参;内部 findEnabled(),未开启抛 BusinessException
resolveActiveCycle(LuckyRewardConfigModel $activityConfig) 去掉 $source;查 findActive();创建下一轮调 createNextCycle($activityConfig, ...)
createNextCycle(LuckyRewardConfigModel $activityConfig, ?int $baseTimestamp = null) 去掉 $sourcecycle_days 直接取自传入的唯一 $activityConfig;不写 cycle.source事务 + FOR UPDATE 防并发双 active

3.2 LuckyRewardCycleRolloverService

  • rolloverExpiredCycle:去掉 $expiredCycle->source
  • 开启下一轮:findActive() 为空且 findEnabled() 非空时 createNextCycle($activityConfig, $nowTimestamp)

4. Logic / 其它调用点slot_console

配置读取:所有 findEnabledBySourceWithFallback($resolvedSource) / requireEnabledConfig($source) 改为无参全局读取。

轮次读取:所有 findActiveBySource(...) 改为 findActive()

仍保留 resolveSource($uid, $source) 的场景(仅写业务行渠道归属,用于查配置):

  • lucky_reward_player.source
  • lucky_reward_spin_grant.source
  • lucky_reward_helper.source
  • lucky_reward_spin_record.source
  • 日志 / MQ 回调 channel_code 字段
文件 改动要点
LuckyRewardLogic.php enter/spin/openBox/toBalance配置无参轮次 findActive()player 写入仍用 $resolvedSource
LuckyRewardInviteLogic.php invite_bind、弹窗、claim配置/轮次全局helper/player 仍写用户渠道
LuckyRewardSupportInvitePendingService.php flush 时查全局 config + active cycle
ShareService.php resolveOrCreateWheelShareRowrequireEnabledConfig() + resolveActiveCycle($config)

Controller 仍向 Logic 传 $request->userEntity->source(供 player 归属Logic 内部不再用它查 config。


5. 管理后台slot_admin

文件 改动
LuckyRewardConfigLogic 去掉 source 写入与 ensureSourceUnique;新增 单例校验(全表仅允许 1 行update 除外)
LuckyRewardConfigValidate save/update scene 移除 source
LuckyRewardConfigController index 去掉 source 筛选options 返回 id,status(无 source
LuckyRewardTierConfigLogic 去掉 source 写入与列表 config_source 展示
LuckyRewardTierConfigValidate 移除 source
Model PHPDoc Config / Tier / Cycle 三表同步

LuckyRewardStatsLogic不变 — 统计仍可按 lucky_reward_player.source 筛用户渠道。

前端(若 slot_admin_vue 有 Lucky Reward 配置页渠道字段):移除主配置/分层表单中的「渠道」输入与列表列(本计划标注为 admin 联调项,改 PHP 后前端需同步)。


6. 测试

LuckyRewardDbTestCase::seedEnabledActivity

  • 不再按 TEST_SOURCE 创建独立 config改为 更新全局唯一 config 为 ENABLED(或 insert 仅当表空)
  • cycle seed 不写 source;先 findActive() 结束旧 active
  • player / helper 测试行仍可写 source => TEST_SOURCE 模拟用户渠道

更新所有集成测试中的 findEnabledBySourceWithFallback / findActiveBySource / config 按 source 查询。

新增用例:两渠道用户(不同 player.source)进入活动,得到相同 config 参数与 cycle_id


7. 部署顺序

  1. 执行 lucky_reward_drop_source.sql(合并 config + 删列)
  2. 同批发布 slot_console + slot_admin删列后旧代码会 SQL 报错)
  3. slot_admin_vue 去渠道字段(可与后端同批或紧随其后)

8. 风险与验收

风险 处理
线上多渠道各有 config / active cycle 迁移脚本合并为单行 config + 单行 active cycle
非 default 渠道 config 有差异化参数 迁移保留 default 启用行;运营需事先确认 default 参数为 SSOT
并发创建双 active cycle createNextCycle 事务 + 行锁
后台误建第二条 config Logic 单例校验拦截

验收清单

  • 全渠道用户 enter 读到同一套 target_amount / cycle_days / tier
  • 全渠道同一 cycle_id;轮转后仅一条新 active
  • 后台主配置 CRUD 无渠道字段,不可新增第二条
  • 统计页仍可按 玩家 source 筛选
  • phpunit Lucky Rewards 集成测 + verify-slot-backend.sh PASS

涉及文件汇总

DDLlucky_reward_drop_source.sqllucky_reward.sqlreward_pool_migrate_lucky_reward_prize.sql(改 config 选取逻辑)、reward_pool_drop_legacy_prize_config.sql、需求文档

slot_consoleLuckyRewardConfigModelLuckyRewardTierConfigModelLuckyRewardCycleModelLuckyRewardConfigServiceLuckyRewardCycleRolloverServiceLuckyRewardLogicLuckyRewardInviteLogicLuckyRewardSupportInvitePendingServiceShareService、集成测试

slot_adminLuckyRewardConfigModelLuckyRewardTierConfigModelLuckyRewardCycleModelLuckyRewardConfigLogicLuckyRewardTierConfigLogic、对应 Validate/Controller

无需改动(已无 sourcereward_pool / reward_pool_itemRewardPoolItemLogic(转盘奖项 CRUD

保留 source 不改lucky_reward_player 及 spin/helper/record/grant 业务表;LuckyRewardStatsLogic 玩家渠道筛选