Files
thebet365/docs/settlement-and-fund-flow-analysis.md
Mars 844727c82e feat: 前台匿名浏览、登录引导、客服入口与返水增强
前台:
- 未登录可浏览首页/赛事/赔率,下注等操作弹出登录引导(去登录/继续浏览)
- 顶部新增客服入口与 iframe 弹窗
- 登录页支持暂不登录返回浏览

API:
- 首页/赛事/冠军盘接口改为公开访问,支持 X-Locale 头
- JWT 守卫支持可选认证

返水:
- 注单新增 is_cashbacked 字段,发放时自动标记
- 预览展示玩家余额,明确平台直发不从代理扣款
- 后台注单列表与玩家历史展示回水状态

其他:
- 串关禁止同场重复选号(SAME_MATCH)
- 补充结算资金流分析文档

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 09:36:44 +08:00

11 KiB
Raw Blame History

赛事结算全流程与资金链路分析报告


一、赛事生命周期

比赛经历以下状态流转:

DRAFT → PUBLISHED → CLOSED → PENDING_SETTLEMENT → SETTLED
                         ↓
                     CANCELLED取消全部退款
状态转换 触发方式
DRAFT → PUBLISHED 管理员发布比赛
PUBLISHED → CLOSED 每分钟自动 cron 检测开赛时间到达,或管理员手动关闭
CLOSED → PENDING_SETTLEMENT 管理员录入比分
PENDING_SETTLEMENT → SETTLED 管理员确认结算
任意 → CANCELLED 管理员取消比赛,所有未结算注单全额退款

玩家端看到三种简化状态:open(可投注)、closed_pending(封盘待结算)、settled(已结算)。


二、结算操作流程(三步走)

结算完全由管理员手动触发,分三个阶段:

第一步:录入比分

  • 接口:POST /admin/matches/:id/settlement/score
  • 管理员输入全场比分(主队/客队半场和全场进球),冠军盘只需选择获胜队伍
  • 系统创建 MatchScore 记录,比赛状态变为 PENDING_SETTLEMENT

第二步:预览结算

  • 接口:POST /admin/matches/:id/settlement/preview
  • 系统自动计算所有该场次的 PENDING 注单结果(赢/输/走水/半赢/半输)
  • 创建 SettlementBatch(状态 PREVIEW汇总总注单数、总派彩、总退款
  • 管理员可分页查看每笔注单的结算预览

第三步:确认结算

  • 接口:POST /admin/settlement/:batchId/confirm
  • 在单个数据库事务中:逐笔结算、更新钱包、标记比赛为 SETTLED
  • 结算后触发所有相关代理的信用额度重算

三、单关 vs 串关结算差异

单关投注Single Bet

每笔注单只有一个选项,直接根据比分计算结果:

结果 派彩公式
WIN全赢 stake × odds
HALF_WIN半赢 stake/2 × odds + stake/2
PUSH走水 stake全额退还
HALF_LOSE半输 stake / 2
LOSE全输 0

串关投注Parlay Bet

串关跨多场比赛,结算逻辑更复杂:

  1. 结算某场比赛时:只计算该场比赛中涉及的"腿"leg其他场次的腿等待后续结算
  2. 所有腿都有结果后:按串关规则合并计算总赔率
  3. 任一腿为 LOSE:整单作废,派彩 = 0
  4. 全部腿为 PUSH/VOID:整单走水,退还本金
  5. 混合结果
    • WIN乘以该腿赔率
    • HALF_WIN乘以 (odds + 1) / 2
    • HALF_LOSE乘以 0.5
    • PUSH/VOID乘以 1.0(跳过)
    • 最终派彩 = stake × 所有腿赔率的连乘

亚盘让球/大小球Quarter Line

对于 .25.75 盘口,系统拆分为两条半盘独立结算,再合并:

  • 两半都赢 → WIN
  • 两半都输 → LOSE
  • 一赢一平 → HALF_WIN
  • 一输一平 → HALF_LOSE
  • 一赢一输 → PUSH

四、支持的盘口类型

盘口类型 结算依据
FT_1X2全场胜平负 全场比分
HT_1X2半场胜平负 半场比分
FT_ODD_EVEN全场单双 全场总进球奇偶0-0 = 双)
FT_HANDICAP全场让球 全场比分 + 让球盘
HT_HANDICAP半场让球 半场比分 + 让球盘
FT_OVER_UNDER全场大小 全场总进球 + 大小盘
HT_OVER_UNDER半场大小 半场总进球 + 大小盘
FT_CORRECT_SCORE波胆 精确比分匹配
HT_CORRECT_SCORE半场波胆 半场精确比分
OUTRIGHT_WINNER冠军 获胜队伍编号

