ok
This commit is contained in:
119
plans/monthly cashback-215d598c.plan.md
Normal file
119
plans/monthly cashback-215d598c.plan.md
Normal file
@@ -0,0 +1,119 @@
|
||||
<!-- 215d598c-9837-4e65-a3e9-850d78cd5f00 -->
|
||||
## 月返水(Monthly Cashback)实现方案
|
||||
|
||||
与周返水完全同构,仅「周」改「月」。需求见 [月返水.md](slot_console/doc/月返水.md);结构镜像 [DailyRebateLogic.php](slot_console/app/api/logic/DailyRebateLogic.php) 与周返水两份计划(核心 + 统计)。
|
||||
|
||||
### 需要改动的服务
|
||||
- slot_lib:常量。
|
||||
- slot_console:表 / 模型 / 服务 / Logic / Controller / Validator / 命令 / 路由 / 活动配置入口 / 部署文档。
|
||||
- backend/slot_admin(+ vue):后台「每月返水统计」页。
|
||||
- slot_wallet:仅确认流水类型 69 打码归【活动赠送 Y3】。
|
||||
- slot_pwa:无需改动(复用 `rebate_bet`)。
|
||||
|
||||
### 周期与口径(与周返水差异)
|
||||
- 统计月:每月 1 日 00:00:00 ~ 月末 23:59:59,服务器时区。
|
||||
- `month_start = date('Y-m-01')`;上月 = `first day of last month`;上上月 = `first day of 2 months ago`;过期时间 = 下月 1 日。
|
||||
- 月有效投注 = 当月每日 `rebate_bet` 之和(仅充值用户,沿用 pwa 现有口径)。
|
||||
- VIP 取月末最后一刻(结算下月 1 日读当前 VIP);`floor` 取整到厘。
|
||||
|
||||
### 数据流
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
pwa["slot_pwa 已累加 rebate_bet"] --> redis["redis user:profit:Ymd TTL7天"]
|
||||
accrue["monthlyRebateAccrue 每天00:05 处理昨日"] --> redis
|
||||
accrue --> rec["monthly_rebate_record 当月Pending bet累加 + vip/rate快照"]
|
||||
settle["monthlyRebateSettle 每月1日00:15"] --> rec
|
||||
settle --> vip["读当前VIP rate=vip_rates level"]
|
||||
settle --> expirePrev["上上月 Claimable -> Expired"]
|
||||
claim["C端 claim 上月"] --> wallet["WalletService gift 类型69 打码Y3"]
|
||||
admin["后台 每月返水统计"] --> rec
|
||||
```
|
||||
|
||||
### 落库方案 B(幂等)
|
||||
- `monthly_rebate_record` 含 `last_accrued_date`(date)。
|
||||
- `monthlyRebateAccrue`(每天)处理**昨日**(key 仅 1 天,在 TTL 内):遍历昨日 profit 哈希,充值用户且 `rebate_bet>0` → 定位昨日所属月记录(无则建 Pending)→ 若 `last_accrued_date < 昨日` 则 `bet_amount += rebate_bet` 且更新 `last_accrued_date`;同时写入当前 `vip_level`/`rate_snapshot`(供后台对待定行展示费率)。
|
||||
- C 端 This Month = `record.bet_amount` + 今日 redis 实时 `rebate_bet`。
|
||||
|
||||
### 结算/过期(每月 1 日)
|
||||
- `monthlyRebateSettle`:上月 Pending → 读当前 VIP → `rebate=floor(bet*rate/100)`;`rebate>0` 置 Claimable,`claimable_at=本月1日`、`expire_at=下月1日`;同次将上上月 `Claimable 且 expire_at<=now` 置 Expired。
|
||||
- `monthlyRebateExpire`:兜底,`Claimable 且 expire_at<=now -> Expired`。
|
||||
- 顺序:accrue 先于 settle。
|
||||
|
||||
### 复用 vs 新建(计费 = VIP 费率单乘,不复用 tiers)
|
||||
- 复用骨架:领取短事务 CAS + 钱包入账失败回滚 + 结算结构化日志 + `RechargedUidSetService`/`UserProfitEntity`/四态 + `statusLabel`/Controller/Validator 形态。
|
||||
- 新建:`MonthlyRebateCalcService::calcRebateLi(betLi, ratePercent)=floor(betLi*ratePercent/100)`、`MonthlyRebateConfigService`(解析 `vip_rates`/`maxRate`/`rateForVip`/`canonicalExtConfigForStorage`)。
|
||||
|
||||
### 文件清单
|
||||
|
||||
slot_lib:
|
||||
- [Consts.php](slot_lib/src/common/const/Consts.php):`ACTIVITY_TYPE_MONTHLY_REBATE=14`、`TRANSACTION_TYPE_MONTHLY_REBATE=69`(若周返水未先上,取下一空位即可)。
|
||||
|
||||
slot_console:
|
||||
- 模型 `app/model/common/MonthlyRebateRecordModel.php`(四态 + `findByUidAndMonth`)。
|
||||
- [RechargeGiftConfigModel.php](slot_console/app/model/common/RechargeGiftConfigModel.php):`TYPE_MONTHLY_REBATE=14` + `getMonthlyRebateActiveInfo/...ByExactSource`(镜像每日返水方法)。
|
||||
- 服务 `app/service/MonthlyRebateCalcService.php`、`app/service/MonthlyRebateConfigService.php`。
|
||||
- Logic `app/api/logic/MonthlyRebateLogic.php`(info/claim/accrue/settle/expire)。
|
||||
- Controller `app/api/controller/MonthlyRebateController.php`、Validator `app/api/validator/MonthlyRebateValidator.php`(claim 场景,可选 month_start)。
|
||||
- 命令 `app/command/MonthlyRebateAccrue.php`、`MonthlyRebateSettle.php`、`MonthlyRebateExpire.php`。
|
||||
- 路由:注册 monthly-rebate info/claim。
|
||||
- SQL `db/monthly_rebate.sql`(含 `month_start date`、`last_accrued_date`、唯一键 `uniq_uid_month`、`vip_level`、`rate_snapshot`)。
|
||||
- 活动配置入口 [ActivityConfigEntity.php](slot_console/app/entity/activity/ActivityConfigEntity.php) 对 `TYPE_MONTHLY_REBATE` 调 `MonthlyRebateConfigService::canonicalExtConfigForStorage`。
|
||||
- 部署文档 `doc/monthly_rebate_deploy.md`。
|
||||
|
||||
backend/slot_admin(后台统计,镜像 [FreeCreditsStatsLogic.php](backend/slot_admin/app/game/logic/FreeCreditsStatsLogic.php)):
|
||||
- 模型 `app/game/model/common/MonthlyRebateRecordModel.php`(`connection='s_common'`、四态 + `statusLabel`)。
|
||||
- DTO `app/game/dto/MonthlyRebateStatsQueryDTO.php`、校验 `app/game/validate/MonthlyRebateStatsValidate.php`(`uid/source/status/stat_range[]/orderBy(in:bet_amount,rebate_amount)/orderType/page/limit`)。
|
||||
- 控制器 `app/game/controller/MonthlyRebateStatsController.php`(`index` 返回 list + otherData)。
|
||||
- Logic `app/game/logic/MonthlyRebateStatsLogic.php`:
|
||||
- `buildQuery`:`uid` 精确、`source` 精确、`status` whereIn、时间范围 snap 整月(`month_start >= 月初(start)` 且 `month_start <= 月初(end)`)、排序白名单默认 `month_start desc`。
|
||||
- `list`:行补 `rate(=rate_snapshot)`、`month_start/month_end`、`is_current_month`、`status_label`,金额 `getNumberFormat`。
|
||||
- `statistics`:总领取/总待领取/总过期/领取率(分母 0→0,2 位小数)/待定(仅当筛选含本月)。
|
||||
- 菜单 `db/menu-monthly-rebate-stats.sql`(挂「综合统计」父菜单,镜像 [menu-free-credits-stats.sql](backend/slot_admin/db/menu-free-credits-stats.sql),`component=game/monthlyRebateStats/index` + `sm_system_group_menu` 授权)。
|
||||
|
||||
backend/slot_admin_vue(镜像 [freeCreditsStats/index.vue](backend/slot_admin_vue/src/views/game/freeCreditsStats/index.vue)):
|
||||
- `src/api/game/monthlyRebateStats.js`、`src/views/game/monthlyRebateStats/index.vue`:筛选(ID/渠道/状态/按日范围)、`#tableAfterButtons` 顶部总计、可排序列(有效下注额/返水金额)、统计周期列显示「本月」或 `month_start - month_end`。
|
||||
|
||||
slot_wallet:
|
||||
- 确认流水类型 `69` 打码归类为 活动赠送 Y3(`SOURCE_TYPE_ACTIVITY_REWARD=3`),如有类型白名单补 69。
|
||||
|
||||
### ext_config 标准格式(type=14)
|
||||
```json
|
||||
{
|
||||
"vip_rates": [
|
||||
{"vip_level": 0, "rate_percent": 0},
|
||||
{"vip_level": 1, "rate_percent": 0.5},
|
||||
{"vip_level": 15, "rate_percent": 2.0}
|
||||
],
|
||||
"banner_image": ""
|
||||
}
|
||||
```
|
||||
|
||||
### cron
|
||||
- `monthlyRebateAccrue` 每天 00:05;`monthlyRebateSettle` 每月 1 日 00:15;`monthlyRebateExpire` 每月 1 日 00:20。
|
||||
|
||||
### 与周返水共用注意
|
||||
- 月返水与周返水各自独立表/配置/命令/流水类型,互不影响;同一用户同期可同时拥有日/周/月三套返水记录。
|
||||
- 若日/周/月三套都上,建议抽取「曾充值判断 + profit 哈希解码 + 月/周边界」公共片段,避免三处复制(可选优化,不阻塞)。
|
||||
|
||||
### 收尾门禁
|
||||
- 改 `app/**/*.php` 后按 `slot-backend-completion-report` Skill 跑 `verify-slot-backend.sh` + docker `php -l`,回复附「检测结果」章节(含 PHPDoc:checked)。
|
||||
</plan>
|
||||
<todos>
|
||||
[
|
||||
{"id": "consts", "content": "slot_lib Consts 新增 ACTIVITY_TYPE_MONTHLY_REBATE=14、TRANSACTION_TYPE_MONTHLY_REBATE=69"},
|
||||
{"id": "sql-model", "content": "monthly_rebate.sql + MonthlyRebateRecordModel(month_start/last_accrued_date/vip_level/rate_snapshot/四态/findByUidAndMonth)"},
|
||||
{"id": "config-model-methods", "content": "RechargeGiftConfigModel 加 TYPE_MONTHLY_REBATE 与 getMonthlyRebateActiveInfo 系列方法"},
|
||||
{"id": "services", "content": "MonthlyRebateCalcService(VIP费率单乘) 与 MonthlyRebateConfigService(vip_rates/maxRate/rateForVip/canonical)"},
|
||||
{"id": "logic", "content": "MonthlyRebateLogic:info/claim(CAS+钱包回滚)/accrue(写vip/rate快照)/settle(读VIP+过期上上月)/expire"},
|
||||
{"id": "controller-route", "content": "MonthlyRebateController + MonthlyRebateValidator + 路由注册"},
|
||||
{"id": "commands", "content": "命令 MonthlyRebateAccrue/Settle/Expire + cron"},
|
||||
{"id": "activity-entry", "content": "ActivityConfigEntity 读写 type=14 走 canonicalExtConfigForStorage"},
|
||||
{"id": "admin-stats", "content": "slot_admin 每月返水统计:Model(s_common)/DTO/Validate/Controller/Logic(buildQuery月snap+list+statistics)"},
|
||||
{"id": "admin-vue", "content": "slot_admin_vue monthlyRebateStats api.js + index.vue(筛选/总计/排序/统计周期)"},
|
||||
{"id": "admin-menu-sql", "content": "menu-monthly-rebate-stats.sql 挂综合统计父菜单 + 分组授权"},
|
||||
{"id": "wallet-verify", "content": "slot_wallet 确认流水类型69 打码归 活动赠送Y3"},
|
||||
{"id": "deploy-doc", "content": "编写 monthly_rebate_deploy.md"},
|
||||
{"id": "verify-gate", "content": "跑 verify-slot-backend.sh + php -l 输出检测结果章节"}
|
||||
]
|
||||
</todos>
|
||||
Reference in New Issue
Block a user