237 lines
13 KiB
Markdown
237 lines
13 KiB
Markdown
---
|
||
name: 全局活动轮次
|
||
overview: Lucky Rewards 活动配置与轮次均改为全平台唯一:`lucky_reward_config`、`lucky_reward_tier_config`、`lucky_reward_cycle` 移除 `source`;转盘奖项已迁至 `reward_pool_item`(无 source,legacy `lucky_reward_prize_config` 确认删除);C 端仍用 `lucky_reward_player.source` 记录用户注册渠道。
|
||
todos:
|
||
- id: ddl-migration
|
||
content: 新增 lucky_reward_drop_source.sql(config/tier/cycle 删 source);确认 legacy prize_config 已 DROP;同步 lucky_reward.sql 与需求文档
|
||
status: completed
|
||
- id: config-model-service
|
||
content: LuckyRewardConfigModel 改为 findEnabled() 单例;ConfigService requireEnabledConfig/resolveActiveCycle/createNextCycle 去 source 参数
|
||
status: completed
|
||
- id: cycle-model-rollover
|
||
content: LuckyRewardCycleModel 全局 findActive/maxCycleNo;CycleRolloverService 去 source;TierConfig 去 source
|
||
status: completed
|
||
- id: console-callers
|
||
content: Logic/Service 配置查询改 findEnabled();轮次查询改 findActive();player 写入仍保留 resolvedSource
|
||
status: completed
|
||
- id: admin-crud
|
||
content: slot_admin Config/Tier Logic/Validate/Controller 去 source 字段;主配置改为单例校验
|
||
status: completed
|
||
- id: tests-verify
|
||
content: 更新集成测试 seed(全局 config+cycle);补跨渠道同 config/cycle 用例;phpunit + verify-slot-backend.sh
|
||
status: completed
|
||
isProject: false
|
||
---
|
||
|
||
# Lucky Rewards 配置与轮次全局化(不区分渠道)
|
||
|
||
## 目标与边界
|
||
|
||
| 维度 | 变更后 |
|
||
| --- | --- |
|
||
| `lucky_reward_config` | **全平台唯一**主配置,**移除 `source` 列**(表内仅保留一行或业务层强制单例) |
|
||
| `lucky_reward_tier_config` | **移除 `source` 列**,仅通过 `config_id` 关联主配置 |
|
||
| `lucky_reward_prize_config` | **已废弃**;奖项 SSOT 为 **`reward_pool` + `reward_pool_item`**(`pool_code=lucky_reward`,**本身无 `source`**)。若环境仍残留 legacy 表,执行 DROP,**无需**对其 ALTER 删列 |
|
||
| `lucky_reward_cycle` | **全平台唯一**进行中轮次,**移除 `source` 列** |
|
||
| `lucky_reward_player.source` 等业务表 | **保留**,表示**用户注册/归属渠道**,供后台统计筛选,与活动配置无关 |
|
||
| wheel 短码 `biz_id=cycle_id` | **不变**,全渠道共用同一 `cycle_id` |
|
||
|
||
```mermaid
|
||
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`](docs/requirements/lucky_rewards/00_整体技术方案.md) 一致:cycle 表本无 `source`;配置亦改为全局单套参数。
|
||
|
||
---
|
||
|
||
## 1. DDL 与数据迁移
|
||
|
||
新增 [`backend/slot_admin/db/lucky_reward_drop_source.sql`](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_config`(legacy)
|
||
|
||
> 现状:转盘奖项已迁移至 [`reward_pool.sql`](backend/slot_admin/db/reward_pool.sql) 的 `reward_pool_item`,console 通过 [`LuckyRewardConfigService::listLuckyRewardPoolItems()`](slot_console/app/service/luckyReward/LuckyRewardConfigService.php) 读 `pool_code=lucky_reward`,**无渠道维度**。
|
||
|
||
迁移步骤(写入 `lucky_reward_drop_source.sql` 末尾或独立段落):
|
||
|
||
1. 若 **`lucky_reward_prize_config` 表仍存在** 且尚未迁移:先跑 [`reward_pool_migrate_lucky_reward_prize.sql`](backend/slot_admin/db/reward_pool_migrate_lucky_reward_prize.sql)(迁移脚本内 `@config_id` 改为取合并后的**唯一** config 行,不再 `WHERE source='default'`)
|
||
2. 验证 `reward_pool_item` 数据完整后,执行 [`reward_pool_drop_legacy_prize_config.sql`](backend/slot_admin/db/reward_pool_drop_legacy_prize_config.sql):`DROP TABLE IF EXISTS lucky_reward_prize_config`
|
||
3. **无 PHP Model/Logic 改动**(已无 `LuckyRewardPrizeConfig*` 代码路径;后台菜单已指向 `RewardPoolItemController`)
|
||
|
||
文档同步:[`04_slot_console_活动主流程方案.md`](docs/requirements/lucky_rewards/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_status` → `DROP COLUMN source` → `ADD KEY idx_status (status)`
|
||
|
||
### 1.5 同步基线 DDL
|
||
|
||
- [`backend/slot_admin/db/lucky_reward.sql`](backend/slot_admin/db/lucky_reward.sql) — config / tier / cycle 去掉 `source`;**不包含**已废弃的 prize_config 建表
|
||
- [`docs/requirements/lucky_rewards/02_管理后台方案.md`](docs/requirements/lucky_rewards/02_管理后台方案.md) §2.1 / §2.3 / §2.4(去掉「用 source 区分渠道」;§2.2 已说明 prize 在 reward_pool)
|
||
|
||
---
|
||
|
||
## 2. Model 层
|
||
|
||
### 2.1 [`LuckyRewardConfigModel`](slot_console/app/model/common/LuckyRewardConfigModel.php)(console + admin 同步)
|
||
|
||
- 删除 `@property string $source`
|
||
- 删除 `findEnabledBySourceWithFallback(string $source)`
|
||
- 新增 `findEnabled(): ?self` — `status=STATUS_ENABLED`,`order id asc`,取唯一启用配置
|
||
- admin Model 去掉 search 按 source 的 scope(若有)
|
||
|
||
### 2.2 [`LuckyRewardTierConfigModel`](slot_console/app/model/common/LuckyRewardTierConfigModel.php)
|
||
|
||
- 删除 `@property string $source`
|
||
- `listEnabledByConfigId(int $configId)` 不变
|
||
|
||
### 2.3 [`LuckyRewardCycleModel`](slot_console/app/model/common/LuckyRewardCycleModel.php)
|
||
|
||
- 删除 `source` 与 `findActiveBySource` / `maxCycleNo(string $source)`
|
||
- 新增 `findActive(): ?self`、`maxCycleNo(): int`
|
||
|
||
---
|
||
|
||
## 3. Service 层(slot_console)
|
||
|
||
### 3.1 [`LuckyRewardConfigService`](slot_console/app/service/luckyReward/LuckyRewardConfigService.php)
|
||
|
||
| 方法 | 改造 |
|
||
| --- | --- |
|
||
| `requireEnabledConfig()` | 无参;内部 `findEnabled()`,未开启抛 `BusinessException` |
|
||
| `resolveActiveCycle(LuckyRewardConfigModel $activityConfig)` | 去掉 `$source`;查 `findActive()`;创建下一轮调 `createNextCycle($activityConfig, ...)` |
|
||
| `createNextCycle(LuckyRewardConfigModel $activityConfig, ?int $baseTimestamp = null)` | 去掉 `$source`;`cycle_days` 直接取自传入的**唯一** `$activityConfig`;不写 cycle.source;事务 + `FOR UPDATE` 防并发双 active |
|
||
|
||
### 3.2 [`LuckyRewardCycleRolloverService`](slot_console/app/service/luckyReward/LuckyRewardCycleRolloverService.php)
|
||
|
||
- `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`](slot_console/app/api/logic/LuckyRewardLogic.php) | enter/spin/openBox/toBalance:配置无参;轮次 `findActive()`;player 写入仍用 `$resolvedSource` |
|
||
| [`LuckyRewardInviteLogic.php`](slot_console/app/api/logic/LuckyRewardInviteLogic.php) | invite_bind、弹窗、claim:配置/轮次全局;helper/player 仍写用户渠道 |
|
||
| [`LuckyRewardSupportInvitePendingService.php`](slot_console/app/service/luckyReward/LuckyRewardSupportInvitePendingService.php) | flush 时查全局 config + active cycle |
|
||
| [`ShareService.php`](slot_console/app/service/ShareService.php) | `resolveOrCreateWheelShareRow`:`requireEnabledConfig()` + `resolveActiveCycle($config)` |
|
||
|
||
Controller 仍向 Logic 传 `$request->userEntity->source`(供 player 归属),Logic 内部不再用它查 config。
|
||
|
||
---
|
||
|
||
## 5. 管理后台(slot_admin)
|
||
|
||
| 文件 | 改动 |
|
||
| --- | --- |
|
||
| [`LuckyRewardConfigLogic`](backend/slot_admin/app/game/logic/LuckyRewardConfigLogic.php) | 去掉 `source` 写入与 `ensureSourceUnique`;新增 **单例校验**(全表仅允许 1 行,update 除外) |
|
||
| [`LuckyRewardConfigValidate`](backend/slot_admin/app/game/validate/LuckyRewardConfigValidate.php) | save/update scene 移除 `source` |
|
||
| [`LuckyRewardConfigController`](backend/slot_admin/app/game/controller/LuckyRewardConfigController.php) | index 去掉 source 筛选;options 返回 `id,status`(无 source) |
|
||
| [`LuckyRewardTierConfigLogic`](backend/slot_admin/app/game/logic/LuckyRewardTierConfigLogic.php) | 去掉 `source` 写入与列表 `config_source` 展示 |
|
||
| [`LuckyRewardTierConfigValidate`](backend/slot_admin/app/game/validate/LuckyRewardTierConfigValidate.php) | 移除 `source` |
|
||
| Model PHPDoc | Config / Tier / Cycle 三表同步 |
|
||
|
||
[`LuckyRewardStatsLogic`](backend/slot_admin/app/game/logic/LuckyRewardStatsLogic.php):**不变** — 统计仍可按 `lucky_reward_player.source` 筛用户渠道。
|
||
|
||
**前端**(若 [`slot_admin_vue`](backend/slot_admin_vue) 有 Lucky Reward 配置页渠道字段):移除主配置/分层表单中的「渠道」输入与列表列(本计划标注为 admin 联调项,改 PHP 后前端需同步)。
|
||
|
||
---
|
||
|
||
## 6. 测试
|
||
|
||
[`LuckyRewardDbTestCase::seedEnabledActivity`](slot_console/tests/Integration/LuckyRewardDbTestCase.php):
|
||
|
||
- 不再按 `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
|
||
|
||
---
|
||
|
||
## 涉及文件汇总
|
||
|
||
**DDL**:`lucky_reward_drop_source.sql`、`lucky_reward.sql`、`reward_pool_migrate_lucky_reward_prize.sql`(改 config 选取逻辑)、`reward_pool_drop_legacy_prize_config.sql`、需求文档
|
||
|
||
**slot_console**:`LuckyRewardConfigModel`、`LuckyRewardTierConfigModel`、`LuckyRewardCycleModel`、`LuckyRewardConfigService`、`LuckyRewardCycleRolloverService`、`LuckyRewardLogic`、`LuckyRewardInviteLogic`、`LuckyRewardSupportInvitePendingService`、`ShareService`、集成测试
|
||
|
||
**slot_admin**:`LuckyRewardConfigModel`、`LuckyRewardTierConfigModel`、`LuckyRewardCycleModel`、`LuckyRewardConfigLogic`、`LuckyRewardTierConfigLogic`、对应 Validate/Controller
|
||
|
||
**无需改动(已无 source)**:`reward_pool` / `reward_pool_item`、`RewardPoolItemLogic`(转盘奖项 CRUD)
|
||
|
||
**保留 source 不改**:`lucky_reward_player` 及 spin/helper/record/grant 业务表;`LuckyRewardStatsLogic` 玩家渠道筛选
|