Files
thebet365/docs/结算与返水金额规则.md
Mars afb5c5437e feat: 充值订单审计与重新申请,优化赛事展示和余额刷新
- 新增 deposit_order_audit_logs 表,记录提交/审批/拒绝/撤销/重提全链路
- 管理端充值单页增加审计历史;玩家端充值历史支持时间线与重新申请
- 已拒绝订单可原单号重提;撤销入账使用 PLAYER_DEPOSIT_REVERSAL 并加强幂等
- 结算后清除热门标记,允许归档已结算赛事,完善今日赛事时区窗口
- 足球页今日/早盘独立折叠;资料与余额在进入钱包/个人页及下注后自动刷新
- 补充投注玩法、结算返水规则文档;新增 smoke/settlement CLI 脚本

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 14:52:05 +08:00

15 KiB
Raw Permalink Blame History

结算与返水金额规则

本文档为 Reference参考:只描述金额如何计算,不涉及管理端操作步骤。各玩法定义与判赢规则见 投注玩法说明.md;赛事结算流程见 settlement-and-fund-flow-analysis.md


1. 说明与术语

术语 含义
stake 投注本金(Bet.stake
odds 欧赔,含本金系数(BetSelection.odds 或 leg 快照)
payout 派彩总额,含本金(函数 calculatePayout / calculateParlayPayout 的返回值)
actualReturn 确认结算后写入注单的派彩,与 payout 一致
净盈亏 payout - stakeLOSE 时 payout=0净亏 = -stake
rate 返水比例,小数0.01 = 1%
lineAmount 单笔注单返水:stake × rate

精度:业务金额在库中为 Decimal(18,4);返水比例为 Decimal(8,4)。计算链使用 Prisma Decimal 全精度,无额外 roundAdmin UI 百分比输入另有 rate-percent.ts 转换,不影响 API 计算)。

代码真源索引

主题 文件
单关/串关派彩、四分之一盘 apps/api/src/domains/settlement/domain/settlement-calculator.ts
确认结算、actualReturn、注单状态 apps/api/src/domains/settlement/settlement.service.ts
钱包冻结/结算/重结算 apps/api/src/domains/ledger/wallet.service.ts
返水费率 apps/api/src/domains/operations/cashback/cashback-rate.resolver.ts
返水聚合/批次/入账 apps/api/src/domains/operations/cashback/cashback.service.ts
下注限额、potentialReturn apps/api/src/domains/betting/betting-limits.service.tsbets.service.ts
代理授信 apps/api/src/domains/agent/agent-credit.service.ts
串关选盘限制 packages/shared/src/betting-rules.ts
可执行数值对照 apps/api/src/infrastructure/database/run-settlement-audit.ts

2. 单关派彩公式

函数:calculatePayout(stake, odds, result)

腿级结果 SelectionResult 公式 说明
WIN stake × odds 全赢
HALF_WIN stake/2 × odds + stake/2 等价于 stake × (odds + 1) / 2
PUSH stake 走水,退本
VOID stake 作废,退本
HALF_LOSE stake / 2 半输,退一半本金
LOSE 0 全输

数值示例stake = 100

用例 ID 场景 odds 结果 payout 净盈亏
BF001 1X2 主胜全赢 2.0 WIN 200.00 +100.00
BF002 1X2 和局选项全输 LOSE 0.00 -100.00
S009 让球 -1 全赢 1.85 WIN 185.00 +85.00
S010 让球 -1 走水 1.85 PUSH 100.00 0.00
S011 让球 -0.25 半输 @ 0-0 1.85 HALF_LOSE 50.00 -50.00
S011B 半赢派彩系数 1.85 HALF_WIN 142.50 +42.50
S012 让球 -0.5 全输 @ 0-0 1.85 LOSE 0.00 -100.00
S015 大小 小球 0-0 1.95 WIN 195.00 +95.00

3. 串关派彩公式

