Files
cursor/plans/每日返水需求文档_20a03147.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

20 KiB
Raw Permalink Blame History

name, overview, todos, isProject
name overview todos isProject
每日返水需求文档 在现有 user_profit_daily 日下注统计基础上,新增「每日返水」活动:累进档位计费、仅充值用户参与、次日 00:00 起可领且 24 小时内有效;配套用户端 API、后台档位配置与综合统计页。
id content status
schema 设计并评审 daily_rebate_record、daily_rebate_tier_config 表结构与索引 in_progress
id content status
calc-service 实现累进分段 RebateCalcService + tier_snapshot + 单元测试(含 $2000→$50 等用例) pending
id content status
cron slot_consoledailyRebateSettle / dailyRebateExpire 定时任务 pending
id content status
user-api slot_console 用户端 info/claim API + slot_wallet 入账与新 transaction type pending
id content status
admin-tier slot_admin 每日返水档位 CRUD 页 pending
id content status
admin-stats slot_admin 综合统计/每日返水统计(列表+筛选+排序+汇总,参考 FreeCreditsStats pending
id content status
pwa-ui 前端三态 UI、7 日表、倒计时、领取按钮 pending
false

每日返水Daily Rebate 3%)需求规格书

1. 背景与目标

  • 业务目标:对已充值用户,按自然日累计有效下注给予累进比例返水,提升留存与投注激励。
  • 数据基础[slot_console](slot_console) 已通过 [LoseReturnDeposit](slot_console/app/command/LoseReturnDeposit.php) + Redis [UserProfitService](slot_pwa/app/service/user/UserProfitService.php) 维护 [s_statistics.user_profit_daily](slot_console/app/model/statistics/UserProfitDaily.php)(字段 bet/win/profit/create_date/source)。
  • 与 VIP 亏损返利的区别VIP 返利基于「当日净亏损」且需 VIP 等级;本活动基于「当日总下注额」、仅需历史充值、档位为累进分段,产品独立。

2. 术语

术语 定义
自然日 服务器时区 Asia/Shanghai(与 [slot_center/config/app.php](slot_center/config/app.php) 一致)的 00:00:0023:59:59
有效下注 等于 user_profit_daily.bet(厘),与 PWA 下注时 incBet 累计值一致;无单独过滤规则
返水金额 按档位配置对当日有效下注做累进分段计算后的金额(厘)
统计日期 发生下注的自然日 create_date,非领取日
充值用户 结算时刻钱包 total_deposit > 0[WalletStatModel](slot_wallet/app/model/multi/WalletStatModel.php)

3. 核心业务规则

3.1 参与资格

  • 未充值不参与返水前端展示「锁定」态Unlock Cashback不可领、列表可置灰或仅展示引导文案。
  • 已充值:自充值成功当日起,当日及之后有有效下注的自然日可产生返水记录;历史未充值日的下注不补发。

3.2 返水计算(累进分段,非整笔单一比例)

