ok
This commit is contained in:
165
plans/stats_init_重复分析_3fc8ad4b.plan.md
Normal file
165
plans/stats_init_重复分析_3fc8ad4b.plan.md
Normal file
@@ -0,0 +1,165 @@
|
||||
---
|
||||
name: Stats init 重复分析
|
||||
overview: 已用生产数据确认:auto_first 以 spin_source=1、prize_type=1、prize_amount_qf=init 落库,当前 statistics 会把同一笔开宝箱金额算两遍。修复方向是在 aggregateSpinStats 排除 spin_source=AUTO_FIRST,保留 init + 手动 Spin 随机入账相加。
|
||||
todos:
|
||||
- id: verify-auto-first-row
|
||||
content: 确认 slot_console 开宝箱 auto_first 记录的 prize_type / spin_source / prize_amount_qf 落库值
|
||||
status: completed
|
||||
- id: exclude-auto-first-stats
|
||||
content: aggregateSpinStats 排除 spin_source=AUTO_FIRST,并补 LuckyRewardSpinRecordModel 常量
|
||||
status: completed
|
||||
- id: add-stats-test
|
||||
content: 补统计用例:仅开宝箱无手动 Spin 时 total_reward_amount_qf=init 总和,且 spin_total/random_hit_count=0
|
||||
status: completed
|
||||
isProject: false
|
||||
---
|
||||
|
||||
# LuckyRewardStatsLogic 统计口径分析
|
||||
|
||||
## 直接回答
|
||||
|
||||
**不是。** `$spinStats` 里**没有**完整包含 `$initAmountTotalQf`,两者是**刻意分开**再相加的:
|
||||
|
||||
```php
|
||||
// backend/slot_admin/app/game/logic/LuckyRewardStatsLogic.php
|
||||
$initAmountTotalQf = (int) (clone $playerQuery)->sum('p.init_amount_qf');
|
||||
$spinStats = $this->aggregateSpinStats($playerQuery);
|
||||
$prizeAmountTotalQf = (int) ($spinStats['prize_amount_total_qf'] ?? 0);
|
||||
$totalRewardAmountQf = $initAmountTotalQf + $prizeAmountTotalQf;
|
||||
```
|
||||
|
||||
`$spinStats` 返回的是 **Spin 记录维度** 的聚合,与 player 表上的 `init_amount_qf` 来源不同:
|
||||
|
||||
| 字段 | 数据来源 | 含义 |
|
||||
|------|----------|------|
|
||||
| `$initAmountTotalQf` | `lucky_reward_player.init_amount_qf` | 开宝箱命中初始金额汇总 |
|
||||
| `$spinStats['prize_amount_total_qf']` | `lucky_reward_spin_record.prize_amount_qf` 且 `prize_type=1` | 随机金额类 Spin **实际入账**汇总 |
|
||||
| `$spinStats['spin_total']` 等 | `lucky_reward_spin_record` 行数 | 命中次数/比率,**不是金额** |
|
||||
|
||||
需求文档口径([02_管理后台方案.md](docs/requirements/lucky_rewards/02_管理后台方案.md) §7.2):
|
||||
|
||||
> **总奖励金额** = `sum(player.init_amount_qf)` + `sum(spin_record.prize_amount_qf)`(开宝箱初始 + Spin 随机入账 = 实际投放)
|
||||
|
||||
因此 **`$initAmountTotalQf` 不能删**;`total_reward_amount_qf` 本应是「init + **手动** Spin 随机入账」,不应把 auto_first 展示记录算进 prize 部分。
|
||||
|
||||
---
|
||||
|
||||
## 生产数据已确认重复计数
|
||||
|
||||
用户提供的真实行:
|
||||
|
||||
```sql
|
||||
INSERT INTO lucky_reward_spin_record
|
||||
(uid, cycle_id, spin_id, spin_source, prize_type, prize_amount_qf, my_amount_after_qf)
|
||||
VALUES
|
||||
(1545906, 46, 'auto_first:1545906:46', 1, 1, 9603, 9603);
|
||||
```
|
||||
|
||||
字段解读(与 DDL 一致):
|
||||
|
||||
| 字段 | 值 | 含义 |
|
||||
|------|-----|------|
|
||||
| `spin_id` | `auto_first:1545906:46` | 开宝箱自动首次 Spin(Record 展示) |
|
||||
| `spin_source` | `1` | 自动首次 |
|
||||
| `prize_type` | `1` | 随机金额 |
|
||||
| `prize_amount_qf` | `9603` | 与 `player.init_amount_qf` 相同 |
|
||||
|
||||
对 uid=1545906、cycle=46 若仅开宝箱、无手动 Spin:
|
||||
|
||||
- `$initAmountTotalQf` = **9603**(来自 player)
|
||||
- `$spinStats['prize_amount_total_qf']` = **9603**(auto_first 被 `prize_type=1` 命中)
|
||||
- `$totalRewardAmountQf` = **19206**(应为 **9603**)
|
||||
|
||||
同时 **`spin_total` / `random_hit_count`** 也会把 auto_first 算进 Spin 次数与命中率,导致指标失真(每个参与用户天然 +1 次 Spin、+1 次 random 命中)。
|
||||
|
||||
---
|
||||
|
||||
## `spin_total` 同样算错
|
||||
|
||||
**是的。** `$spinStats['spin_total']` 对 `lucky_reward_spin_record` **全表 count**,没有任何 `spin_source` 过滤:
|
||||
|
||||
```php
|
||||
$spinTotal = (int) (clone $spinQuery)->count(); // 含 auto_first
|
||||
```
|
||||
|
||||
对你给的 uid `1545906`:只开宝箱、从未手动 Spin 时:
|
||||
|
||||
| 指标 | 当前错误值 | 修正后应为 |
|
||||
|------|------------|------------|
|
||||
| `spin_total` | **1**(auto_first) | **0** |
|
||||
| `random_hit_count` | **1**(auto_first 且 prize_type=1) | **0** |
|
||||
| `random_hit_rate` | **100%** | **0%** |
|
||||
|
||||
需求文档 [02_管理后台方案.md](docs/requirements/lucky_rewards/02_管理后台方案.md) §7.3 明细列 **Spin** 用的是 `player.spin_used`,注释为 **「不含初始自动」**;总览 `spin_total` 应与之一致,只统计**真实消耗 Spin 次数**的记录(`spin_source` 为每日/邀请/手动),不应含开宝箱 auto_first。
|
||||
|
||||
连带影响的字段(同一 `$spinQuery` 未过滤):
|
||||
|
||||
- `spin_total` — 分母偏大
|
||||
- `random_hit_count` / `random_hit_rate` — 分子、分母都含 auto_first
|
||||
- `cashout_hit_count` / `cashout_hit_rate` — 若 auto_first 不会 prize_type=3,通常不受影响,但仍应统一排除
|
||||
- `spin_prize_hit_count` / `spin_prize_hit_rate` — 同上
|
||||
- `prize_amount_total_qf` — 金额重复(见上)
|
||||
|
||||
修复时在 `$spinQuery` 基查询一次性加 `spin_source <> AUTO_FIRST`,上述字段一并修正。
|
||||
|
||||
---
|
||||
|
||||
## 根因
|
||||
|
||||
[`aggregateSpinStats()`](backend/slot_admin/app/game/logic/LuckyRewardStatsLogic.php) 只按 `prize_type=1` 过滤金额/次数,**未排除 `spin_source=1`(auto_first)**:
|
||||
|
||||
```php
|
||||
$prizeAmountTotalQf = (int) (clone $spinQuery)
|
||||
->where('sr.prize_type', LuckyRewardSpinRecordModel::PRIZE_TYPE_RANDOM)
|
||||
->sum('sr.prize_amount_qf');
|
||||
```
|
||||
|
||||
C 端开宝箱写 auto_first 时为了 Record 展示使用了 `prize_type=1` + `prize_amount_qf=init`,与 My Amount 入账无关;admin 统计误将其当作「Spin 随机入账」。
|
||||
|
||||
需求 [04_slot_console_活动主流程方案.md](docs/requirements/lucky_rewards/04_slot_console_活动主流程方案.md):`auto_first` **仅动画/Record,不叠加金额**。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
openBox[openBox 开宝箱]
|
||||
playerInit["player.init_amount_qf = 9603"]
|
||||
autoFirst["spin_record auto_first prize_amount_qf = 9603"]
|
||||
statsInit["initAmountTotalQf += 9603"]
|
||||
statsPrize["prize_amount_total_qf += 9603"]
|
||||
doubleCount["total_reward = 19206 重复"]
|
||||
openBox --> playerInit
|
||||
openBox --> autoFirst
|
||||
playerInit --> statsInit
|
||||
autoFirst --> statsPrize
|
||||
statsInit --> doubleCount
|
||||
statsPrize --> doubleCount
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 修复方案(待执行)
|
||||
|
||||
在 [`aggregateSpinStats()`](backend/slot_admin/app/game/logic/LuckyRewardStatsLogic.php) 中,**所有** spin 记录聚合(次数与金额)统一排除 auto_first:
|
||||
|
||||
1. [LuckyRewardSpinRecordModel](backend/slot_admin/app/game/model/common/LuckyRewardSpinRecordModel.php) 增加常量:
|
||||
- `SPIN_SOURCE_AUTO_FIRST = 1`
|
||||
- `SPIN_SOURCE_DAILY = 2`
|
||||
- `SPIN_SOURCE_INVITE = 3`(与 DDL 注释对齐)
|
||||
2. `$spinQuery` 基查询增加:`->where('sr.spin_source', '<>', SPIN_SOURCE_AUTO_FIRST)`
|
||||
- 影响:`spin_total`、`random_hit_count`、`cashout_hit_count`、`spin_prize_hit_count`、`prize_amount_total_qf`
|
||||
3. **保持** `$totalRewardAmountQf = $initAmountTotalQf + $prizeAmountTotalQf` 不变
|
||||
4. 补测试:仅开宝箱用户 → `total_reward_amount_qf === sum(init_amount_qf)`;auto_first 行存在但不计入 prize
|
||||
|
||||
**不改 C 端落库**:auto_first 继续用于 Record 展示;仅修正 admin 统计过滤。
|
||||
|
||||
**不建议**删掉 `$initAmountTotalQf` 或改为只 sum spin_record。
|
||||
|
||||
---
|
||||
|
||||
## 小结
|
||||
|
||||
| 问题 | 答案 |
|
||||
|------|------|
|
||||
| `$spinStats` 是否已包含 `$initAmountTotalQf`? | **否**;但 `prize_amount_total_qf` 对 auto_first **重复计入了同一份 init** |
|
||||
| 两处相加是否合理? | **公式合理**;当前实现因未排除 auto_first **结果错误** |
|
||||
| 生产数据是否证实? | **是**(uid 1545906 示例:9603 被算成 19206) |
|
||||
| `spin_total` 是否也算错? | **是**;仅开宝箱用户会显示 spin_total=1、random_hit_rate=100% |
|
||||
Reference in New Issue
Block a user