8.9 KiB
8.9 KiB
月返水(Monthly Cashback)实现方案
与周返水完全同构,仅「周」改「月」。需求见 月返水.md;结构镜像 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取整到厘。
数据流
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:
ACTIVITY_TYPE_MONTHLY_REBATE=14、TRANSACTION_TYPE_MONTHLY_REBATE=69(若周返水未先上,取下一空位即可)。
slot_console:
- 模型
app/model/common/MonthlyRebateRecordModel.php(四态 +findByUidAndMonth)。 - 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、Validatorapp/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 对
TYPE_MONTHLY_REBATE调MonthlyRebateConfigService::canonicalExtConfigForStorage。 - 部署文档
doc/monthly_rebate_deploy.md。
backend/slot_admin(后台统计,镜像 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精确、statuswhereIn、时间范围 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,component=game/monthlyRebateStats/index+sm_system_group_menu授权)。
backend/slot_admin_vue(镜像 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)
{
"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-reportSkill 跑verify-slot-backend.sh+ dockerphp -l,回复附「检测结果」章节(含 PHPDoc:checked)。 [ {"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 输出检测结果章节"} ]