函数:calculateParlayPayout(stake, legs[])
返回:{ betResult, payout, effectiveOdds }

flowchart TD
  start[开始] --> anyLose{任一脚 LOSE?}
  anyLose -->|是| lost["payout=0, betResult=LOST"]
  anyLose -->|否| combine[连乘有效因子]
  combine --> allPush{全部 PUSH 或 VOID?}
  allPush -->|是| push["payout=stake, betResult=PUSH"]
  allPush -->|否| won["payout=stake×combinedOdds, betResult=WON"]

各腿有效因子

腿级结果 连乘因子
WIN × odds
HALF_WIN × (odds + 1) / 2
HALF_LOSE × 0.5
PUSH / VOID × 1.0(不参与升赔)
LOSE 整单终止payout = 0

最终payout = stake × combinedOdds(除非 LOST 或全 PUSH/VOID

数值示例stake = 100

用例 ID 腿组合 effectiveOdds payout 净盈亏
S016 1.8 WIN × 2.0 WIN 3.6000 360.00 +260.00
S017 1.8 WIN × 2.0 LOSE 0 0.00 -100.00
S018 1.8 WIN × 2.0 PUSH × 1.9 WIN 3.4200 342.00 +242.00
S019 1.8 PUSH × 2.0 VOID 1.0000 100.00 0.00

串关下注限制

串关禁止四分之一让球/大小盘(.25 / .75 线),见 canSelectForParlay → 错误码 QUARTER_LINE。单关可使用四分之一盘。

跨场结算时序(影响何时产生 payout

  1. 结算某一场比赛时,只更新该场对应腿的 resultStatus
  2. 若其他场腿尚无结果 → 注单仍为 PENDING,此时无最终 payout
  3. 任一脚在本场结算为 LOSE 且其余腿已有结果 → 整单 LOSTpayout = 0
  4. 全部腿均有结果后 → 调用 calculateParlayPayout 一次,写入 actualReturn 并入账。

4. 四分之一盘(.25 / .75

让球/大小盘为 .25.75 时,settleHandicap / settleOverUnder 拆成两条半盘分别判定 WIN/PUSH/LOSE再合并

两半组合 合并结果
两赢 WIN
两输 LOSE
一赢一平 HALF_WIN
一输一平 HALF_LOSE
一赢一输 PUSH

示例S011:主让 -0.25,比分 0-0 → HALF_LOSE → payout = 50stake=100


5. actualReturn 与注单 status 映射

确认结算时(SettlementService.confirmSettlement

  • 单关1 腿)result = settleSelection(...)payout = calculatePayout(...)actualReturn = payout
  • 单关多腿 / 串关:各腿结果齐备后 → actualReturn = calculateParlayPayout(...).payout

注单状态由 betStatusFromSelection 映射:

腿级 / 整单结果 Bet.status
LOSE LOST
PUSH、VOID PUSH
WIN、HALF_WIN、HALF_LOSE WON

易错点

场景 actualReturn Bet.status
单关半输HALF_LOSE stake/2(如 50 WON(部分返还仍记赢单)
串关任一脚 LOSE 0 LOST
串关全 PUSH/VOID stake PUSH

串关确认入账时,钱包侧 result 简化为:整单 LOST → LOSE;整单 PUSH → PUSH;整单 WON → WIN(不区分腿级半赢半输)。


6. 钱包金额变动(结算侧)

下注冻结(freezeForBet

availableBalance -= stake
frozenBalance    += stake
transactionType  = BET_FREEZE

余额不足(availableBalance < stake)→ INSUFFICIENT_BALANCE

结算入账(settleBet

frozenBalance    -= stake
availableBalance += payout    // payout 即 actualReturn
传入 result 流水类型 payout 典型值
WIN BET_SETTLE_WIN stake × odds
HALF_WIN BET_SETTLE_WIN 半赢派彩
LOSE BET_SETTLE_LOSE 0
HALF_LOSE BET_SETTLE_LOSE stake / 2
PUSH BET_SETTLE_PUSH stake
VOID BET_VOID_REFUND stake

幂等键:businessKey = settle:{batchNo}:{betNo}

作废 / 取消比赛

  • Bet.status = VOIDactualReturn = stake
  • 调用 settleBet(..., payout=stake, result='VOID')BET_VOID_REFUND

重结算(confirmResettlement

delta = 新 payout - 旧 actualReturn
  • delta > 0applyResettleDelta,流水 BET_SETTLE_WIN
  • delta < 0 → 流水 RESETTLE_REVERSE(可扣至负余额
  • 注单 settlementStatus = RESETTLEDactualReturn 更新为新 payout

端到端钱包示例

用例 ID 流程 钱包变化 actualReturn
BF001 单关赢 100@2.02-1 1000 → 900 avail + 100 frozen → 1100 avail 200
BF002 单关输(和局+2-1 1000 → 900 avail 0
BF003 幂等 50 注 500 → 450 avail + 50 frozen
BF004 余额不足 50 不变
BF005 代理线玩家输 100 玩家 900 avail 0

BF001 流水顺序:MANUAL_DEPOSITBET_FREEZEBET_SETTLE_WIN


7. 下注时金额校验(与派彩相关)

potentialReturn下单时估算

类型 公式
单关 stake × odds
串关 stake × ∏(各腿 odds)

全赢估算,不含半赢/走水;用于 maxPayout* 校验。

默认限额(BettingLimitsService,可被 system_config 覆盖)

配置键 默认值
bet.min_stake 1
bet.max_stake_single 50,000
bet.max_stake_parlay 20,000
bet.max_payout_single 500,000
bet.max_payout_parlay 1,000,000
bet.daily_stake_limit 200,000placedAt 当日,排除 VOID/CANCELLED

返水基数 stake 为通过上述校验后的下注金额,与返水 rate 无联动


8. 返水金额算法

8.1 单笔返水

lineAmount = stake × rate
  • stake:整单本金;串关不按 leg 拆分
  • 实现:cashback.service.tsbet.stake.mul(rate)

8.2 费率解析(resolveCashbackRateForBet

规则表 CashbackRule 优先级(数值越大越优先):

优先级 targetType 匹配条件
3 USER targetId === userId
2 AGENT targetId === agentId(玩家直属代理)
1 GLOBAL 无 targetId 限制
  • 规则带 marketType 时,注单任一 BetSelection.marketType 命中才适用;否则跳过该规则。
  • 同优先级:遍历规则时 priority > best.priority严格大于),先写入的同优先级规则不会被后者覆盖。
  • 无规则命中 → 使用 agentDefaultRate(见下节)。

8.3 默认费率 agentDefaultRate(无 CashbackRule 时)

玩家类型 来源
parentId(代理线下) 直属代理 AgentProfile.cashbackRate;无 profile → 0
无 parent邀请人 sponsor 为 ADMIN system_configcashback.admin_invite_rate
无 parent 的其他平台直属 system_configcashback.platform_direct_rate

未配置时平台直属/邀请费率默认 0。子代理 cashbackRate 不得高于父代理。

8.4 合格注单(进入返水批次)

查询条件(aggregatePeriod

status IN ('WON', 'LOST')
settledAt 落在 [periodStart 00:00:00.000, periodEnd 23:59:59.999]
rate > 0
未被其他 PREVIEW / CONFIRMED 批次的 cashback_bets 占用

不包含PENDING、VOID、CANCELLED、PUSH 等。

周期按 settledAt,不是 placedAt

8.5 批次汇总

层级 公式
单笔 lineAmount = stake × rate
玩家 amount = Σ lineAmounteffectiveStake = Σ stake
展示 rate amount / effectiveStake(加权平均,仅展示)
平台批次 totalAmount = Σ 玩家 amount

8.6 发放入账

确认批次(confirmBatch)时:

字段
transactionType CASHBACK_DEPOSIT
amount 玩家批次 item.amount(正数)
businessKey cashback:{batchNo}:{userId}
效果 availableBalance += amount

平台直充,不扣代理 creditconfirm 调用 recalculateUsedCredit
同时将批次内注单 Bet.isCashbacked = true

8.7 数值示例(费率 → 到账)

用例 ID 场景 rate stake 100 stake 500 stake 1000
CB001 玩家专属规则 0.03 (3%) 3.00 15.00 30.00
CB002 玩法专属 FT_HANDICAP 0.005 (0.5%) 0.50 2.50 5.00
CB003 无规则,代理默认 0.02 (2%) 2.00 10.00 20.00
CB004 玩法不匹配回退默认 0.01 (1%) 1.00

8.8 本地 dev seed

  • agent1 / agent2AgentProfile.cashbackRate 默认为 schema 0seed 未显式设置。
  • seed 不创建 CashbackRule不写入 平台返水 system_config
  • 本地要产生返水批次需在管理端配置代理默认比例、CashbackRule 或平台直属费率。

9. 代理授信与金额的间接关系

usedCredit = directPlayerLiability + childExposure
availableCredit = creditLimit - usedCredit
  • directPlayerLiability = Σ(直属玩家的 availableBalance + frozenBalance
  • childExposure = Σ max(子代理 creditLimit, 子代理 usedCredit)
事件 与返水/结算关系
玩家结算输/赢 改变余额 → 结算后重算 usedCredit
返水 confirm 扣代理 credit即时重算;余额增加后下次重算计入负债
代理给玩家上分 检查 availableCredit ≥ amount

BF005:玩家输 100 → 玩家 avail 900;代理 usedCredit 1000 → 900(结算后重算)。


10. 数值对照总表与验证

10.1 派彩速查stake = 100

类型 场景 payout
单关 1X2 @2.0 WIN 200.00
单关 LOSE 0.00
单关 让球 -1 @1.85 WIN 185.00
单关 PUSH 100.00
单关 HALF_LOSE 50.00
单关 HALF_WIN @1.85 142.50
单关 小球 @1.95 WIN 195.00
串关 1.8×2.0 全中 360.00
串关 一关 LOSE 0.00
串关 含 PUSH 342.00
串关 全 PUSH/VOID 100.00

10.2 返水速查rate × stake

rate stake 100 stake 1000
3% (0.03) 3.00 30.00
2% (0.02) 2.00 20.00
0.5% (0.005) 0.50 5.00

10.3 维护与回归命令

# 打印与本文一致的金额对照表
pnpm --filter @thebet365/api audit:settlement

# 结算引擎单测(含 S016S019 串关)
pnpm --filter @thebet365/api exec jest settlement-calculator.spec.ts --runInBand

# 全量 smoke含 S009S019、CB001CB004、BF001BF005
pnpm test:smoke

修改 settlement-calculator.tscashback-rate.resolver.ts 或 smoke 期望金额时,须同步更新本文档并重新运行上述命令。

10.4 Smoke / 单测用例索引

套件 ID 范围 内容
settlement S009S019 让球/大小/串关派彩数值
settlement S011B HALF_WIN 系数
cashback CB001CB004 费率解析
bet-flow BF001BF005 钱包 + 代理额度端到端

附录:支持的 marketType结算判定入口

金额规则与下列盘口共用 settleSelection;具体比分判定逻辑见 settlement-calculator.ts,本文不展开。

FT_1X2HT_1X2FT_ODD_EVENFT_HANDICAPHT_HANDICAPFT_OVER_UNDERHT_OVER_UNDERFT_CORRECT_SCOREHT_CORRECT_SCORESH_CORRECT_SCOREHT_FTFT_TOTAL_GOALS、球队进球、角球/牌数相关盘、OUTRIGHT_WINNER 等。

未知 marketType 默认腿级结果 LOSEpayout = 0