swagger: "2.0" info: title: HY Gateway Service API version: "1.0.0" description: | gateway-service 对外暴露 HTTP + JSON API。 `/api/v1/**` 业务接口统一返回 `code/message/request_id/data` 外壳,并通过 `X-Request-ID` 贯穿 HTTP 响应和内部 gRPC RequestMeta。 `/healthz/**` 是健康检查接口,不使用业务响应外壳。 host: localhost:13000 basePath: / schemes: - http consumes: - application/json produces: - application/json tags: - name: health description: Gateway 进程和依赖探测。 - name: auth description: 登录、刷新、登出和密码初始化。 - name: users description: 当前展示短号查询和变更。 - name: rooms description: 房间命令入口,最终由 room-service 执行。 securityDefinitions: BearerAuth: type: apiKey name: Authorization in: header description: 使用 `Bearer `;JWT 使用 HS256,claims 必须包含数值型 `user_id`。 parameters: RequestIDHeader: name: X-Request-ID in: header required: false type: string description: 可选追踪 ID;未传时 gateway 自动生成,并返回在响应 header 和 JSON `request_id`。 DisplayUserIDPath: name: display_user_id in: path required: true type: string description: 当前有效展示短号。 responses: BadRequest: description: 请求体不是合法 JSON,或下游返回非法参数;常见 `code` 为 `INVALID_JSON`、`INVALID_ARGUMENT`、`DISPLAY_USER_ID_INVALID`。 schema: $ref: "#/definitions/ErrorEnvelope" Unauthorized: description: 未通过 access token 校验,或下游返回认证失败。 schema: $ref: "#/definitions/ErrorEnvelope" Forbidden: description: 已认证但被禁用、封禁或无权限。 schema: $ref: "#/definitions/ErrorEnvelope" NotFound: description: 下游实体不存在。 schema: $ref: "#/definitions/ErrorEnvelope" Conflict: description: 下游唯一键、状态、租约或冷却期冲突。 schema: $ref: "#/definitions/ErrorEnvelope" Internal: description: gateway 或下游返回不可细分的内部错误。 schema: $ref: "#/definitions/ErrorEnvelope" UpstreamError: description: gateway 调内部服务失败,或内部服务返回不可映射错误。 schema: $ref: "#/definitions/ErrorEnvelope" paths: /healthz/live: get: tags: - health summary: 进程存活探测 operationId: gatewayLive responses: "200": description: Gateway HTTP runtime 正在服务。 schema: $ref: "#/definitions/HealthResponse" "500": description: Gateway HTTP runtime 不可用。 schema: $ref: "#/definitions/HealthResponse" /healthz/ready: get: tags: - health summary: 接流量就绪探测 operationId: gatewayReady responses: "200": description: Gateway 可接新请求。 schema: $ref: "#/definitions/HealthResponse" "503": description: Gateway 不应接新请求,失败详情在 `checks`。 schema: $ref: "#/definitions/HealthResponse" /api/v1/auth/password/login: post: tags: - auth summary: 短号密码登录 operationId: loginPassword parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/LoginPasswordRequest" responses: "200": description: 登录成功,`data` 返回 token 集合。 schema: $ref: "#/definitions/AuthTokenEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/auth/third-party/login: post: tags: - auth summary: 三方登录或注册 operationId: loginThirdParty parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/LoginThirdPartyRequest" responses: "200": description: 登录或注册成功,`data` 返回 token 集合和 `is_new_user`。 schema: $ref: "#/definitions/AuthTokenEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/auth/password/set: post: tags: - auth summary: 已登录用户首次设置密码 operationId: setPassword security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/SetPasswordRequest" responses: "200": description: 密码设置成功。 schema: $ref: "#/definitions/PasswordSetEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/auth/token/refresh: post: tags: - auth summary: 刷新 token operationId: refreshToken parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/RefreshTokenRequest" responses: "200": description: 刷新成功,`data` 返回新的 token 集合。 schema: $ref: "#/definitions/AuthTokenEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/auth/logout: post: tags: - auth summary: 登出并失效 refresh session operationId: logout parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/LogoutRequest" responses: "200": description: 登出处理完成。 schema: $ref: "#/definitions/LogoutEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/users/by-display-user-id/{display_user_id}: get: tags: - users summary: 通过当前展示短号解析用户 operationId: resolveDisplayUserID parameters: - $ref: "#/parameters/RequestIDHeader" - $ref: "#/parameters/DisplayUserIDPath" responses: "200": description: 解析成功,`data` 返回短号归属。 schema: $ref: "#/definitions/UserIdentityEnvelope" "400": $ref: "#/responses/BadRequest" "404": $ref: "#/responses/NotFound" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/users/me/identity: get: tags: - users summary: 查询当前用户展示短号状态 operationId: getMyIdentity security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" responses: "200": description: 查询成功,`data` 返回当前用户短号状态。 schema: $ref: "#/definitions/UserIdentityEnvelope" "401": $ref: "#/responses/Unauthorized" "404": $ref: "#/responses/NotFound" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/users/me/display-id/change: post: tags: - users summary: 修改当前用户默认短号 operationId: changeMyDisplayUserID security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/ChangeDisplayUserIDRequest" responses: "200": description: 修改成功,`data` 返回最新短号状态。 schema: $ref: "#/definitions/UserIdentityEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/users/me/display-id/pretty/apply: post: tags: - users summary: 申请临时靓号 operationId: applyMyPrettyDisplayUserID security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/ApplyPrettyDisplayUserIDRequest" responses: "200": description: 申请成功,`data` 返回最新短号状态和租约 ID。 schema: $ref: "#/definitions/UserIdentityEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/create: post: tags: - rooms summary: 创建房间 operationId: createRoom security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/CreateRoomRequest" responses: "200": description: 创建成功,`data` 返回房间快照。 schema: $ref: "#/definitions/CreateRoomEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/join: post: tags: - rooms summary: 加入房间 operationId: joinRoom security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/JoinRoomRequest" responses: "200": description: 加入成功,`data` 返回用户房间态和房间快照。 schema: $ref: "#/definitions/JoinRoomEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/leave: post: tags: - rooms summary: 离开房间 operationId: leaveRoom security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/LeaveRoomRequest" responses: "200": description: 离房成功,`data` 返回最新房间快照。 schema: $ref: "#/definitions/LeaveRoomEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/mic/up: post: tags: - rooms summary: 上麦 operationId: micUp security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/MicUpRequest" responses: "200": description: 上麦成功,`data` 返回目标麦位和最新房间快照。 schema: $ref: "#/definitions/MicUpEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/mic/down: post: tags: - rooms summary: 下麦 operationId: micDown security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/MicDownRequest" responses: "200": description: 下麦成功,`data` 返回目标麦位和最新房间快照。 schema: $ref: "#/definitions/MicDownEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/mic/change: post: tags: - rooms summary: 调整麦位 operationId: changeMicSeat security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/ChangeMicSeatRequest" responses: "200": description: 调整成功,`data` 返回最新房间快照。 schema: $ref: "#/definitions/ChangeMicSeatEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/user/mute: post: tags: - rooms summary: 设置房间内禁言 operationId: muteUser security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/MuteUserRequest" responses: "200": description: 禁言状态修改成功,`data` 返回最新房间快照。 schema: $ref: "#/definitions/MuteUserEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/user/kick: post: tags: - rooms summary: 踢出房间用户 operationId: kickUser security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/KickUserRequest" responses: "200": description: 踢人成功,`data` 返回最新房间快照。 schema: $ref: "#/definitions/KickUserEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" /api/v1/rooms/gift/send: post: tags: - rooms summary: 发送礼物 operationId: sendGift security: - BearerAuth: [] parameters: - $ref: "#/parameters/RequestIDHeader" - name: body in: body required: true schema: $ref: "#/definitions/SendGiftRequest" responses: "200": description: 送礼成功,`data` 返回账单凭证、热度、本地礼物榜和房间快照。 schema: $ref: "#/definitions/SendGiftEnvelope" "400": $ref: "#/responses/BadRequest" "401": $ref: "#/responses/Unauthorized" "403": $ref: "#/responses/Forbidden" "404": $ref: "#/responses/NotFound" "409": $ref: "#/responses/Conflict" "500": $ref: "#/responses/Internal" "502": $ref: "#/responses/UpstreamError" definitions: GatewayOKEnvelopeBase: type: object required: - code - message - request_id properties: code: type: string enum: - OK message: type: string enum: - ok request_id: type: string description: gateway 业务 API 成功响应外壳。 ErrorEnvelope: type: object required: - code - message - request_id properties: code: type: string enum: - INVALID_JSON - UNAUTHORIZED - UPSTREAM_ERROR - INTERNAL_ERROR - INVALID_ARGUMENT - AUTH_FAILED - SESSION_EXPIRED - SESSION_REVOKED - PASSWORD_ALREADY_SET - DISPLAY_USER_ID_INVALID - DISPLAY_USER_ID_EXISTS - DISPLAY_USER_ID_COOLDOWN - DISPLAY_USER_ID_NOT_FOUND - DISPLAY_USER_ID_PRETTY_NOT_AVAILABLE - DISPLAY_USER_ID_PRETTY_ACTIVE - DISPLAY_USER_ID_LEASE_EXPIRED - DISPLAY_USER_ID_PAYMENT_REQUIRED - USER_DISABLED - PERMISSION_DENIED - NOT_FOUND message: type: string enum: - invalid request body - unauthorized - upstream service error - internal error - invalid argument - authentication failed - conflict - permission denied - not found request_id: type: string description: gateway 业务 API 失败响应外壳;失败响应不返回 `data`。 HealthResponse: type: object properties: status: type: string enum: - ok - fail service: type: string example: gateway-service node_id: type: string time_unix_ms: type: integer format: int64 checks: type: array items: $ref: "#/definitions/HealthCheck" HealthCheck: type: object properties: name: type: string example: room_grpc status: type: string enum: - ok - fail message: type: string LoginPasswordRequest: type: object properties: display_user_id: type: string password: type: string device_id: type: string LoginThirdPartyRequest: type: object properties: provider: type: string example: wechat credential: type: string device_id: type: string SetPasswordRequest: type: object properties: password: type: string RefreshTokenRequest: type: object properties: refresh_token: type: string device_id: type: string LogoutRequest: type: object properties: refresh_token: type: string session_id: type: string AuthTokenData: type: object properties: user_id: type: integer format: int64 display_user_id: type: string default_display_user_id: type: string display_user_id_kind: type: string display_user_id_expires_at_ms: type: integer format: int64 session_id: type: string access_token: type: string refresh_token: type: string expires_in_sec: type: integer format: int64 token_type: type: string example: Bearer is_new_user: type: boolean PasswordSetData: type: object properties: password_set: type: boolean LogoutData: type: object properties: revoked: type: boolean UserIdentityData: type: object properties: user_id: type: integer format: int64 display_user_id: type: string status: type: string default_display_user_id: type: string display_user_id_kind: type: string display_user_id_expires_at_ms: type: integer format: int64 lease_id: type: string ChangeDisplayUserIDRequest: type: object properties: new_display_user_id: type: string reason: type: string ApplyPrettyDisplayUserIDRequest: type: object properties: pretty_display_user_id: type: string lease_duration_ms: type: integer format: int64 payment_receipt_id: type: string CreateRoomRequest: type: object properties: room_id: type: string owner_user_id: type: integer format: int64 host_user_id: type: integer format: int64 seat_count: type: integer format: int32 mode: type: string JoinRoomRequest: type: object properties: room_id: type: string role: type: string LeaveRoomRequest: type: object properties: room_id: type: string MicUpRequest: type: object properties: room_id: type: string seat_no: type: integer format: int32 MicDownRequest: type: object properties: room_id: type: string target_user_id: type: integer format: int64 ChangeMicSeatRequest: type: object properties: room_id: type: string target_user_id: type: integer format: int64 seat_no: type: integer format: int32 MuteUserRequest: type: object properties: room_id: type: string target_user_id: type: integer format: int64 muted: type: boolean KickUserRequest: type: object properties: room_id: type: string target_user_id: type: integer format: int64 SendGiftRequest: type: object properties: room_id: type: string target_user_id: type: integer format: int64 gift_id: type: string gift_count: type: integer format: int32 gift_unit_value: type: integer format: int64 CommandResult: type: object properties: applied: type: boolean room_version: type: integer format: int64 server_time_ms: type: integer format: int64 RoomUser: type: object properties: user_id: type: integer format: int64 role: type: string joined_at_ms: type: integer format: int64 last_seen_at_ms: type: integer format: int64 SeatState: type: object properties: seat_no: type: integer format: int32 user_id: type: integer format: int64 locked: type: boolean RankItem: type: object properties: user_id: type: integer format: int64 score: type: integer format: int64 gift_value: type: integer format: int64 updated_at_ms: type: integer format: int64 RoomSnapshot: type: object properties: room_id: type: string owner_user_id: type: integer format: int64 host_user_id: type: integer format: int64 mode: type: string status: type: string chat_enabled: type: boolean mic_seats: type: array items: $ref: "#/definitions/SeatState" online_users: type: array items: $ref: "#/definitions/RoomUser" admin_user_ids: type: array items: type: integer format: int64 ban_user_ids: type: array items: type: integer format: int64 mute_user_ids: type: array items: type: integer format: int64 gift_rank: type: array items: $ref: "#/definitions/RankItem" room_ext: type: object additionalProperties: type: string heat: type: integer format: int64 version: type: integer format: int64 CreateRoomResponse: type: object properties: result: $ref: "#/definitions/CommandResult" room: $ref: "#/definitions/RoomSnapshot" JoinRoomResponse: type: object properties: result: $ref: "#/definitions/CommandResult" user: $ref: "#/definitions/RoomUser" room: $ref: "#/definitions/RoomSnapshot" LeaveRoomResponse: type: object properties: result: $ref: "#/definitions/CommandResult" room: $ref: "#/definitions/RoomSnapshot" MicUpResponse: type: object properties: result: $ref: "#/definitions/CommandResult" seat_no: type: integer format: int32 room: $ref: "#/definitions/RoomSnapshot" MicDownResponse: type: object properties: result: $ref: "#/definitions/CommandResult" seat_no: type: integer format: int32 room: $ref: "#/definitions/RoomSnapshot" ChangeMicSeatResponse: type: object properties: result: $ref: "#/definitions/CommandResult" room: $ref: "#/definitions/RoomSnapshot" MuteUserResponse: type: object properties: result: $ref: "#/definitions/CommandResult" room: $ref: "#/definitions/RoomSnapshot" KickUserResponse: type: object properties: result: $ref: "#/definitions/CommandResult" room: $ref: "#/definitions/RoomSnapshot" SendGiftResponse: type: object properties: result: $ref: "#/definitions/CommandResult" billing_receipt_id: type: string room_heat: type: integer format: int64 gift_rank: type: array items: $ref: "#/definitions/RankItem" room: $ref: "#/definitions/RoomSnapshot" AuthTokenEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/AuthTokenData" PasswordSetEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/PasswordSetData" LogoutEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/LogoutData" UserIdentityEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/UserIdentityData" CreateRoomEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/CreateRoomResponse" JoinRoomEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/JoinRoomResponse" LeaveRoomEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/LeaveRoomResponse" MicUpEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/MicUpResponse" MicDownEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/MicDownResponse" ChangeMicSeatEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/ChangeMicSeatResponse" MuteUserEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/MuteUserResponse" KickUserEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/KickUserResponse" SendGiftEnvelope: allOf: - $ref: "#/definitions/GatewayOKEnvelopeBase" - type: object properties: data: $ref: "#/definitions/SendGiftResponse"