- 新增 useGameBoardVm 数据层实施说明文档 - 添加 36字花核心玩法与前端规则摘要 - 创建游戏模块数据与界面分层第一阶段实施稿 - 定义四层架构:api/dto、store、view-model hooks、ui层 - 规范 PC 与 Mobile 共享业务逻辑的改造方案 - 明确各层职责边界和组件改造顺序
397 lines
8.9 KiB
Markdown
397 lines
8.9 KiB
Markdown
# 36字花核心玩法与前端规则摘要
|
||
|
||
## 1. 项目定位
|
||
|
||
36字花是一个**单期开奖结果、单期号循环运行**的实时开奖类游戏。
|
||
|
||
前端的核心界面是:
|
||
|
||
- 36 宫格下注盘
|
||
- 倒计时与当前期状态栏
|
||
- 筹码与确认下注区
|
||
- 开奖历史与走势信息
|
||
- 公告、规则、自动托管、充值提现等外围模块
|
||
|
||
产品运行原则:
|
||
|
||
- 全平台共享同一局数据
|
||
- `PC` 与 `Mobile` 只分界面,不分玩法、不分对局
|
||
- 前端必须以服务端状态为准,不能靠本地时间自行判断开奖或封盘
|
||
|
||
---
|
||
|
||
## 2. 核心玩法
|
||
|
||
### 2.1 基本盘面
|
||
|
||
- 游戏共有 **36 个号码格子**
|
||
- 每个格子代表一个“字花编号”
|
||
- 编号范围为 **1-36**
|
||
- 当前前端盘面布局为 **6 x 6**
|
||
|
||
相关代码与约束:
|
||
|
||
- [src/features/game/shared/constants.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/constants.ts)
|
||
- `GAME_GRID_ROWS = 6`
|
||
- `GAME_GRID_COLUMNS = 6`
|
||
- `GAME_TOTAL_CELLS = 36`
|
||
|
||
### 2.2 开奖规则
|
||
|
||
- 每一期只会开出 **1 个中奖号码**
|
||
- 开奖号码属于 1-36 中的一个
|
||
- 用户只要下注号码集合中包含该号码,即视为中奖
|
||
|
||
接口口径见:
|
||
|
||
- [docs/36字花-移动端接口设计草案.md](/Users/jiaunun/Desktop/36-character-flower/docs/36字花-移动端接口设计草案.md)
|
||
- `POST /api/game/placeBet`
|
||
|
||
文档原意:
|
||
|
||
- 玩家提交的是“号码集合 + 单注金额”
|
||
- 系统按“单注金额 × 号码数量”计算本笔总扣款
|
||
- 开奖后只出一个号码
|
||
- 若该号码命中玩家所选集合,则该笔下注中奖
|
||
|
||
---
|
||
|
||
## 3. 下注模型
|
||
|
||
### 3.1 单注结构
|
||
|
||
一笔下注至少包含:
|
||
|
||
- `period_no`:下注目标期号
|
||
- `numbers`:本次下注号码集合
|
||
- `single_bet_amount`:单个号码的下注金额
|
||
|
||
说明:
|
||
|
||
- `numbers` 是一个号码集合,而不是单个号码
|
||
- 多选号码时,总扣款 = `single_bet_amount × numbers数量`
|
||
- 重复号码应去重
|
||
|
||
### 3.2 前端当前数据模型
|
||
|
||
当前前端 store 中的单笔选择数据为:
|
||
|
||
- `cellId`
|
||
- `chipId`
|
||
- `amount`
|
||
- `placedAt`
|
||
- `source`
|
||
|
||
定义见:
|
||
|
||
- [src/features/game/shared/types.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/types.ts)
|
||
- `BetSelection`
|
||
|
||
当前本地 mock 与 store 落地方式是:
|
||
|
||
- 每点击一个格子,会追加一条 `selection`
|
||
- 每条 `selection` 对应一个格子和一个筹码金额
|
||
|
||
这与接口文档中的“号码集合提交”并不完全一致。
|
||
|
||
因此当前前端可以先采用如下理解:
|
||
|
||
- UI 交互阶段:按“逐格选择”记录本地状态
|
||
- 提交到后端阶段:再把本地多个格子聚合成 `numbers`
|
||
|
||
这是后续 `confirm bet` 需要承担的转换逻辑。
|
||
|
||
---
|
||
|
||
## 4. 回合状态机
|
||
|
||
### 4.1 当前状态枚举
|
||
|
||
当前项目中定义的回合阶段为:
|
||
|
||
- `waiting`
|
||
- `betting`
|
||
- `locked`
|
||
- `revealing`
|
||
- `settled`
|
||
|
||
定义见:
|
||
|
||
- [src/features/game/shared/constants.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/constants.ts)
|
||
|
||
接口文档中的状态口径包括:
|
||
|
||
- `betting`
|
||
- `locked`
|
||
- `settling`
|
||
- `finished`
|
||
- `void`
|
||
|
||
说明:
|
||
|
||
- 当前前端内部状态和接口文档状态还未完全统一
|
||
- 第一阶段前端可继续沿用内部状态机
|
||
- 后续真实联调时,要补一层接口状态 -> 前端状态的映射
|
||
|
||
### 4.2 各阶段前端规则
|
||
|
||
#### `BETTING`
|
||
|
||
- 允许选格子
|
||
- 允许切换筹码
|
||
- 允许清除当前选择
|
||
- 允许重复上一注
|
||
- 允许确认下注
|
||
- 允许开启自动托管
|
||
|
||
#### `LOCKED`
|
||
|
||
- 已封盘
|
||
- 前端必须立即停止下注交互
|
||
- 不应等待后端返回后才禁用点击
|
||
|
||
#### `REVEALING / SETTLING`
|
||
|
||
- 等待开奖与派彩
|
||
- 展示开奖结果、跑马灯、中奖态
|
||
|
||
#### `SETTLED / FINISHED`
|
||
|
||
- 本期结束
|
||
- 准备进入下一轮
|
||
- 历史、走势、余额等应刷新
|
||
|
||
#### `VOID`
|
||
|
||
- 本期作废
|
||
- 需要退款待开奖本金
|
||
- 前端要清理本期下注态并正确提示
|
||
|
||
---
|
||
|
||
## 5. 封盘与倒计时规则
|
||
|
||
### 5.1 核心原则
|
||
|
||
- 前端必须以服务端返回的时间和阶段为准
|
||
- 到达封盘时间点时,前端应立即锁盘
|
||
- 即使网络延迟,也不能继续允许下注交互
|
||
|
||
这点在需求文档中是明确要求:
|
||
|
||
- [docs/frontend-baseline-requirements.md](/Users/jiaunun/Desktop/36-character-flower/docs/frontend-baseline-requirements.md)
|
||
|
||
### 5.2 当前前端倒计时模型
|
||
|
||
当前前端使用:
|
||
|
||
- `round.bettingClosesAt`
|
||
- `round.revealingAt`
|
||
- `round.settledAt`
|
||
|
||
并通过:
|
||
|
||
- [src/features/game/shared/selectors.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/selectors.ts)
|
||
- `getRoundCountdownMs(round)`
|
||
|
||
来计算当前倒计时毫秒数。
|
||
|
||
规则如下:
|
||
|
||
- `waiting / betting`:倒计时到 `bettingClosesAt`
|
||
- `locked / revealing`:倒计时到 `revealingAt`
|
||
- 其余:倒计时到 `settledAt`
|
||
|
||
### 5.3 Mock 数据口径
|
||
|
||
当前 mock 数据中:
|
||
|
||
- 开始后约 18 秒封盘
|
||
- 约 24 秒开奖
|
||
- 约 30 秒结算
|
||
|
||
来源:
|
||
|
||
- [src/features/game/shared/mock-data.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/mock-data.ts)
|
||
|
||
这只是前端演示节奏,不代表最终真实服务端配置。
|
||
|
||
---
|
||
|
||
## 6. 赔率、金额与筹码规则
|
||
|
||
### 6.1 当前赔率
|
||
|
||
当前 mock 中每个格子赔率固定为:
|
||
|
||
- `36`
|
||
|
||
来源:
|
||
|
||
- [src/features/game/shared/mock-data.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/mock-data.ts)
|
||
- `createGameCells()`
|
||
|
||
### 6.2 当前默认筹码
|
||
|
||
当前默认筹码面额为:
|
||
|
||
- `10`
|
||
- `25`
|
||
- `50`
|
||
- `100`
|
||
- `200`
|
||
- `500`
|
||
|
||
来源:
|
||
|
||
- [src/features/game/shared/constants.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/constants.ts)
|
||
- `DEFAULT_GAME_CHIP_AMOUNTS`
|
||
|
||
### 6.3 下注限制
|
||
|
||
从接口文档与基线需求中可得出以下前端规则:
|
||
|
||
- 单次下注号码数量不得超过 `pick_max_number_count`
|
||
- 单号码下注金额不得超过 `single_number_max_bet`
|
||
- 总下注额不得超过余额
|
||
- 封盘后不能继续下注
|
||
|
||
文档来源:
|
||
|
||
- [docs/36字花-移动端接口设计草案.md](/Users/jiaunun/Desktop/36-character-flower/docs/36字花-移动端接口设计草案.md)
|
||
- [docs/frontend-baseline-requirements.md](/Users/jiaunun/Desktop/36-character-flower/docs/frontend-baseline-requirements.md)
|
||
|
||
---
|
||
|
||
## 7. 中奖、历史与走势
|
||
|
||
### 7.1 历史记录
|
||
|
||
每期开奖后应产生一条历史记录,至少包括:
|
||
|
||
- `roundId`
|
||
- `winningCellId`
|
||
- `settledAt`
|
||
- `payoutMultiplier`
|
||
- `totalPoolAmount`
|
||
|
||
定义见:
|
||
|
||
- [src/features/game/shared/types.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/types.ts)
|
||
- `HistoryEntry`
|
||
|
||
### 7.2 走势
|
||
|
||
前端会基于历史数据派生走势信息,包括:
|
||
|
||
- `currentStreak`
|
||
- `hitCount`
|
||
- `missCount`
|
||
- `direction`
|
||
- `lastHitRoundId`
|
||
|
||
派生逻辑见:
|
||
|
||
- [src/features/game/shared/selectors.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/selectors.ts)
|
||
- `deriveTrendEntries(history)`
|
||
|
||
说明:
|
||
|
||
- 走势属于纯派生展示数据
|
||
- 不应由 UI 组件自己重复计算
|
||
|
||
---
|
||
|
||
## 8. 公告、维护与运行开关
|
||
|
||
### 8.1 运行开关
|
||
|
||
接口文档中存在:
|
||
|
||
- `runtime_enabled`
|
||
|
||
含义:
|
||
|
||
- `true`:游戏正常运行
|
||
- `false`:后台维护中,禁止下注
|
||
|
||
规则:
|
||
|
||
- 维护中不允许新下注
|
||
- 当前已开盘的一局仍可正常开奖和派彩
|
||
- 前端应禁用下注入口并提示维护状态
|
||
|
||
### 8.2 公告
|
||
|
||
公告是前端大厅的一部分,但不属于主玩法。
|
||
|
||
当前前端已存在公告模型:
|
||
|
||
- `AnnouncementState`
|
||
- `AnnouncementItem`
|
||
|
||
它们属于会话层状态,不应混入下注与回合逻辑。
|
||
|
||
---
|
||
|
||
## 9. 自动托管
|
||
|
||
接口文档中已定义自动托管能力:
|
||
|
||
- `POST /api/game/autoSpin`
|
||
|
||
入参包括:
|
||
|
||
- `action`
|
||
- `period_no`
|
||
- `numbers`
|
||
- `single_bet_amount`
|
||
- `rounds`
|
||
|
||
说明:
|
||
|
||
- 自动托管属于建立在主玩法之上的扩展能力
|
||
- 它依赖同一套选号与下注规则
|
||
- 当前前端可以先保留 UI 壳层,不需要在主玩法没走通前抢先落业务
|
||
|
||
---
|
||
|
||
## 10. 前端当前最应该优先走通的主玩法链路
|
||
|
||
基于上述规则,当前前端最核心、最应该优先走通的是:
|
||
|
||
1. 状态栏拿到当前期状态与倒计时
|
||
2. 控制栏拿到当前筹码与总下注额
|
||
3. 选号盘点击格子后写入本地下注选择
|
||
4. 总下注额、选中数量、选号高亮联动刷新
|
||
5. 后续再补确认下注请求与开奖结果回写
|
||
|
||
这也是为什么当前下一步应优先实现:
|
||
|
||
- `useGameBoardVm`
|
||
|
||
而不是优先改 `mobile` 或外围弹窗。
|
||
|
||
---
|
||
|
||
## 11. 总结
|
||
|
||
36字花这个项目的核心,不是“36 张图摆出来”,而是下面这条实时对局链路:
|
||
|
||
- 同一局
|
||
- 同一倒计时
|
||
- 同一开奖结果
|
||
- 36 格可选号码
|
||
- 用户用统一筹码模型下注
|
||
- 封盘、开奖、派奖按服务端状态推进
|
||
|
||
前端实现时必须坚持两点:
|
||
|
||
1. **玩法规则统一**
|
||
- PC 和 Mobile 只能换壳,不能换规则
|
||
|
||
2. **服务端状态优先**
|
||
- 前端可以先做本地交互反馈
|
||
- 但回合状态、封盘、开奖、派彩都必须最终以服务端为准
|
||
|