五、资金链路全景图

                        充值Admin/Agent → 玩家)
                    ┌──────────────────────────────┐
                    │ availableBalance += 金额       │
                    │ 流水: MANUAL_DEPOSIT (+)       │
                    │ Agent.usedCredit 重算          │
                    └──────────────────────────────┘
                              │
                              ▼
                        下注(玩家 → 系统)
                    ┌──────────────────────────────┐
                    │ availableBalance -= stake      │
                    │ frozenBalance += stake         │
                    │ 流水: BET_FREEZE (-stake)      │
                    │ 注单状态: PENDING              │
                    └──────────────────────────────┘
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
         赢了 (WIN)       输了 (LOSE)      走水 (PUSH)
    ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
    │ frozen -= stk│ │ frozen -= stk│ │ frozen -= stk    │
    │ avail += stk×odds     │ │ avail += 0   │ │ avail += stk     │
    │ 流水: BET_SETTLE_WIN  │ │ 流水: BET_SETTLE_LOSE   │ │ 流水: BET_SETTLE_PUSH │
    │ 注单: WON    │ │ 注单: LOST   │ │ 注单: PUSH       │
    └──────────────┘ └──────────────┘ └──────────────────┘

                        提现(玩家 → Admin/Agent
                    ┌──────────────────────────────┐
                    │ availableBalance -= 金额       │
                    │ 流水: MANUAL_WITHDRAW (-)      │
                    │ Agent.usedCredit 重算          │
                    └──────────────────────────────┘

                        返水/返佣(系统 → 玩家)
                    ┌──────────────────────────────┐
                    │ availableBalance += 金额       │
                    │ 流水: CASHBACK_DEPOSIT (+)     │
                    │ 金额 = 有效投注额 × 返水率      │
                    └──────────────────────────────┘

                        重新结算(比分纠正)
                    ┌──────────────────────────────┐
                    │ availableBalance += delta      │
                    │ delta = 新派彩 - 旧派彩        │
                    │ 流水: BET_SETTLE_WIN 或        │
                    │       RESETTLE_REVERSE         │
                    │ delta 可为负数,余额可变负)   │
                    └──────────────────────────────┘

六、钱包模型

每个玩家有一个钱包,包含两个余额字段:

  • availableBalance(可用余额):可自由使用的金额
  • frozenBalance(冻结余额):已下注未结算的金额

每次余额变动都会创建一条 WalletTransaction 记录包含变动前后的快照balanceBefore/After, frozenBefore/After形成完整的审计链。

所有交易类型

类别 类型 方向
充值 MANUAL_DEPOSIT 入账 (+)
提现 MANUAL_WITHDRAW 出账 (-)
下注冻结 BET_FREEZE available → frozen
结算赢 BET_SETTLE_WIN frozen 释放 + 派彩入账
结算输 BET_SETTLE_LOSE frozen 释放,无入账
结算走水 BET_SETTLE_PUSH frozen 释放 + 退还本金
比赛取消退款 BET_VOID_REFUND frozen 释放 + 退还本金
重新结算调整 RESETTLE_REVERSE delta 调整(可正可负)
返水 CASHBACK_DEPOSIT 入账 (+)

七、代理信用体系

代理Agent没有真实钱包采用信用额度模型:

可用信用 = creditLimit - usedCredit

其中 usedCredit = directPlayerLiability + childAgentExposure

  • directPlayerLiability:所有直属玩家的(可用余额 + 冻结余额)之和
  • childAgentExposure:所有子代理的 max(creditLimit, usedCredit) 之和

约束规则

  • 子代理的 creditLimit 不能超过父代理
  • 子代理的 cashbackRate 不能超过父代理
  • 给玩家充值前,检查代理可用信用 ≥ 充值金额
  • 每次充值/提现/结算后都会重算代理信用

八、返水Cashback系统

返水率解析优先级(从高到低)

  1. 玩家个人规则targetType = USER
  2. 代理规则targetType = AGENT
  3. 全局规则targetType = GLOBAL
  4. 代理默认 cashbackRate

返水流程

  1. 预览:加载周期内所有 WON/LOST 的注单,计算有效投注额 × 返水率
  2. 确认:逐笔调用 wallet.deposit() 发放返水,交易类型 CASHBACK_DEPOSIT
  3. 取消:仅 PREVIEW 状态可取消

返水公式

返水金额 = 有效投注额 × 返水率

有效投注额 = 周期内所有已结算WON/LOST注单的投注金额之和


九、安全与并发控制

机制 说明
行级锁 每次钱包操作使用 SELECT ... FOR UPDATEPostgreSQL 悲观锁串行化
幂等性 注单表有 UNIQUE(userId, requestId) 约束,重复提交直接返回已有注单
乐观版本 Wallet.version 每次变动递增,提供审计轨迹
事务完整性 下注+冻结在同一事务;一场比赛所有注单结算在同一事务
余额校验 下注前检查 availableBalance ≥ stake提现前检查 availableBalance ≥ amount
重新结算 delta 可为负数,允许追扣(余额可能变负)

十、重新结算(纠错机制)

当管理员发现比分录入错误时:

  1. 预览重新结算:用新比分重新计算所有注单结果,对比新旧派彩差额
  2. 确认重新结算
    • delta > 0补发差额BET_SETTLE_WIN
    • delta < 0追扣差额RESETTLE_REVERSE余额可能变负
    • 注单标记 settlementStatus = RESETTLED
    • 代理信用重算

十一、典型流程示例

示例:玩家投注主胜 100 元,赔率 2.0

步骤 availableBalance frozenBalance 说明
初始 1,000 0 -
下注 900 100 BET_FREEZE
结算(赢) 1,100 0 BET_SETTLE_WIN: +200
净收益 +100 - 派彩 200 - 本金 100

示例:串关 3 串 1本金 100 元,赔率 2.0 × 3.0 × 1.5 = 9.0

步骤 说明
下注 冻结 100 元
第一场结算(赢) 该腿 WIN注单仍 PENDING
第二场结算(输) 该腿 LOSE整单 LOST派彩 = 0
第三场 无需结算,注单已终结
结果 冻结释放,派彩 0玩家亏损 100 元