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

273 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 赛事结算全流程与资金链路分析报告
---
### 一、赛事生命周期
比赛经历以下状态流转:
```
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 UPDATE`PostgreSQL 悲观锁串行化 |
| 幂等性 | 注单表有 `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 元 |