Files
cursor/plans/lucky_reward_奖池迁移_efaf709a.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

213 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: Lucky Reward 奖池迁移
overview: 采用全平台统一的 reward_pool / reward_pool_item以 pool_code = lucky_reward 承载大转盘手动 Spin 奖项;外层仅 prize_type 表达类型lucky_reward 业务参数(含 unlockInviteCount一律进 config逐步替换 lucky_reward_prize_config。
todos:
- id: ddl-seed
content: s_common 建 reward_pool / reward_pool_item含 prize_type 外层字段seed pool_code=lucky_reward编写从 lucky_reward_prize_config 的迁移脚本
status: completed
- id: console-draw
content: slot_consoleRewardPoolItemModel + ConfigService/DrawService/LuckyRewardLogic 改读 lucky_reward 奖池Draw 读 prize_type 列
status: completed
- id: admin-crud
content: slot_admin替换 LuckyRewardPrizeConfig CRUD 为 lucky_reward 奖池项管理,更新菜单权限
status: completed
- id: test-docs-cleanup
content: 单测/集成测、需求文档更新、删除旧 Model 与 lucky_reward_prize_config 表
status: completed
isProject: false
---
# Lucky Reward 奖池方案评估与迁移计划
## 结论:`pool_code = lucky_reward` 可行,推荐做
**方向正确**:把「可复用奖池」与「活动专用抽奖/入账」拆开,比继续扩 [`lucky_reward_prize_config`](docs/requirements/lucky_rewards/02_管理后台方案.md) 更适合后续其它活动复用同一套表结构。
**与你已确认的前提对齐**
- 全平台 **一个** 奖池:`pool_code = 'lucky_reward'`
- **替换** 现有 [`lucky_reward_prize_config`](slot_console/app/model/common/LuckyRewardPrizeConfigModel.php)(非并存长期双写)
- **外层仅 `prize_type` 表达奖项类型**lucky_reward 业务参数(含开放条件)**一律进 `config`**,避免个别字段外置导致表结构膨胀
```mermaid
flowchart LR
subgraph admin [slot_admin]
PoolCRUD[RewardPoolItem CRUD]
end
subgraph db [s_common]
RP[reward_pool pool_code=lucky_reward]
RPI["reward_pool_item prize_type + config"]
end
subgraph console [slot_console]
Draw[LuckyRewardDrawService]
Spin[LuckyRewardLogic.executeManualSpin]
end
PoolCRUD --> RPI
RP --> RPI
RPI --> Draw
Draw --> Spin
```
---
## 核心设计:`prize_type` 外层 + 业务参数全进 `config`
**字段分层原则**
| 层级 | 字段 | 说明 |
| --- | --- | --- |
| 表通用列 | `pool_id``pool_code``item_code``item_name``prize_type``weight``display_text``icon_url``status``sort` | 奖池项元数据,各 pool 共用 |
| `config` JSON | lucky_reward 及后续各 pool 的**业务参数** | 含开放条件、金额区间等;**禁止**再拆个别业务字段到表列 |
| 禁止 | 表列 `unlock_invite_count` 等 lucky_reward 专用字段 | 与「通用奖池表 + pool 级 config 约定」冲突 |
### 1. 表字段调整(相对你初版 DDL
原 DDL 中的 `reward_type` **改为 `prize_type`**TINYINT奖项命中语义。不同 `pool_code` 可约定不同枚举;`lucky_reward` 池定义如下:
| prize_type | 含义 | 抽奖 | 入账行为(与现网一致) |
| --- | --- | --- | --- |
| 1 | 随机金额 | 参与权重 | My Amount 增加(封顶至 target |
| 2 | 1x Spin | 参与权重 | spin_available +1 |
| 3 | Cash Out | 参与权重 | playerStatus → 可提取 |
| 4 | Jackpot 展示 | **不参与**weight=0 或 Draw 过滤) | 无入账,仅转盘 UI 展示 |
常量命名沿用现有 [`LuckyRewardPrizeConfigModel::PRIZE_TYPE_*`](slot_console/app/model/common/LuckyRewardPrizeConfigModel.php),迁移到 `RewardPoolItemModel`(或共用常量类),保证 [`lucky_reward_spin_record.prize_type`](docs/requirements/lucky_rewards/02_管理后台方案.md) 与 [`DrawResultEntity::prizeType`](slot_console/app/entity/luckyReward/DrawResultEntity.php) **数值不变**
### 2. `lucky_reward` 池 `config` 结构SSOT
**禁止** config 内写 `prizeType`(类型只看外层列)。按 `prize_type` 校验必填/可选字段admin Validate + 文档):
**各类型共有可选字段**(参与抽奖的类型 1/2/3 常用):
| config 字段 | 说明 |
| --- | --- |
| `unlockInviteCount` | 开放命中所需有效邀请人数,默认 0无需 |
**各类型专有字段**
| prize_type | 专有 config 字段 | 说明 |
| --- | --- | --- |
| 1 随机金额 | `amountMinQf`, `amountMaxQf` | 千分位区间min ≤ max |
| 2 1x Spin | — | 可无专有字段 |
| 3 Cash Out | — | 可无专有字段;开放条件用 `unlockInviteCount` |
| 4 Jackpot | `displayAmountQf` | 展示用金额(千分位) |
示例:
```json
// prize_type = 1需 3 个有效邀请后才开放
{ "unlockInviteCount": 3, "amountMinQf": 100, "amountMaxQf": 500 }
// prize_type = 3Cash Out 需 5 个邀请
{ "unlockInviteCount": 5 }
// prize_type = 4
{ "displayAmountQf": 6666000 }
```
### 3. 外层列(不进 config
仅保留奖池项**通用**列,不含 lucky_reward 业务参数:
| 字段 | 说明 |
| --- | --- |
| `weight` | 抽取权重 |
| `display_text` / `icon_url` / `sort` | 前端展示与排序 |
`reward_pool_item` 建议 DDL 片段(相对初版变更点):
```sql
`prize_type` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '奖项类型lucky_reward池见 PRIZE_TYPE_* 常量',
`config` JSON NOT NULL COMMENT 'pool 业务参数lucky_reward见文档 SSOT',
-- 删除 reward_type不新增 unlock_invite_count 等 pool 专用列
```
### 4. 全平台单池
所有 source 共用 `pool_code = lucky_reward` 一套转盘格子和权重;[`LuckyRewardConfigService::listEnabledPrizeConfigs($configId)`](slot_console/app/service/luckyReward/LuckyRewardConfigService.php) 改为按 `pool_code` 加载,**不再依赖** `lucky_reward_config.id`
活动 per-source 配置(开关、周期、分层开宝箱、目标金额)不变。
### 5. DDL 其它细节
| 项 | 建议 |
| --- | --- |
| 主键 | 确认 ID 策略:若无雪花发号,补 AUTO_INCREMENT |
| 时间字段 | Model 层映射 `created_at`/`updated_at` |
| 库 | **s_common** |
| 索引 | item 表增加 `KEY idx_pool_code_status (pool_code, status)``KEY idx_prize_type (prize_type)` |
### 6. 常量约定
```php
/** Lucky Rewards 大转盘手动 Spin 奖池编码 */
public const POOL_CODE_LUCKY_REWARD = 'lucky_reward';
```
`prize_type` 常量(中文注释必填)与现网 1/2/3/4 对齐。
---
## 迁移与实现步骤
### Phase 1 — 表结构与数据迁移
1.`s_common` 执行 `reward_pool``reward_pool_item` DDL`prize_type` + `config`,无 `reward_type`、无 `unlock_invite_count` 列)
2. Seed`pool_code = lucky_reward``pool_name = Lucky Rewards Wheel``status = 1`
3. 迁移 `lucky_reward_prize_config``reward_pool_item`
- `prize_type` ← 旧 `prize_type`(直迁)
- `weight``sort``status` 直迁
- `config`:写入 `unlockInviteCount`(← 旧 `unlock_invite_count`+ 类型专有字段amountMinQf/MaxQf 或 displayAmountQf
- `item_code`:建议 `lr_prize_{old_id}`
4. 验证通过后 drop 旧表(或先 rename `_bak`
### Phase 2 — slot_console 读新池抽奖
| 文件 | 变更 |
| --- | --- |
| 新建 `RewardPoolModel` / `RewardPoolItemModel` | `listEnabledByPoolCode()` |
| [`LuckyRewardConfigService`](slot_console/app/service/luckyReward/LuckyRewardConfigService.php) | `listLuckyRewardPoolItems()`,固定 `POOL_CODE_LUCKY_REWARD` |
| [`LuckyRewardDrawService`](slot_console/app/service/luckyReward/LuckyRewardDrawService.php) | 读列 `prize_type`;过滤读 `config.unlockInviteCount`(缺省 0随机金额读 `config.amountMinQf/MaxQf`Jackpot(4) 仍排除 |
| [`DrawResultEntity`](slot_console/app/entity/luckyReward/DrawResultEntity.php) | 增加 `poolItemId``prizeType` 来自 item 的 `prize_type` 列 |
| [`LuckyRewardLogic::executeManualSpin`](slot_console/app/api/logic/LuckyRewardLogic.php) | 切换数据源spin_record.prize_type 仍写 Draw 结果 |
**行为不变**:随机封顶、+1 Spin、Cash Out 改状态、spin_record 写入。
### Phase 3 — slot_admin 后台替换 CRUD
| 现状 | 目标 |
| --- | --- |
| [`LuckyRewardPrizeConfigController`](backend/slot_admin/app/game/controller/LuckyRewardPrizeConfigController.php) 等 | Lucky Reward 奖池项管理scoped `pool_code=lucky_reward` |
| Validate | 校验外层 `prize_type`、按类型校验 config`unlockInviteCount`、金额区间)、权重 |
| [`menu-lucky-rewards.sql`](backend/slot_admin/db/menu-lucky-rewards.sql) | 路由指向新 Controller |
表单:美元展示 → 写入 config 内 `_qf``unlockInviteCount` 表单项写入 config`prize_type` 下拉与现网四类一致。
### Phase 4 — 文档、测试、清理
- 更新 [`02_管理后台方案.md`](docs/requirements/lucky_rewards/02_管理后台方案.md)`reward_pool_item.prize_type` 枚举 + config 结构 SSOT
- 更新 [`LuckyRewardDrawServiceTest`](slot_console/tests/Unit/LuckyRewardDrawServiceTest.php) fixturepool item 含 `prize_type` 列 + 精简 config
- 删除 `LuckyRewardPrizeConfigModel` 及旧表引用
---
## 风险与规避
| 风险 | 规避 |
| --- | --- |
| 运营迁移窗口改旧表 | 迁移前冻结编辑;脚本完成后立即切读新表 |
| Jackpot 无 C 端拉取 | 后台仍配 prize_type=4后续如需动态转盘再补接口 |
| 其它 pool_code 的 prize_type 语义冲突 | 按 pool 文档约定枚举;通用表结构复用,语义由 pool_code + 文档定义 |
| config 误写 prizeType | Validate 拒绝;迁移脚本不写入 prizeType |
| 业务字段外置导致表膨胀 | 约定:除 `prize_type` 外 lucky_reward 参数一律 config其它 pool 同理 |
---
## 总体评价
- **赞成** `pool_code = lucky_reward` 作为大转盘唯一奖池编码。
- **采纳**:类型语义 **外层 `prize_type`****`unlockInviteCount` 等业务参数进 config**,与通用奖池表设计一致。
- 其它活动复用 `reward_pool_item` 时,同一 `prize_type` 数值在不同 pool 可代表不同含义,需在各自 pool 文档中定义lucky_reward 池先用 1/2/3/4 四态)。
确认本计划后,按 Phase 1→4 落地。