已确认:采用分段累进,与原型及后台示例一致($2000 → $50

默认档位(首版种子数据,后台可配置

分段序号 下注区间(含边界,美元展示) 比例
1 $0 $1,000 3%
2 $1,001 $3,000 2%
3 $3,001 $5,000 1%
4 $5,001 及以上 0.5%

计算公式B = 当日有效下注,美元;存储与计算用厘,1 USD = 1000 厘,与 [share_config.moneyFormat](slot_center/config/share_config.php) 一致):

rebate_li = Σ segment_i( min(B, max_i) - min(B, min_i) + ε_i ) × rate_i
  • 各档 min_i/max_i 以后台配置为准(最后一档 max 为空表示无上限)。
  • 分段左闭右闭:第 1 档覆盖 [0, 1000],第 2 档覆盖 (1000, 3000],依此类推(实现时用「上一档上限 + 1」作为下一档起点避免重复计费
  • 舍入:返水金额入库前 floor 到厘;前端展示保留 2 位小数美元。

计算示例

有效下注 计算过程 返水
$125.56 125.56 × 3% $3.77
$200 200 × 3% $6.00
$2,000 1000×3% + 1000×2% $50.00
$6,000 1000×3% + 2000×2% + 2000×1% + 1000×0.5% $95.00

待定态(当天):用 Redis 当日 bet 实时重算返水预览;不入库终态金额,或与 DB 行 status=pending 同步更新。

3.3 结算与领取时间轴

sequenceDiagram
    participant User
    participant PWA as slot_pwa
    participant Redis
    participant Cron as slot_console_cron
    participant DB as daily_rebate_record

  Note over User,Redis: D日 00:00-23:59
    User->>PWA: 下注
    PWA->>Redis: incBet
    Note over DB: status=pending 实时 bet/rebate

  Note over Cron,DB: D+1日 00:00后 cron
    Cron->>Redis: 读取 D日 bet
    Cron->>DB: 写入/更新 settle: claimable, dead_time=D+2 00:00

  Note over User,DB: D+1日 仅可领 D日 返水
    User->>PWA: claim(stat_date=D)
    PWA->>DB: status=claimed

  Note over DB: 超过 dead_time 未领
    Cron->>DB: status=expired
时点 行为
D 日进行中 状态 待定;有效下注、返水金额随 Redis 刷新
D+1 日 00:00 定时任务结算 D 日:有 bet>0 且已充值 → 生成/更新记录,状态 待领取claimable_at = D+1 00:00:00expire_at = D+2 00:00:00(领取窗口 24 小时,对齐 VIP [vip_rebate_lose_time](slot_center/config/share_config.php)
D+1 日全天 用户只能领取 stat_date=D 的返水(「前一天」)
D+2 日 00:00 前未领 状态 过期rebate_amount>0 且未领
领取成功 状态 已领;记 claimed_at;钱包入账

无有效下注:不生成记录(后台列表、前端 7 日表均不展示该日)。

返水为 0:若档位计算为 0bet=0 已排除),bet>0 但舍入为 0 时可不落库或落库且不可领——建议 不落库,与「未有有效下注不显示」一致。

3.4 状态机

状态码 中文 条件
pending 待定 stat_date = 今天;实时统计未结束
claimable 待领取 stat_date < 今天rebate_amount > 0claimed_at 空;now < expire_at
claimed 已领 claimed_at 非空
expired 过期 rebate_amount > 0;未领;now >= expire_at

状态迁移:

  • pendingclaimable:日切结算任务(仅昨日及更早批量处理;今日保持 pending
  • claimableclaimed:用户领取接口(幂等)
  • claimableexpired:过期扫描任务或领取时校验

前端映射(英文 UI

状态 展示 行样式
pending Pending 绿色标签(进行中)
claimable Claimable 绿色可点
claimed Claimed 白色/灰色
expired Expired 红色/灰色
无记录 No Bets 灰字(仅前端 7 日占位,无 DB 行)

3.5 领取规则

  • 每次领取一条指定 stat_date(通常为昨日)。
  • 并发DB 行级锁 / UPDATE ... WHERE status=claimable 防重复领。
  • 入账:新增钱包流水类型(建议 TRANSACTION_TYPE_DAILY_REBATE = 65,在 [Consts.php](slot_lib/src/common/const/Consts.php) 登记);更新 total_cashback
  • 失败:事务回滚,状态不变。

4. 数据设计

4.1 沿用表

  • **s_statistics.user_profit_daily**:只读来源,提供 betsourcecreate_date不扩展状态/返水字段。

4.2 新建表 daily_rebate_record(建议库:s_common

字段 类型 说明
id bigint PK
uid bigint 用户 ID
source varchar 渠道号(结算时快照)
stat_date date 统计日期(下注日)
bet_amount bigint 有效下注(厘)
rebate_amount bigint 返水金额(厘)
status tinyint 1 pending 2 claimable 3 claimed 4 expired
tier_snapshot json 结算时档位快照(审计)
claimable_at datetime 可领取开始
expire_at datetime 过期时间
claimed_at datetime nullable 领取时间
created_at / updated_at datetime

唯一索引(uid, stat_date)

索引(stat_date, status)(source, stat_date)(uid)

4.3 新建表 daily_rebate_tier_config(后台可配置)

字段 说明
id PK
sort 排序(从小到大)
min_bet 区间下限(厘,含)
max_bet 区间上限NULL=无上限)
rate_percent 比例,如 3.00 表示 3%
status 启用/停用
updated_by / updated_at 审计

校验:档位连续无空洞、无重叠;至少一档;最后一档可无 max_bet


5. 定时任务slot_console

任务 触发 职责
dailyRebateSettle 每日 00:05可配置 结算 昨日 user_profit_daily + 充值校验 → upsert daily_rebate_record 为 claimable
dailyRebateExpire 每小时或 00:10 claimablenow>=expire_at 置为 expired
(可选)与现有 [loseReturnDeposit](slot_console/app/command/LoseReturnDeposit.php) 同批读 Redis 避免重复扫 Redis推荐独立命令以免耦合 VIP 逻辑

待定实时用户打开活动页时Logic 读 Redis user:profit:{Ymd} + 当前档位配置计算预览,不写 claimable。


6. 用户端PWA / Gateway

6.1 页面态(对齐原型)

条件 主按钮 表格
锁定 total_deposit==0 Unlock Cashback 模糊/遮罩
已解锁 已充值,无可领 Play Now 近 7 日明细
可领取 存在 claimablestat_date=昨天 Claim($X.XX) 昨日行 Claimable

倒计时「THIS ROUND」= 距今日自然日结束的秒数(与 Rates reset 00:00 文案一致)。

6.2 API建议落在 slot_console /api/daily-rebate/*,经 gateway 转发)

GET /api/daily-rebate/info

响应示例字段:

{
  "unlocked": true,
  "countdown_seconds": 86399,
  "claimable": { "stat_date": "2026-05-25", "rebate_amount": 3000, "display": "3.00" },
  "tiers": [{ "min": 0, "max": 1000000, "rate": 3 }],
  "records": [
    { "stat_date": "05/26", "bet_amount": 125560, "rebate_amount": 3770, "status": "pending" }
  ]
}
  • records:最近 7 个自然日,有 bet 或 DB 行的日期;无下注日可不返回或前端填 No Bets。
  • 打开弹窗时 强制刷新 当日 pending 数据。

POST /api/daily-rebate/claim

  • 入参:stat_date(可选,默认昨天)
  • 校验已充值、status=claimable、未过期、rebate>0
  • 出参:领取后余额/流水号

GET /api/daily-rebate/tiers可选info 已含则省略)


7. 管理后台slot_admin + slot_admin_vue

7.1 菜单

  • 活动配置 / 每日返水档位新建CRUD [daily_rebate_tier_config],校验区间连续。
  • 综合统计 / 每日返水统计(新建):列表 + 汇总。

实现参考:列表+汇总 [FreeCreditsStats](backend/slot_admin/app/game/logic/FreeCreditsStatsLogic.php) + 前端 [freeCreditsStats/index.vue](backend/slot_admin_vue/src/views/game/freeCreditsStats/index.vue)

7.2 统计列表

ID(uid) | 渠道号 | 有效下注额⇅ | 返水金额⇅ | 状态 | 统计日期

筛选

  • UID精确
  • 渠道(source,可搜索下拉,复用 commonStore.allSourcesOptionsNoAll
  • 状态:待定 / 待领取 / 已领 / 过期(多选)
  • 统计日期范围(stat_date[]

排序白名单bet_amountrebate_amount(默认 stat_date desc, id desc

数据范围:仅 total_deposit>0 用户在结算时已入库的记录;待定行可对「今天」合并 Redis 实时 bet与 Free Credits「打开刷新」一致

7.3 顶部汇总(随筛选变化)

指标 计算
总领取 status=claimedsum(rebate_amount)
总待领取 status=claimable 的 sum
总过期 status=expired 的 sum
待定 status=pending 的 sum若筛选日期范围不含今天则为 0
领取率 总领取 / (总领取 + 总过期 + 总待领取);分母为 0 时显示 0%-

展示格式:$888.88rebate_amount / 10002 位小数)。


8. 非功能需求

  • 幂等:结算任务对 (uid, stat_date) upsert领取 CAS 更新状态。
  • 性能:日活结算批量按日期分页;后台列表分页默认 100最大 100。
  • 审计tier_snapshot 保留结算时档位;领取写 wallet log biz_extra: {activity:"daily_rebate", stat_date}
  • 监控:结算失败/领取失败打日志 + 指标;过期数量日报。

9. 边界与异常

场景 处理
结算日用户刚充值、昨日下注 昨日结算时已按当时 total_deposit 判断;若需「充值后立即对历史补发」→ 不做,以结算时刻为准
结算任务重复跑 upsert不重复入账
时区变更 禁止随意改;变更需重算规则文档化
bet>0 未充值 不生成记录
领取时档位已改 tier_snapshot 为准,不受后续配置变更影响
跨天未关弹窗 前端倒计时结束刷新 infopending→claimable 由后端状态驱动

10. 实施拆分(供研发排期)

flowchart LR
  subgraph phase1 [Phase1 数据与配置]
    T1[daily_rebate_tier_config]
    T2[daily_rebate_record]
    T3[admin 档位 CRUD]
  end
  subgraph phase2 [Phase2 结算与过期]
    C1[dailyRebateSettle]
    C2[dailyRebateExpire]
    L1[rebate calc service]
  end
  subgraph phase3 [Phase3 用户端]
    A1[info API]
    A2[claim API + wallet]
    F1[PWA 三态 UI]
  end
  subgraph phase4 [Phase4 后台统计]
    B1[DailyRebateStats Logic]
    B2[admin vue 列表汇总]
  end
  phase1 --> phase2 --> phase3
  phase2 --> phase4
仓库 改动要点
slot_console Model、结算/过期 Command、RebateCalcService、用户 API、Redis 读当日 bet
slot_wallet 新 transaction type、领取入账
slot_lib Consts 新类型
backend/slot_admin Controller/Logic/Validate、菜单 SQL
backend/slot_admin_vue 档位页 + 统计页
slot_sdk 若 gateway 经 sdk 调 console补 Client 方法

不建议复用 vip_rebate_record 表:业务语义、状态机、计费基础均不同。


11. 验收标准(摘要)

  1. 充值用户 D 日下注 $2000D+1 00:00 后显示待领取 $5024h 内领取成功,流水类型正确。
  2. 未充值用户仅见锁定态,无领取接口成功路径。
  3. 超 24h 未领变过期,不可再领。
  4. 后台筛选/status/排序/汇总与文档公式一致;改档位仅影响新结算日。
  5. 前端 7 日表、Claim 按钮金额与后台一致;当日为 Pending 且随下注刷新。

12. 已确认决策

  • 计费方式:累进分段(非整笔落档单一比例)。
  • 有效下注:等同 user_profit_daily.bet
  • 档位:后台可配置daily_rebate_tier_config)。