Files
webman-buildadmin/docs/en/36zihua-mobile-api-design-draft.md
2026-05-29 12:01:00 +08:00

46 KiB
Raw Blame History

36 Zihua Mobile API Design Draft (V1)

This document is based on docs/36字花-数据库与实施计划.md and the PRD. It provides an initial mobile-facing API inventory and field definitions.

Scope: platform-wide single period number and single draw result; channels are used only for attribution, commission sharing, and risk control—not for splitting game rounds.

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.

1. Design Conventions

1.1 Base Conventions

  • Protocol: HTTPS + JSON
  • Path naming: /api/{module}/{action}, must match regex ^/api/[a-z]+/[a-z]+[A-Z][a-zA-Z]*$
  • HTTP methods: All mobile business APIs (/api/*, excluding /api/v1/authToken) use POST. Query-style endpoints also accept GET (for browser/debug tools); clients should uniformly use POST.
    • For POST, header Content-Type: application/json; parameters in JSON body
    • In GET compatibility mode, parameters go in the URL query string
    • Exception: Notice module /api/notice/noticeList, /api/notice/noticeConfirm support GET only; parameters always use URL query string
    • Notice list without auth: GET /api/notice/noticeList does not require auth-token or user-token; if user-token is still sent, popout items can return the users real is_read
    • Auth endpoint /api/v1/authToken remains GET
  • Time: UTC timestamp (seconds) + server timezone configuration
  • Amounts: numeric transport (e.g. "100.00"); client display uses two decimal places (storage remains decimal(18,2))
  • Idempotency: critical write APIs require idempotency_key
  • Required headers:
    • auth-token: API auth token from GET /api/v1/authToken (signature-based API access credential)
    • user-token: user session token; required on login-protected APIs
  • Language header:
    • lang=zh: Chinese (default)
    • lang=en: English

1.2 Common Response Shape

{
  "code": 1,
  "message": "ok",
  "data": {}
}
  • code=1 means success; any other value is a business error

  • All /api/* response messages support Chinese and English: default Chinese; lang=en returns English, lang=zh returns Chinese

  • Suggested error code ranges (by nature):

    • 1000-1099: parameter errors (missing fields, type errors, format errors, out of range)
    • 1100-1199: auth errors (not logged in, token expired, insufficient permission)
    • 2000-2999: business errors (insufficient balance, round not found, order not found, notice not found)
    • 3000-3099: flow errors (illegal flow/state, e.g. bet after lock, duplicate confirm, illegal state transition)
    • 5000-5999: system errors (service exception, dependency timeout, unknown error)
  • Recommended base error codes (first release):

  • 1: success

    • 1001: missing parameter
    • 1002: invalid parameter format
    • 1003: illegal parameter value
    • 1101: not logged in or session expired
    • 1103: no permission
    • 2001: insufficient balance
    • 2002: round not found
    • 2003: order not found
    • 2004: notice not found
    • 3001: operation not allowed in current flow
    • 3002: betting closed, bets not allowed
    • 3003: duplicate request (idempotency conflict)
    • 5000: system busy, try again later

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

1.4 Obtain API Auth Token (auth-token)

  • GET /api/v1/authToken
  • Purpose: obtain auth-token (required on all API request headers)

Request example: /api/v1/authToken?secret=564d14asdasd113e46542asd6das1a2a&timestamp=1776331077&device_id=1&signature=AD84C49880896DBC16C59C7B122D1FF7

Request parameters:

  • secret: string (client secret; server validates against env AUTH_TOKEN_SECRET)
  • timestamp: int (request timestamp; server allows ±300 seconds from server time)
  • device_id: string (device id)
  • signature: string (signature value)

Signature algorithm:

  • Parameters in signature (excluding signature): device_id, secret, timestamp
  • Sort parameter names a-z, concatenate as key=value&key=value...
  • Compute: signature = strtoupper(md5(concatenated string))

Response parameters:

  • auth_token: string (API auth token; put in header auth-token)
  • expires_in: int (TTL in seconds)
  • server_time: int (server timestamp for clock sync)

Possible error codes:

  • 1001 missing parameter
  • 1002 invalid parameter format
  • 1103 invalid secret / signature error
  • 3001 invalid timestamp

1.5 Server Performance & Redis Hot-Spot Cache (Implementation Notes)

No client contract change: request paths, parameters, response JSON shape, and error codes are unchanged by caching; this section only explains how the server reduces latency, read paths, and consistency notes.

Difference from “framework file cache”

Config Scope
CACHE_DRIVER (config/cache.php, e.g. file) Think-ORM / get_sys_config() etc. system config table model cache under runtime/cache; not used on this game hot-path.
GAME_HOT_CACHE_* (config/game_hot_cache.php) Game user / game_config / game_record row-level JSON cache via support\Redis (config/redis.php), key prefix dfw:v1:.

Server cache coverage (read paths relevant to mobile)

  • User: member auth prefers Redis user row snapshot; on miss, DB then backfill. After balance, streak, bet-flow, etc. change and commit, GameHotDataCoordinator::afterUserCommitted($userId): GameHotDataRedis::userReplaceCacheFromDb aligns with DB, then enqueues idempotent refresh tasks (GameHotDataWriteQueue / GameHotDataQueueConsumer) for peak shaving—not a substitute for synchronous reload.
  • Game config: game_config cached by config_key. Direct Db updates in admin must call GameHotDataCoordinator::afterGameConfigKeyCommitted($key) (model GameConfig events and standalone form controllers wired); standalone save uses GameHotDataLock (TYPE_GAME_CONFIG) per config_key. Do not only delete cache keys without reload—max inconsistency window is TTL.
  • Round: active round, round by id, latest game_record, etc.; after write, GameHotDataCoordinator::afterGameRecordCommitted refreshes Redis keys and enqueues. Draw/lock paths may use GameHotDataLock (TYPE_GAME_RECORD) per record id.

Environment variables (see repo root .env-example)

  • GAME_HOT_CACHE_ENABLED: enable Redis hot cache (false = always DB).
  • GAME_HOT_CACHE_TTL_GAME_CONFIG / GAME_HOT_CACHE_TTL_GAME_RECORD / GAME_HOT_CACHE_TTL_USER: TTL seconds; write-then-sync reload is primary, TTL is fallback only.
  • GAME_HOT_CACHE_ENABLE_WRITE_QUEUE and queue length, consumer interval, etc.: idempotent refresh after write (config/game_hot_cache.php).

Consistency notes (integration / testing)

  • Scripts that bypass coordinator and only change DB without GameHotDataCoordinator may be briefly inconsistent with Redis; avoid in production.
  • POST /api/game/betPlace debit path uses the same per-user Redis lock as admin wallet adjust (GameHotDataRedis::userAdminMutationLockTry) and WHERE coin = ? conditional update, mutually exclusive with concurrent payout/admin adjust; on failure returns Chinese messages listed in §4.2.
  • Clients may still use §3.2 dictionaryList version for local cache; server dictionary also has Redis acceleration—both can coexist.

2. Auth & Account Module (user)

2.1 Register

  • POST /api/user/register
  • Purpose: phone-only registration with invite attribution (admin/channel)

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)
  • refresh_token: string, optional (refresh access token)
  • expires_in: int (seconds, token TTL)
  • user: object (non-sensitive fields only; no id)
    • uuid: string (public user id, 10 chars)
    • username: string (nickname/display name)
    • coin: string (current balance)
    • channel_id: int (attribution channel id)
    • risk_flags: int (risk status bitmask)

2.2 Login

  • POST /api/user/login

Request parameters:

  • username: string (login account; currently phone)
  • password: string (login password)
  • device_id: string, optional (risk assist)

Response parameters:

  • user-token: string (access token for login-protected APIs)
  • refresh_token: string, optional
  • expires_in: int (access token remaining seconds)
  • user: object (non-sensitive fields only; no id)
    • uuid: string (public user id, 10 chars)
    • username: string (nickname/display name)
    • coin: string (current balance)
    • channel_id: int (attribution channel id)
    • risk_flags: int (risk status bitmask)

2.3 Current User Profile

  • POST /api/user/profile

Response parameters (amount fields as 2-decimal strings, aligned with wallet display):

Basic profile

  • uuid: string (public user id, 10 chars)
  • username: string (nickname)
  • head_image: string (avatar URL)
  • phone: string (mobile)
  • email: string (email)
  • register_invite_code: string (invite code snapshot at registration)
  • channel_id: int (attribution channel id)
  • risk_flags: int (risk status bitmask)
  • current_streak: int (current win streak count)
  • last_bet_period_no: string (period no of last valid bet)
  • create_time: int (registration timestamp)

Funds & withdraw quota

  • coin / coin_balance: string (current balance; same value)
  • frozen_balance: string (frozen balance; fixed 0.00 when none)
  • total_deposit_coin: string (lifetime deposits)
  • total_withdraw_coin: string (lifetime withdraws; incremented after acceptance)
  • bet_flow_coin: string (bet flow / turnover; after settlement, +total_amount per bet 1:1)
  • max_withdrawable: string (max single withdraw allowed now = min(coin_balance, max_withdraw_by_flow))
  • withdraw_flow: object (bet-flow / withdraw quota diagnostic snapshot; includes pending_withdraw)
    • ratio: string (bet-flow multiplier; 0 = unlimited)
    • net_deposit: string (net deposit = max(0, total deposit total withdraw))
    • required_bet_flow: string (required bet flow by threshold rule; display only)
    • remaining_bet_flow: string (remaining bet flow by threshold; display only)
    • eligible: bool (meets overall threshold; display only; actual gate is max_withdrawable)
    • max_withdraw_by_flow: string/null (cap from bet flow only; null when ratio=0)
    • flow_unlimited: bool (unlimited bet-flow mode)
    • pending_withdraw: object
      • 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)

  • POST /api/user/refreshToken

Request parameters:

  • refresh_token: string (credential to renew access token)

Response parameters:

  • user-token: string (new access token)
  • expires_in: int (new token TTL)

3. Game Lobby & Dictionary Module (game/lobby)

3.1 Lobby Init

  • POST /api/game/lobbyInit
  • Purpose: one-shot current round, config, 36 zihua dictionary, user quick display

Response parameters:

  • server_time: int (server time for client clock sync)
  • runtime_enabled: bool (game runtime switch; false = admin maintenance—no new bets, idle wont auto-create periods, post-payout wont auto-create next; current open round still draws, pays out, and settles. Mobile should disable bet entry and show maintenance copy)
  • period: object
    • period_no: string (global period number)
    • status: string (betting/locked/settling/finished/void; void = period voided)
    • countdown: int (countdown seconds)
    • lock_at: int (lock timestamp)
    • open_at: int (expected draw timestamp)
  • bet_config: object
    • pick_max_number_count: int (max numbers per bet; from game_config.config_key = pick_max_number_count; default matches seed, usually 10; range 136)
    • chips: object (quick chip map; fixed keys "1""6", values are per-number stake strings, 2 decimals; same as admin game_config.bet_chips)
    • default_bet_chip_id: int (default chip id from game_config.default_bet_chip_id; invalid → first valid chip)
    • min_bet_per_number: string (min per number; ≤ selected chip and admin limits)
    • max_bet_per_number: string (max per number)
  • dictionary: array