1.新增退出登录的接口
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
|
||||
**补充(2026-04)**:§**1.5** 描述服务端 **Redis 热点缓存**(`GameHotDataRedis`),**不改变**各接口 URL、参数与响应字段约定,仅供联调与运维对照。
|
||||
|
||||
**补充(2026-05)**:§**1.3** / §**1.4** / §**2.3** 描述 **单设备登录** 与 **`POST /api/user/logout`** 退出接口(依赖 Redis 存储 `device_id` 绑定)。
|
||||
|
||||
## 1. 设计约定
|
||||
|
||||
### 1.1 基础约定
|
||||
@@ -63,6 +65,11 @@
|
||||
### 1.3 鉴权方式
|
||||
- **接口鉴权(auth-token)**:所有移动端业务接口请求时必须携带请求头 `auth-token`(由 `/api/v1/authToken` 签发)
|
||||
- **用户登录鉴权(user-token)**:需要登录的接口携带请求头 `user-token`;token 失效后调用刷新或重新登录
|
||||
- **单设备登录(实现约束)**:
|
||||
- 获取 `auth-token` 时 Query 中的 `device_id` 会与该 `auth-token` 绑定(Redis)
|
||||
- 同一账号**仅允许一个活跃设备**:新设备登录成功后,会清除该用户其它 `user-token` / `refresh_token`,并绑定新 `device_id`
|
||||
- 旧设备继续携带原 `user-token` 访问需登录接口时,返回 `code=1101`,`message` 为「您的账号已在其他设备登录,请重新登录」(`lang=en` 时为英文)
|
||||
- 客户端应为每个安装实例生成**稳定且唯一**的 `device_id`(勿每次随机,否则会被视为新设备)
|
||||
|
||||
### 1.4 获取接口鉴权 Token(auth-token)
|
||||
- **GET** `/api/v1/authToken`
|
||||
@@ -87,6 +94,10 @@
|
||||
- `expires_in`:int(含义:有效期秒数)
|
||||
- `server_time`:int(含义:服务器时间戳,用于校时)
|
||||
|
||||
服务端行为(与单设备登录相关):
|
||||
- 签发成功后将 `device_id` 与 `auth_token` 写入 Redis,TTL 与 `expires_in` 一致
|
||||
- 后续登录、已登录业务接口、WebSocket 握手均以此 `auth-token` 所绑定的 `device_id` 与用户活跃设备比对
|
||||
|
||||
可能错误码:
|
||||
- `1001` 参数缺失
|
||||
- `1002` 参数格式错误
|
||||
@@ -128,13 +139,13 @@
|
||||
|
||||
### 2.1 注册
|
||||
- **POST** `/api/user/register`
|
||||
- 用途:仅手机号注册并绑定邀请归属(admin/channel)
|
||||
- 用途:仅手机号注册并绑定邀请归属(admin/channel);注册成功后会自动登录
|
||||
- 请求头:必带 `auth-token`(设备标识以该 token 绑定的 `device_id` 为准,见 §1.3)
|
||||
|
||||
请求参数:
|
||||
- `username`:string,手机号(含义:注册账号,仅支持大陆手机号)
|
||||
- `password`:string,明文经 HTTPS 传输(含义:登录密码,服务端需加盐哈希存储)
|
||||
- `invite_code`:string,必填(含义:子代理邀请码,用于绑定渠道 `channel_id` 与归属)
|
||||
- `device_id`:string,可选(含义:设备标识,用于风控与登录记录)
|
||||
|
||||
返回参数:
|
||||
- `user-token`:string(含义:后续接口登录态令牌;用于需要登录的接口请求头)
|
||||
@@ -149,11 +160,14 @@
|
||||
|
||||
### 2.2 登录
|
||||
- **POST** `/api/user/login`
|
||||
- 请求头:必带 `auth-token`(其绑定的 `device_id` 即本次登录设备,**无需**在 body 重复传 `device_id`)
|
||||
|
||||
请求参数:
|
||||
- `username`:string(含义:登录账号,当前支持手机号)
|
||||
- `password`:string(含义:登录密码)
|
||||
- `device_id`:string,可选(含义:设备标识,辅助风控)
|
||||
|
||||
服务端行为:
|
||||
- 登录成功后会清除该用户其它会话 token,并将活跃设备设为当前 `auth-token` 对应 `device_id`(见 §1.3)
|
||||
|
||||
返回参数:
|
||||
- `user-token`:string(含义:访问令牌;用于需要登录的接口请求头)
|
||||
@@ -166,7 +180,29 @@
|
||||
- `channel_id`:int(含义:归属渠道 ID)
|
||||
- `risk_flags`:int(含义:风控状态位)
|
||||
|
||||
### 2.3 获取当前用户信息
|
||||
### 2.3 退出登录
|
||||
- **POST** `/api/user/logout`
|
||||
- 用途:主动退出当前账号,作废登录态并释放单设备占用
|
||||
|
||||
请求头:
|
||||
- `auth-token`:必填
|
||||
- `user-token`:已登录时必填
|
||||
|
||||
请求参数(可选):
|
||||
- `refresh_token`:string(含义:刷新令牌;建议传入以便服务端一并删除;未传且已登录时服务端尝试删除当前会话的 refresh_token)
|
||||
|
||||
返回参数:
|
||||
- `data`:空对象 `{}`
|
||||
|
||||
服务端行为:
|
||||
- 删除当前 `user-token`、清除 Redis 中该用户的活跃 `device_id` 绑定
|
||||
- 若提供 `refresh_token` 则删除对应 refresh 记录
|
||||
- **幂等**:未登录或 token 已失效时仍返回 `code=1`(便于客户端清理本地缓存)
|
||||
|
||||
可能错误码:
|
||||
- `1101`:`auth-token` 缺失或无效(与 §1.4 一致)
|
||||
|
||||
### 2.4 获取当前用户信息
|
||||
- **POST** `/api/user/profile`
|
||||
|
||||
返回参数(金额类字段统一 2 位小数字符串,与钱包展示口径一致):
|
||||
@@ -203,8 +239,9 @@
|
||||
- `count`:int(当前待审核提现订单数)
|
||||
- `max`:int(单用户最多允许的待审核提现数,当前为 `3`;超过 `withdrawCreate` 返回 `code=2004`)
|
||||
|
||||
### 2.4 刷新令牌(可选)
|
||||
### 2.5 刷新令牌(可选)
|
||||
- **POST** `/api/user/refreshToken`
|
||||
- 请求头:必带 `auth-token`;已登录会话须与当前活跃 `device_id` 一致(§1.3)
|
||||
|
||||
请求参数:
|
||||
- `refresh_token`:string(含义:续签访问令牌的凭证)
|
||||
@@ -765,7 +802,7 @@
|
||||
- **混合内容**:若 H5 页面为 **HTTPS**,浏览器要求 WebSocket 使用 **`wss://`**,否则会被拦截。
|
||||
- **事件投递依赖 Redis**:HTTP 侧业务通过 **`GameWebSocketEventBus`**(Redis 列表)将事件投递到 WebSocket 进程;Redis 不可用或队列异常时,**除 `admin.live.snapshot` 外**的广播类推送可能收不到。后台若订阅了 `admin.live.snapshot`,服务端有**每秒直连构建快照**的兜底,不依赖队列。
|
||||
- **握手鉴权(2026-05 重构后强制)**:`GameWebSocketServer::onWebSocketConnect` 通过 `GameWebSocketAuthHelper::authorize` 校验 URL Query。两种合法身份:
|
||||
- **mobile(H5/移动端)**:必须同时携带 Query **`auth-token`**、**`user-token`**(与 HTTP 请求头同名,**统一用连字符**)。校验通过后绑定 `user_id`,分发器仅向其推送本人的 user 级主题。服务端仍兼容旧别名 `auth_token` / `user_token` 解析,但**新接入请只用连字符**。
|
||||
- **mobile(H5/移动端)**:必须同时携带 Query **`auth-token`**、**`user-token`**(与 HTTP 请求头同名,**统一用连字符**)。校验通过后绑定 `user_id`,分发器仅向其推送本人的 user 级主题;并校验 `auth-token` 绑定 `device_id` 与用户活跃设备一致(§1.3),异设备会话将被拒绝。服务端仍兼容旧别名 `auth_token` / `user_token` 解析,但**新接入请只用连字符**。
|
||||
- **admin(后台/运维)**:必须携带 Query **`admin-ws-token`**(由后台 `wsConfig` 签发,写入 Redis,默认 TTL 7200s)。`ws_url` 已自动拼接该参数;admin 模式 `user_id=0`,可观测全量推送。
|
||||
- 任一身份不通过 → 服务端发送 `{"event":"ws.error","code":1101,"message":"Authentication failed: ..."}` 并立即 `close`。
|
||||
- **服务端按 user_id 过滤(user 级主题)**:以下 topic 的 `data.user_id` 必须 **等于** 当前连接绑定的 `user_id` 才会下发——**`bet.win` / `user.streak` / `wallet.changed` / `bet.accepted` / `auto.spin.progress`**。其它 topic(`period.tick` / `period.opened` / `jackpot.hit` / `admin.*` 等)按订阅广播。admin 模式不参与此过滤。
|
||||
@@ -918,13 +955,14 @@ php scripts/republish_bet_win.php --period-no=20260526-183418-c9c90ef1
|
||||
## 8. 移动端完整调用流程
|
||||
|
||||
## 8.1 首次进入游戏
|
||||
1. `GET /api/v1/authToken?secret=xxx×tamp=xxx&device_id=xxx&signature=xxx` 获取 `auth-token`
|
||||
2. `POST /api/user/login` 登录(请求头带 `auth-token`)
|
||||
3. `POST /api/game/lobbyInit` 拉首页初始化(请求头带 `auth-token`)
|
||||
1. `GET /api/v1/authToken?secret=xxx×tamp=xxx&device_id=xxx&signature=xxx` 获取 `auth-token`(`device_id` 须为客户端稳定设备码)
|
||||
2. `POST /api/user/login` 登录(请求头带 `auth-token` + 登录成功后带 `user-token`)
|
||||
3. `POST /api/game/lobbyInit` 拉首页初始化(请求头带 `auth-token` + `user-token`)
|
||||
4. 取得 WebSocket 基址(**当前非 lobbyInit 下发**:与运维/打包配置中的 `H5_WEBSOCKET_URL` 或自建配置接口一致)后建立 WebSocket 连接,**立即发送 `subscribe`** 监听状态流(见 §7.0 / §7.1;**务必包含 `bet.win`**)
|
||||
5. 用户下注调用 `POST /api/game/placeBet`
|
||||
6. 下单后以 `placeBet.balance_after` 与 `wallet.changed` 同步余额;开奖结算后监听 **`bet.win`**(`is_win=true`)展示中奖,大奖档看 `data.is_jackpot`(连接已绑定用户,载荷无 `user_id`)
|
||||
7. 断线或页面回前台时,重连 WebSocket 并重新订阅主题回补实时状态
|
||||
8. 用户主动退出:`POST /api/user/logout`(请求头 `auth-token` + `user-token`,body 可选 `refresh_token`),成功后清除本地 token 并关闭 WebSocket
|
||||
|
||||
## 8.2 充值到下注到提现闭环
|
||||
1. 拉取档位:`POST /api/finance/depositTierList`(玩家选择一档,并记下该档 `channels[].code`)
|
||||
|
||||
@@ -6,6 +6,8 @@ Scope: **platform-wide single period number and single draw result**; channels a
|
||||
|
||||
**Addendum (2026-04)**: §**1.5** describes server-side **Redis hot-spot caching** (`GameHotDataRedis`). It does **not** change any interface URL, parameter, or response field contracts; it is for integration testing and operations reference only.
|
||||
|
||||
**Addendum (2026-05)**: §**1.3** / §**1.4** / §**2.3** document **single-device login** and **`POST /api/user/logout`** (Redis-backed `device_id` binding).
|
||||
|
||||
## 1. Design Conventions
|
||||
|
||||
### 1.1 Base Conventions
|
||||
@@ -64,6 +66,11 @@ Scope: **platform-wide single period number and single draw result**; channels a
|
||||
### 1.3 Authentication
|
||||
- **API auth (`auth-token`)**: all mobile business APIs must send header `auth-token` (issued by `/api/v1/authToken`)
|
||||
- **User session (`user-token`)**: login-protected APIs send header `user-token`; on expiry, refresh or log in again
|
||||
- **Single-device session (server rule)**:
|
||||
- `device_id` in the `authToken` query is bound to that `auth-token` in Redis
|
||||
- Each account allows **one active device** only: a successful login on a new device clears other `user-token` / `refresh_token` rows and binds the new `device_id`
|
||||
- Old devices calling login-protected APIs get `code=1101` with message like “logged in on another device, please sign in again” (`lang=en` for English)
|
||||
- Clients must use a **stable unique** `device_id` per install (do not randomize on every launch)
|
||||
|
||||
### 1.4 Obtain API Auth Token (`auth-token`)
|
||||
- **GET** `/api/v1/authToken`
|
||||
@@ -88,6 +95,10 @@ Response parameters:
|
||||
- `expires_in`: int (TTL in seconds)
|
||||
- `server_time`: int (server timestamp for clock sync)
|
||||
|
||||
Server behavior (single-device):
|
||||
- On success, stores `device_id` for this `auth_token` in Redis with TTL = `expires_in`
|
||||
- Login, login-protected HTTP APIs, and WebSocket handshake compare this `device_id` with the user’s active device
|
||||
|
||||
Possible error codes:
|
||||
- `1001` missing parameter
|
||||
- `1002` invalid parameter format
|
||||
@@ -129,13 +140,13 @@ Possible error codes:
|
||||
|
||||
### 2.1 Register
|
||||
- **POST** `/api/user/register`
|
||||
- Purpose: phone-only registration with invite attribution (admin/channel)
|
||||
- Purpose: phone-only registration with invite attribution (admin/channel); auto-login on success
|
||||
- Headers: `auth-token` required (device id is taken from that token’s bound `device_id`; see §1.3)
|
||||
|
||||
Request parameters:
|
||||
- `username`: string, phone number (registration account; mainland China mobile only)
|
||||
- `password`: string, plaintext over HTTPS (login password; server stores salted hash)
|
||||
- `invite_code`: string, required (sub-agent invite code; binds `channel_id` and ownership)
|
||||
- `device_id`: string, optional (device id for risk control and login logs)
|
||||
|
||||
Response parameters:
|
||||
- `user-token`: string (session token for login-protected APIs)
|
||||
@@ -150,11 +161,14 @@ Response parameters:
|
||||
|
||||
### 2.2 Login
|
||||
- **POST** `/api/user/login`
|
||||
- Headers: `auth-token` required (bound `device_id` is the login device; **do not** send `device_id` in body)
|
||||
|
||||
Request parameters:
|
||||
- `username`: string (login account; currently phone)
|
||||
- `password`: string (login password)
|
||||
- `device_id`: string, optional (risk assist)
|
||||
|
||||
Server behavior:
|
||||
- On success, clears other sessions for this user and sets active device to the `auth-token`’s `device_id` (§1.3)
|
||||
|
||||
Response parameters:
|
||||
- `user-token`: string (access token for login-protected APIs)
|
||||
@@ -167,7 +181,29 @@ Response parameters:
|
||||
- `channel_id`: int (attribution channel id)
|
||||
- `risk_flags`: int (risk status bitmask)
|
||||
|
||||
### 2.3 Current User Profile
|
||||
### 2.3 Logout
|
||||
- **POST** `/api/user/logout`
|
||||
- Purpose: sign out, invalidate session, release single-device lock
|
||||
|
||||
Headers:
|
||||
- `auth-token`: required
|
||||
- `user-token`: required when logged in
|
||||
|
||||
Request parameters (optional):
|
||||
- `refresh_token`: string (recommended; server deletes it; if omitted while logged in, server tries current session refresh token)
|
||||
|
||||
Response:
|
||||
- `data`: empty object `{}`
|
||||
|
||||
Server behavior:
|
||||
- Deletes current `user-token`, clears Redis active `device_id` for the user
|
||||
- Deletes `refresh_token` when provided
|
||||
- **Idempotent**: returns `code=1` even if already logged out (client can clear local storage)
|
||||
|
||||
Possible error codes:
|
||||
- `1101`: missing or invalid `auth-token` (same as §1.4)
|
||||
|
||||
### 2.4 Current User Profile
|
||||
- **POST** `/api/user/profile`
|
||||
|
||||
Response parameters (amount fields as 2-decimal strings, aligned with wallet display):
|
||||
@@ -204,8 +240,9 @@ Response parameters (amount fields as 2-decimal strings, aligned with wallet dis
|
||||
- `count`: int (pending-review withdraw orders)
|
||||
- `max`: int (max pending-review withdraws per user, currently `3`; exceeds → `withdrawCreate` returns `code=2004`)
|
||||
|
||||
### 2.4 Refresh Token (Optional)
|
||||
### 2.5 Refresh Token (Optional)
|
||||
- **POST** `/api/user/refreshToken`
|
||||
- Headers: `auth-token` required; active `device_id` must match (§1.3)
|
||||
|
||||
Request parameters:
|
||||
- `refresh_token`: string (credential to renew access token)
|
||||
@@ -680,7 +717,7 @@ Aligned with `app/process/GameWebSocketServer.php`, `config/process.php`, `app/c
|
||||
- **Mixed content**: HTTPS pages require **`wss://`**.
|
||||
- **Redis event bus**: HTTP uses **`GameWebSocketEventBus`** (Redis list); if Redis down, broadcast pushes may fail except **`admin.live.snapshot`** has per-second direct snapshot fallback.
|
||||
- **Handshake auth (2026-05, mandatory)**: `GameWebSocketServer::onWebSocketConnect` via `GameWebSocketAuthHelper::authorize` on URL query:
|
||||
- **mobile**: query **`auth-token`** + **`user-token`** (hyphenated; legacy `auth_token`/`user_token` parsed but **use hyphens for new clients**). Binds `user_id`; user topics only to owner.
|
||||
- **mobile**: query **`auth-token`** + **`user-token`** (hyphenated; legacy `auth_token`/`user_token` parsed but **use hyphens for new clients**). Binds `user_id`; user topics only to owner. Also validates `auth-token`’s bound `device_id` against the user’s active device (§1.3); other-device sessions are rejected.
|
||||
- **admin**: query **`admin-ws-token`** (from admin `wsConfig`, Redis, TTL 7200s). `user_id=0`, full observability.
|
||||
- Failure → `{"event":"ws.error","code":1101,"message":"Authentication failed: ..."}` then `close`.
|
||||
- **Server filter (user topics)**: `bet.win`, `user.streak`, `wallet.changed`, `bet.accepted`, `auto.spin.progress` only if `data.user_id` equals connection `user_id`. Others broadcast by subscription. Admin mode not filtered.
|
||||
@@ -811,13 +848,14 @@ Integration: `php scripts/verify_ws_topic_subscribe.php`.
|
||||
## 8. Mobile End-to-End Call Flows
|
||||
|
||||
## 8.1 First Game Entry
|
||||
1. `GET /api/v1/authToken?secret=xxx×tamp=xxx&device_id=xxx&signature=xxx` → `auth-token`
|
||||
2. `POST /api/user/login` (header `auth-token`)
|
||||
3. `POST /api/game/lobbyInit` (header `auth-token`)
|
||||
1. `GET /api/v1/authToken?secret=xxx×tamp=xxx&device_id=xxx&signature=xxx` → `auth-token` (stable `device_id` per install)
|
||||
2. `POST /api/user/login` (headers `auth-token`; after login also `user-token`)
|
||||
3. `POST /api/game/lobbyInit` (headers `auth-token` + `user-token`)
|
||||
4. WebSocket base URL (**not in lobbyInit today**: ops/bundle `H5_WEBSOCKET_URL` or custom config) → connect, **send `subscribe` immediately** (§7.0/7.1; **include `bet.win`**)
|
||||
5. `POST /api/game/placeBet`
|
||||
6. Balance: `placeBet.balance_after` + `wallet.changed`; after draw, **`bet.win`** (`is_win=true`), jackpot style via `data.is_jackpot` (no `user_id` in payload)
|
||||
7. On disconnect/foreground, reconnect and resubscribe
|
||||
8. User logout: `POST /api/user/logout` (headers `auth-token` + `user-token`, optional body `refresh_token`); clear local tokens and close WebSocket
|
||||
|
||||
## 8.2 Deposit → Bet → Withdraw
|
||||
1. `POST /api/finance/depositTierList` (pick tier + `channels[].code`)
|
||||
|
||||
Reference in New Issue
Block a user