62 KiB
62 KiB
36字花移动端接口设计草案(V1)
本文基于 docs/36字花-数据库与实施计划.md 与 PRD,先给出移动端可对接的接口清单与字段初设。
口径遵循:全平台单期号、单开奖结果;渠道仅用于归属、分润与风控,不拆分对局。
补充(2026-04):§1.5 描述服务端 Redis 热点缓存(GameHotDataRedis),不改变各接口 URL、参数与响应字段约定,仅供联调与运维对照。
1. 设计约定
1.1 基础约定
- 协议:HTTPS + JSON
- 接口命名规范:
/api/{module}/{action},且必须满足正则^/api/[a-z]+/[a-z]+[A-Z][a-zA-Z]*$ - 请求方法:所有移动端业务接口(
/api/*,不含/api/v1/authToken)一律使用POST调用;查询类接口同时兼容GET(便于浏览器/调试工具直接访问),客户端统一走POSTPOST时请求头Content-Type: application/json,参数放在 JSON bodyGET兼容模式下,参数走 URL query string- 例外:公告模块
/api/notice/noticeList、/api/notice/noticeConfirm仅支持GET,参数一律走 URL query string - 公告列表免鉴权:
GET /api/notice/noticeList无需auth-token、user-token;若仍携带user-token则强弹窗项可返回该用户真实的is_read - 鉴权类接口
/api/v1/authToken仍为GET
- 时间:UTC 时间戳(秒) + 服务端时区配置
- 金额:数字传输(如
"100.00"),客户端展示统一保留两位小数(存储仍为decimal(18,2)) - 幂等:关键写接口要求
idempotency_key - 请求头(必带):
auth-token:通过GET /api/v1/authToken获取的接口鉴权令牌(含义:接口访问的签名鉴权凭证)user-token:用户登录态令牌;需要登录的接口必带
- 语言请求头:
lang=zh:返回中文(默认)lang=en:返回英文
1.2 通用响应结构
{
"code": 1,
"message": "ok",
"data": {}
}
-
code=1表示成功,非 1 为业务错误 -
/api/*所有接口返回文案支持中英双语:默认中文;请求头lang=en返回英文,lang=zh返回中文 -
建议错误码段(按错误性质):
1000-1099:参数错误(字段缺失、类型错误、格式错误、超范围)1100-1199:鉴权错误(未登录、token 失效、权限不足)2000-2999:业务错误(余额不足、对局不存在、订单不存在、公告不存在)3000-3099:流程错误(非法流程/状态不允许,如封盘后下注、重复确认、状态跃迁非法)5000-5999:系统错误(服务异常、依赖超时、未知错误)
-
推荐基础错误码(首版):
-
1:成功1001:参数缺失1002:参数格式错误1003:参数取值非法1101:未登录或登录已过期1103:无权限操作2001:余额不足2002:对局不存在2003:订单不存在2004:公告不存在3001:当前流程不允许该操作3002:已封盘,禁止下注3003:重复请求(幂等冲突)5000:系统繁忙,请稍后重试
1.3 鉴权方式
- 接口鉴权(auth-token):所有移动端业务接口请求时必须携带请求头
auth-token(由/api/v1/authToken签发) - 用户登录鉴权(user-token):需要登录的接口携带请求头
user-token;token 失效后调用刷新或重新登录
1.4 获取接口鉴权 Token(auth-token)
- GET
/api/v1/authToken - 用途:获取
auth-token(所有接口请求头必带)
请求示例:
/api/v1/authToken?secret=564d14asdasd113e46542asd6das1a2a×tamp=1776331077&device_id=1&signature=AD84C49880896DBC16C59C7B122D1FF7
请求参数:
secret:string(含义:客户端密钥;服务端从环境变量AUTH_TOKEN_SECRET校验)timestamp:int(含义:请求时间戳;服务端允许与服务器时间误差 ±300 秒)device_id:string(含义:设备码)signature:string(含义:签名值)
签名算法:
- 取参与签名的参数(不含
signature):device_id、secret、timestamp - 按参数名 a-z 排序后拼接为字符串:
key=value&key=value... - 计算:
signature = strtoupper(md5(拼接字符串))
返回参数:
auth_token:string(含义:接口鉴权 token;放到请求头auth-token)expires_in:int(含义:有效期秒数)server_time:int(含义:服务器时间戳,用于校时)
可能错误码:
1001参数缺失1002参数格式错误1103密钥无效/签名错误3001时间戳无效
1.5 服务端性能与 Redis 热点缓存(实现说明)
对客户端无契约变更:请求路径、参数、响应 JSON 形状与错误码均不因缓存而改变;本节仅说明服务端如何降延迟、读路径与一致性注意点。
与「框架文件缓存」的区别
| 配置 | 作用域 |
|---|---|
CACHE_DRIVER(config/cache.php,如 file) |
Think-ORM / get_sys_config() 等系统参数表 config 的模型缓存,落盘在 runtime/cache,不参与本游戏业务热点路径。 |
GAME_HOT_CACHE_*(config/game_hot_cache.php) |
游戏侧 user / game_config / game_record 行级 JSON 缓存,走 support\Redis(config/redis.php 连接),键前缀 dfw:v1:。 |
服务端缓存覆盖(与移动端直接相关的读路径)
- 用户:会员鉴权优先读 Redis 中的
user行快照,未命中再查库并回填。余额、连胜、打码量等变更落库后,统一经GameHotDataCoordinator::afterUserCommitted($userId):先GameHotDataRedis::userReplaceCacheFromDb与 DB 对齐,再向 Redis 写队列投递幂等刷新任务(见GameHotDataWriteQueue/GameHotDataQueueConsumer),用于削峰而非替代同步回源。 - 游戏配置:
game_config按config_key缓存。后台直连Db更新时须GameHotDataCoordinator::afterGameConfigKeyCommitted($key)(模型GameConfig事件与独立表单控制器已接入);独立保存接口在写入前对同一config_key使用GameHotDataLock(TYPE_GAME_CONFIG) 互斥。勿仅删除缓存键而不回源,否则最长不一致窗口为 TTL。 - 对局:当前活跃局、按
id的局、最新一条game_record等;写库后经GameHotDataCoordinator::afterGameRecordCommitted同步刷新相关 Redis 键并入队。开奖/封盘等路径另可按记录 id 使用GameHotDataLock(TYPE_GAME_RECORD) 串行化。
环境变量(示例见仓库根目录 .env-example)
GAME_HOT_CACHE_ENABLED:是否启用上述 Redis 热点缓存(false时全程回退数据库)。GAME_HOT_CACHE_TTL_GAME_CONFIG/GAME_HOT_CACHE_TTL_GAME_RECORD/GAME_HOT_CACHE_TTL_USER:各类缓存 TTL(秒);以写后同步回源为主,TTL 仅作兜底。GAME_HOT_CACHE_ENABLE_WRITE_QUEUE及队列长度、消费进程间隔等:控制写库后的幂等刷新任务是否入队及背压策略(见config/game_hot_cache.php)。
一致性提示(联调/测试)
- 任何绕过协调入口、只改 DB 不调用
GameHotDataCoordinator的手工脚本,都可能与 Redis 短期不一致;生产环境应避免。 POST /api/game/betPlace扣款路径使用与后台钱包加减点相同的 用户维度 Redis 锁(GameHotDataRedis::userAdminMutationLockTry)及WHERE coin = ?条件更新,与并发派彩/后台调账互斥;失败时返回 §4.2 所列中文说明。- 客户端仍可按 §3.2
dictionaryList的version做本地缓存;服务端字典另有 Redis 加速,二者可同时存在。
2. 认证与账户模块(user)
2.1 注册
- POST
/api/user/register - 用途:仅手机号注册并绑定邀请归属(admin/channel)
请求参数:
username:string,手机号(含义:注册账号,仅支持大陆手机号)password:string,明文经 HTTPS 传输(含义:登录密码,服务端需加盐哈希存储)invite_code:string,必填(含义:子代理邀请码,用于绑定渠道channel_id与归属)device_id:string,可选(含义:设备标识,用于风控与登录记录)
返回参数:
user-token:string(含义:后续接口登录态令牌;用于需要登录的接口请求头)refresh_token:string,可选(含义:用于刷新访问令牌)expires_in:int(秒,含义:令牌有效期)user:object(仅返回非私密信息,不返回id)uuid:string(含义:用户对外唯一标识,10 位)username:string(含义:用户昵称/展示名)coin:string(含义:当前余额)channel_id:int(含义:归属渠道 ID)risk_flags:int(含义:风控状态位)
2.2 登录
- POST
/api/user/login
请求参数:
username:string(含义:登录账号,当前支持手机号)password:string(含义:登录密码)device_id:string,可选(含义:设备标识,辅助风控)
返回参数:
user-token:string(含义:访问令牌;用于需要登录的接口请求头)refresh_token:string,可选(含义:用于刷新访问令牌)expires_in:int(含义:访问令牌剩余有效秒数)user:object(仅返回非私密信息,不返回id)uuid:string(含义:用户对外唯一标识,10 位)username:string(含义:用户昵称/展示名)coin:string(含义:当前余额)channel_id:int(含义:归属渠道 ID)risk_flags:int(含义:风控状态位)
2.3 获取当前用户信息
- POST
/api/user/profile
返回参数(金额类字段统一 2 位小数字符串,与钱包展示口径一致):
基础档案
uuid:string(含义:用户对外唯一标识,10 位)username:string(含义:昵称)head_image:string(含义:头像地址)phone:string(含义:手机号)email:string(含义:邮箱)register_invite_code:string(含义:注册邀请码快照)channel_id:int(含义:归属渠道 ID)risk_flags:int(含义:风控状态位)current_streak:int(含义:当前连胜次数)last_bet_period_no:string(含义:最近一笔有效下注所在期号)create_time:int(含义:注册时间戳)
资金与提现配额
coin/coin_balance:string(含义:当前余额;两字段同值)frozen_balance:string(含义:冻结余额;无冻结场景,固定0.00)total_deposit_coin:string(含义:累计充值)total_withdraw_coin:string(含义:累计提现;受理后累加)bet_flow_coin:string(含义:打码量/流水;开奖结算后按每注total_amount1:1 累加)max_withdrawable:string(含义:当前允许发起的单笔最大提现金额 =min(coin_balance, max_withdraw_by_flow))withdraw_flow:object(含义:打码量 / 提现配额诊断快照,此处额外附pending_withdraw)ratio:string(打码量倍数;0表示不限打码)net_deposit:string(净充值 = max(0, 累计充值 − 累计提现))required_bet_flow:string(按门槛口径所需打码量,纯展示)remaining_bet_flow:string(按门槛口径还差多少打码量,纯展示)eligible:bool(是否满足整体门槛,纯展示;真正放行以max_withdrawable为准)max_withdraw_by_flow:string/null(仅按打码量折算的上限;ratio=0时为null)flow_unlimited:bool(是否处于"不限打码"状态)pending_withdraw:objectcount:int(当前待审核提现订单数)max:int(单用户最多允许的待审核提现数,当前为3;超过withdrawCreate返回code=2004)
2.4 刷新令牌(可选)
- POST
/api/user/refreshToken
请求参数:
refresh_token:string(含义:续签访问令牌的凭证)
返回参数:
user-token:string(含义:新访问令牌)expires_in:int(含义:新令牌有效期)
3. 游戏大厅与字典模块(game/lobby)
3.1 获取首页初始化数据
- POST
/api/game/lobbyInit - 用途:一次返回本局、配置、36字花字典、用户快捷展示
返回参数:
server_time:int(含义:服务端当前时间,用于客户端校时)runtime_enabled:bool(含义:游戏运行开关;false时表示后台维护——禁止下注,且 idle 时不会自动创建新期、派彩结束后也不会自动创建下一期;当前已开盘的局仍会开奖、派彩并结算。移动端应禁用下注入口并提示「维护」类文案)period:objectperiod_no:string(含义:当前全局期号)status:string(betting/locked/settling/finished/void,含义:当前期状态;void表示该期已作废)countdown:int(含义:当前期倒计时秒数)lock_at:int(含义:封盘时间戳)open_at:int(含义:预计开奖时间戳)
bet_config:objectpick_max_number_count:int(含义:单注最多可选号码数,来自game_config.config_key = pick_max_number_count,缺省与库内种子一致,通常为 10,合法范围 1–36)chips:object(含义:快捷筹码字典,固定 6 个键"1"…"6",值为该档单注面额字符串,两位小数;与后台game_config.bet_chips语义一致)default_bet_chip_id:int(含义:默认选中的筹码标识,来自game_config.default_bet_chip_id,非法或指向无效档位时服务端回退为首个有效档)min_bet_per_number:string(含义:单号码最小下注额,须 ≤ 所选筹码面额且受后台配置约束)max_bet_per_number:string(含义:单号码最大下注额)
dictionary:array