swagger: "2.0"
info:
  title: HY User Service API
  version: "1.0.0"
  description: |
    user-service 当前只暴露内部 gRPC + protobuf 接口，没有 HTTP JSON router。
    本文件用 OpenAPI 2.0 记录 gRPC 合约，`paths` 使用 gRPC full method 作为文档路径，不代表真实 HTTP endpoint。
host: localhost:13005
basePath: /
schemes:
  - http
consumes:
  - application/grpc+proto
produces:
  - application/grpc+proto
tags:
  - name: auth
    description: AuthService，登录注册和 token 生命周期。
  - name: users
    description: UserService，用户主状态读取。
  - name: user-devices
    description: UserDeviceService，App 设备推送 token 绑定和失效。
  - name: user-identity
    description: UserIdentityService，展示短号解析和变更。
  - name: country-admin
    description: CountryAdminService，国家主数据内部管理 RPC。
  - name: country-query
    description: CountryQueryService，App 注册页公开国家列表内部 RPC。
  - name: region-admin
    description: RegionAdminService，区域和国家归属内部管理 RPC。
  - name: health
    description: 标准 grpc.health.v1.Health。
responses:
  GRPCError:
    description: gRPC status error；稳定业务原因放在 google.rpc.ErrorInfo.reason。
    schema:
      $ref: "#/definitions/GRPCError"
paths:
  /hyapp.user.v1.AuthService/LoginPassword:
    post:
      tags:
        - auth
      summary: 短号密码登录
      operationId: userAuthLoginPassword
      x-grpc-full-method: /hyapp.user.v1.AuthService/LoginPassword
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/LoginPasswordRequest"
      responses:
        "200":
          description: 登录成功。
          schema:
            $ref: "#/definitions/AuthResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.AuthService/LoginThirdParty:
    post:
      tags:
        - auth
      summary: 三方登录或注册
      operationId: userAuthLoginThirdParty
      x-grpc-full-method: /hyapp.user.v1.AuthService/LoginThirdParty
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/LoginThirdPartyRequest"
      responses:
        "200":
          description: 登录或注册成功。
          schema:
            $ref: "#/definitions/AuthResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.AuthService/SetPassword:
    post:
      tags:
        - auth
      summary: 首次设置密码
      operationId: userAuthSetPassword
      x-grpc-full-method: /hyapp.user.v1.AuthService/SetPassword
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/SetPasswordRequest"
      responses:
        "200":
          description: 密码设置完成。
          schema:
            $ref: "#/definitions/SetPasswordResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.AuthService/RefreshToken:
    post:
      tags:
        - auth
      summary: 刷新 token
      operationId: userAuthRefreshToken
      x-grpc-full-method: /hyapp.user.v1.AuthService/RefreshToken
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/RefreshTokenRequest"
      responses:
        "200":
          description: 刷新成功。
          schema:
            $ref: "#/definitions/RefreshTokenResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.AuthService/Logout:
    post:
      tags:
        - auth
      summary: 登出并失效 refresh session
      operationId: userAuthLogout
      x-grpc-full-method: /hyapp.user.v1.AuthService/Logout
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/LogoutRequest"
      responses:
        "200":
          description: 登出处理完成。
          schema:
            $ref: "#/definitions/LogoutResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserService/GetUser:
    post:
      tags:
        - users
      summary: 查询单个用户主状态
      operationId: userGetUser
      x-grpc-full-method: /hyapp.user.v1.UserService/GetUser
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/GetUserRequest"
      responses:
        "200":
          description: 查询成功。
          schema:
            $ref: "#/definitions/GetUserResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserService/BatchGetUsers:
    post:
      tags:
        - users
      summary: 批量查询用户主状态
      operationId: userBatchGetUsers
      x-grpc-full-method: /hyapp.user.v1.UserService/BatchGetUsers
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/BatchGetUsersRequest"
      responses:
        "200":
          description: 查询成功；缺失用户不填充占位对象。
          schema:
            $ref: "#/definitions/BatchGetUsersResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserService/ListUserIDs:
    post:
      tags:
        - users
      summary: 后台任务按 user_id 游标查询目标用户
      operationId: userListUserIDs
      x-grpc-full-method: /hyapp.user.v1.UserService/ListUserIDs
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ListUserIDsRequest"
      responses:
        "200":
          description: 返回递增 user_id 页；调用方用 next_cursor_user_id 继续。
          schema:
            $ref: "#/definitions/ListUserIDsResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserService/UpdateUserProfile:
    post:
      tags:
        - users
      summary: 修改用户基础资料
      operationId: userUpdateUserProfile
      x-grpc-full-method: /hyapp.user.v1.UserService/UpdateUserProfile
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/UpdateUserProfileRequest"
      responses:
        "200":
          description: 修改成功。
          schema:
            $ref: "#/definitions/UpdateUserProfileResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserService/ChangeUserCountry:
    post:
      tags:
        - users
      summary: 修改用户国家
      operationId: userChangeUserCountry
      description: 国家修改写 `user_country_change_logs`，同一用户按滚动 30 天冷却。
      x-grpc-full-method: /hyapp.user.v1.UserService/ChangeUserCountry
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ChangeUserCountryRequest"
      responses:
        "200":
          description: 修改成功。
          schema:
            $ref: "#/definitions/ChangeUserCountryResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserService/CompleteOnboarding:
    post:
      tags:
        - users
      summary: 完成注册页必填资料
      operationId: userCompleteOnboarding
      description: 同一事务校验并写入 username、avatar、gender、country、region_id 和 profile_completed 状态。
      x-grpc-full-method: /hyapp.user.v1.UserService/CompleteOnboarding
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/CompleteOnboardingRequest"
      responses:
        "200":
          description: 注册资料完成。
          schema:
            $ref: "#/definitions/CompleteOnboardingResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserDeviceService/BindPushToken:
    post:
      tags:
        - user-devices
      summary: 绑定当前登录用户的系统推送 token
      operationId: userBindPushToken
      description: push token 只服务离线触达，不作为登录凭证或设备身份凭证；同一 provider/token 被新设备绑定时会失效旧归属。
      x-grpc-full-method: /hyapp.user.v1.UserDeviceService/BindPushToken
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/BindPushTokenRequest"
      responses:
        "200":
          description: 绑定成功或幂等更新成功。
          schema:
            $ref: "#/definitions/BindPushTokenResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserDeviceService/DeletePushToken:
    post:
      tags:
        - user-devices
      summary: 失效当前登录用户的系统推送 token
      operationId: userDeletePushToken
      description: push_token 为空时按 user_id + device_id 失效该设备的 active token。
      x-grpc-full-method: /hyapp.user.v1.UserDeviceService/DeletePushToken
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/DeletePushTokenRequest"
      responses:
        "200":
          description: 删除成功；没有匹配 token 时返回 deleted=false。
          schema:
            $ref: "#/definitions/DeletePushTokenResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.CountryAdminService/CreateCountry:
    post:
      tags:
        - country-admin
      summary: 创建国家主数据
      operationId: userCreateCountry
      x-grpc-full-method: /hyapp.user.v1.CountryAdminService/CreateCountry
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/CreateCountryRequest"
      responses:
        "200":
          description: 创建成功。
          schema:
            $ref: "#/definitions/CountryResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.CountryAdminService/ListCountries:
    post:
      tags:
        - country-admin
      summary: 查询国家主数据
      operationId: userListCountries
      x-grpc-full-method: /hyapp.user.v1.CountryAdminService/ListCountries
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ListCountriesRequest"
      responses:
        "200":
          description: 查询成功。
          schema:
            $ref: "#/definitions/ListCountriesResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.CountryAdminService/UpdateCountry:
    post:
      tags:
        - country-admin
      summary: 修改国家展示字段
      operationId: userUpdateCountry
      x-grpc-full-method: /hyapp.user.v1.CountryAdminService/UpdateCountry
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/UpdateCountryRequest"
      responses:
        "200":
          description: 修改成功。
          schema:
            $ref: "#/definitions/CountryResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.CountryAdminService/EnableCountry:
    post:
      tags:
        - country-admin
      summary: 开放国家给 App 选择
      operationId: userEnableCountry
      x-grpc-full-method: /hyapp.user.v1.CountryAdminService/EnableCountry
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/EnableCountryRequest"
      responses:
        "200":
          description: 开放成功。
          schema:
            $ref: "#/definitions/CountryResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.CountryAdminService/DisableCountry:
    post:
      tags:
        - country-admin
      summary: 关闭国家 App 可选状态
      operationId: userDisableCountry
      description: 只写 `enabled=false`，不改变国家生命周期，不释放 region_countries 配置。
      x-grpc-full-method: /hyapp.user.v1.CountryAdminService/DisableCountry
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/DisableCountryRequest"
      responses:
        "200":
          description: 停用成功。
          schema:
            $ref: "#/definitions/CountryResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.CountryQueryService/ListRegistrationCountries:
    post:
      tags:
        - country-query
      summary: 查询 App 注册页开放国家列表
      operationId: userListRegistrationCountries
      x-grpc-full-method: /hyapp.user.v1.CountryQueryService/ListRegistrationCountries
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ListRegistrationCountriesRequest"
      responses:
        "200":
          description: 查询成功；只返回 active 且 enabled 的国家。
          schema:
            $ref: "#/definitions/ListRegistrationCountriesResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.RegionAdminService/CreateRegion:
    post:
      tags:
        - region-admin
      summary: 创建区域并配置国家列表
      operationId: userCreateRegion
      x-grpc-full-method: /hyapp.user.v1.RegionAdminService/CreateRegion
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/CreateRegionRequest"
      responses:
        "200":
          description: 创建成功。
          schema:
            $ref: "#/definitions/RegionResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.RegionAdminService/ListRegions:
    post:
      tags:
        - region-admin
      summary: 查询区域列表
      operationId: userListRegions
      x-grpc-full-method: /hyapp.user.v1.RegionAdminService/ListRegions
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ListRegionsRequest"
      responses:
        "200":
          description: 查询成功。
          schema:
            $ref: "#/definitions/ListRegionsResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.RegionAdminService/GetRegion:
    post:
      tags:
        - region-admin
      summary: 查询单个区域
      operationId: userGetRegion
      x-grpc-full-method: /hyapp.user.v1.RegionAdminService/GetRegion
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/GetRegionRequest"
      responses:
        "200":
          description: 查询成功。
          schema:
            $ref: "#/definitions/RegionResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.RegionAdminService/UpdateRegion:
    post:
      tags:
        - region-admin
      summary: 修改区域展示字段
      operationId: userUpdateRegion
      x-grpc-full-method: /hyapp.user.v1.RegionAdminService/UpdateRegion
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/UpdateRegionRequest"
      responses:
        "200":
          description: 修改成功。
          schema:
            $ref: "#/definitions/RegionResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.RegionAdminService/ReplaceRegionCountries:
    post:
      tags:
        - region-admin
      summary: 替换区域国家列表
      operationId: userReplaceRegionCountries
      x-grpc-full-method: /hyapp.user.v1.RegionAdminService/ReplaceRegionCountries
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ReplaceRegionCountriesRequest"
      responses:
        "200":
          description: 替换成功。
          schema:
            $ref: "#/definitions/RegionResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.RegionAdminService/DisableRegion:
    post:
      tags:
        - region-admin
      summary: 停用区域
      operationId: userDisableRegion
      x-grpc-full-method: /hyapp.user.v1.RegionAdminService/DisableRegion
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/DisableRegionRequest"
      responses:
        "200":
          description: 停用成功。
          schema:
            $ref: "#/definitions/RegionResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserIdentityService/GetUserIdentity:
    post:
      tags:
        - user-identity
      summary: 按 user_id 查询当前展示短号
      operationId: userGetUserIdentity
      x-grpc-full-method: /hyapp.user.v1.UserIdentityService/GetUserIdentity
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/GetUserIdentityRequest"
      responses:
        "200":
          description: 查询成功。
          schema:
            $ref: "#/definitions/GetUserIdentityResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserIdentityService/ResolveDisplayUserID:
    post:
      tags:
        - user-identity
      summary: 通过当前展示短号解析用户
      operationId: userResolveDisplayUserID
      x-grpc-full-method: /hyapp.user.v1.UserIdentityService/ResolveDisplayUserID
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ResolveDisplayUserIDRequest"
      responses:
        "200":
          description: 解析成功。
          schema:
            $ref: "#/definitions/ResolveDisplayUserIDResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserIdentityService/ChangeDisplayUserID:
    post:
      tags:
        - user-identity
      summary: 修改默认展示短号
      operationId: userChangeDisplayUserID
      x-grpc-full-method: /hyapp.user.v1.UserIdentityService/ChangeDisplayUserID
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ChangeDisplayUserIDRequest"
      responses:
        "200":
          description: 修改成功。
          schema:
            $ref: "#/definitions/ChangeDisplayUserIDResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserIdentityService/ApplyPrettyDisplayUserID:
    post:
      tags:
        - user-identity
      summary: 申请临时靓号
      operationId: userApplyPrettyDisplayUserID
      x-grpc-full-method: /hyapp.user.v1.UserIdentityService/ApplyPrettyDisplayUserID
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ApplyPrettyDisplayUserIDRequest"
      responses:
        "200":
          description: 申请成功。
          schema:
            $ref: "#/definitions/ApplyPrettyDisplayUserIDResponse"
        default:
          $ref: "#/responses/GRPCError"
  /hyapp.user.v1.UserIdentityService/ExpirePrettyDisplayUserID:
    post:
      tags:
        - user-identity
      summary: 主动触发靓号过期恢复
      operationId: userExpirePrettyDisplayUserID
      x-grpc-full-method: /hyapp.user.v1.UserIdentityService/ExpirePrettyDisplayUserID
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/ExpirePrettyDisplayUserIDRequest"
      responses:
        "200":
          description: 恢复成功。
          schema:
            $ref: "#/definitions/ExpirePrettyDisplayUserIDResponse"
        default:
          $ref: "#/responses/GRPCError"
  /grpc.health.v1.Health/Check:
    post:
      tags:
        - health
      summary: 标准 gRPC health check
      operationId: userHealthCheck
      x-grpc-full-method: /grpc.health.v1.Health/Check
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/HealthCheckRequest"
      responses:
        "200":
          description: 返回 SERVING 或 NOT_SERVING；未知 service 返回 gRPC NotFound。
          schema:
            $ref: "#/definitions/HealthCheckResponse"
        default:
          $ref: "#/responses/GRPCError"
  /grpc.health.v1.Health/Watch:
    post:
      tags:
        - health
      summary: 标准 gRPC health watch
      operationId: userHealthWatch
      x-grpc-full-method: /grpc.health.v1.Health/Watch
      x-server-streaming: true
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/HealthCheckRequest"
      responses:
        "200":
          description: 服务端流式推送健康状态变化。
          schema:
            $ref: "#/definitions/HealthCheckResponse"
        default:
          $ref: "#/responses/GRPCError"
definitions:
  RequestMeta:
    type: object
    properties:
      request_id:
        type: string
      caller:
        type: string
      gateway_node_id:
        type: string
      sent_at_ms:
        type: integer
        format: int64
      device_id:
        type: string
      client_ip:
        type: string
        maxLength: 64
      user_agent:
        type: string
        maxLength: 512
      country_by_ip:
        type: string
        maxLength: 2
  AuthToken:
    type: object
    properties:
      user_id:
        type: integer
        format: int64
        description: 数值型用户 ID；服务间和客户端响应统一使用该 ID。
      session_id:
        type: string
      access_token:
        type: string
      refresh_token:
        type: string
      expires_in_sec:
        type: integer
        format: int64
      token_type:
        type: string
      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
      profile_completed:
        type: boolean
      onboarding_status:
        type: string
        enum:
          - profile_required
          - completed
  LoginPasswordRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      display_user_id:
        type: string
      password:
        type: string
  LoginThirdPartyRequest:
    type: object
    description: 三方登录或注册的内部 gRPC 请求；首次注册时资料字段写入 users 主记录。
    required:
      - provider
      - credential
      - device_id
      - platform
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      provider:
        type: string
        example: firebase
      credential:
        type: string
        description: Firebase Auth ID token；不能提交 Google account id、email 或 access token。
      username:
        type: string
        maxLength: 64
      gender:
        type: string
        maxLength: 32
      country:
        type: string
        maxLength: 3
        pattern: "^[A-Za-z]{2,3}$"
        description: 国家主数据 country_code，服务端会转大写并要求 `enabled=true`。
      invite_code:
        type: string
        maxLength: 64
      device_id:
        type: string
        maxLength: 128
        description: 注册和 refresh session 绑定设备；为空时使用 meta.device_id。
      device:
        type: string
        maxLength: 128
      os_version:
        type: string
        maxLength: 64
      avatar:
        type: string
        maxLength: 512
        description: 绝对 http/https URL。
      birth:
        type: string
        description: yyyy-mm-dd。
      app_version:
        type: string
        maxLength: 64
      build_number:
        type: string
        maxLength: 64
      source:
        type: string
        maxLength: 64
      install_channel:
        type: string
        maxLength: 64
      campaign:
        type: string
        maxLength: 128
      platform:
        type: string
        maxLength: 16
        enum:
          - android
          - ios
      language:
        type: string
        maxLength: 32
        description: BCP 47 language tag subset，例如 en-US。
      timezone:
        type: string
        maxLength: 64
        description: IANA timezone name，例如 America/Los_Angeles。
  AuthResponse:
    type: object
    properties:
      token:
        $ref: "#/definitions/AuthToken"
      is_new_user:
        type: boolean
      profile_completed:
        type: boolean
      onboarding_status:
        type: string
        enum:
          - profile_required
          - completed
  SetPasswordRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      password:
        type: string
  SetPasswordResponse:
    type: object
    properties:
      password_set:
        type: boolean
  RefreshTokenRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      refresh_token:
        type: string
  RefreshTokenResponse:
    type: object
    properties:
      token:
        $ref: "#/definitions/AuthToken"
  LogoutRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      session_id:
        type: string
      refresh_token:
        type: string
  LogoutResponse:
    type: object
    properties:
      revoked:
        type: boolean
  User:
    type: object
    properties:
      user_id:
        type: integer
        format: int64
        description: 数值型用户 ID；服务间和客户端响应统一使用该 ID。
      status:
        type: integer
        format: int32
        enum:
          - 0
          - 1
          - 2
          - 3
        x-enum-names:
          - USER_STATUS_UNSPECIFIED
          - USER_STATUS_ACTIVE
          - USER_STATUS_DISABLED
          - USER_STATUS_BANNED
      created_at_ms:
        type: integer
        format: int64
      updated_at_ms:
        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
      username:
        type: string
      gender:
        type: string
      country:
        type: string
        maxLength: 3
        pattern: "^[A-Z]{2,3}$"
        description: 用户选择的国家主数据 country_code。
      avatar:
        type: string
      birth:
        type: string
        description: yyyy-mm-dd。
      language:
        type: string
      country_id:
        type: integer
        format: int64
        description: 国家主数据内部 ID；0 表示未命中国家表。
      country_name:
        type: string
      country_display_name:
        type: string
      iso_numeric:
        type: string
        description: ISO numeric / UNSD M49 三位字符串，客户端不能按数字解析。
      phone_country_code:
        type: string
        description: E.164 国家呼叫码，例如 `+86`；可能为空或多个国家共享。
      country_enabled:
        type: boolean
        description: 当前国家是否仍对 App 注册和改国家开放。
      region_id:
        type: integer
        format: int64
        description: 国家映射出的区域 ID；0 表示无区域归属。
      region_code:
        type: string
      region_name:
        type: string
      profile_completed:
        type: boolean
      profile_completed_at_ms:
        type: integer
        format: int64
      onboarding_status:
        type: string
        enum:
          - profile_required
          - completed
      invite:
        $ref: "#/definitions/InviteOverview"
  InviteOverview:
    type: object
    properties:
      my_invite_code:
        type: string
      invite_enabled:
        type: boolean
      invite_count:
        type: integer
        format: int64
      valid_invite_count:
        type: integer
        format: int64
      valid_invite_threshold_coin:
        type: integer
        format: int64
  InviteBinding:
    type: object
    properties:
      bound:
        type: boolean
      invite_code:
        type: string
      inviter_user_id:
        type: integer
        format: int64
  GetUserRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
  GetUserResponse:
    type: object
    properties:
      user:
        $ref: "#/definitions/User"
  BatchGetUsersRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_ids:
        type: array
        items:
          type: integer
          format: int64
  BatchGetUsersResponse:
    type: object
    properties:
      users:
        type: object
        additionalProperties:
          $ref: "#/definitions/User"
  ListUserIDsRequest:
    type: object
    required:
      - target_scope
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      target_scope:
        type: string
        enum:
          - all_active_users
          - region
          - country
      cursor_user_id:
        type: integer
        format: int64
      page_size:
        type: integer
        format: int32
      region_id:
        type: integer
        format: int64
      country:
        type: string
        description: "`target_scope=country` 时使用的国家码。"
  ListUserIDsResponse:
    type: object
    properties:
      user_ids:
        type: array
        items:
          type: integer
          format: int64
      next_cursor_user_id:
        type: integer
        format: int64
      done:
        type: boolean
  UpdateUserProfileRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      username:
        type: string
        description: optional；未传表示不修改。
      avatar:
        type: string
        description: optional；未传表示不修改。
      birth:
        type: string
        description: optional yyyy-mm-dd；空字符串表示清空生日。
  UpdateUserProfileResponse:
    type: object
    properties:
      user:
        $ref: "#/definitions/User"
  CompleteOnboardingRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      username:
        type: string
      avatar:
        type: string
      gender:
        type: string
        maxLength: 32
      country:
        type: string
        maxLength: 3
        pattern: "^[A-Z]{2,3}$"
        description: 注册页开放国家列表返回的 country_code。
      invite_code:
        type: string
        maxLength: 64
        description: 可选邀请码；非空时必须解析到有效邀请人。
  CompleteOnboardingResponse:
    type: object
    properties:
      user:
        $ref: "#/definitions/User"
      profile_completed:
        type: boolean
      profile_completed_at_ms:
        type: integer
        format: int64
      onboarding_status:
        type: string
      token:
        $ref: "#/definitions/AuthToken"
      invite:
        $ref: "#/definitions/InviteBinding"
  BindPushTokenRequest:
    type: object
    required:
      - meta
      - user_id
      - device_id
      - push_token
      - platform
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      device_id:
        type: string
        maxLength: 128
        description: App 安装维度稳定 ID；不是登录凭证。
      push_token:
        type: string
        maxLength: 512
      provider:
        type: string
        maxLength: 32
        description: 推送服务提供方；为空时服务端按 platform 默认 android=fcm、ios=apns。
      platform:
        type: string
        enum:
          - android
          - ios
      app_version:
        type: string
        maxLength: 64
      language:
        type: string
        maxLength: 32
      timezone:
        type: string
        maxLength: 64
  BindPushTokenResponse:
    type: object
    properties:
      bound:
        type: boolean
      updated_at_ms:
        type: integer
        format: int64
  DeletePushTokenRequest:
    type: object
    required:
      - meta
      - user_id
      - device_id
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      device_id:
        type: string
        maxLength: 128
      push_token:
        type: string
        maxLength: 512
        description: 可选；为空时删除该设备当前 active token。
  DeletePushTokenResponse:
    type: object
    properties:
      deleted:
        type: boolean
      updated_at_ms:
        type: integer
        format: int64
  ChangeUserCountryRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      country:
        type: string
        maxLength: 3
        pattern: "^[A-Z]{2,3}$"
        description: 国家主数据 country_code，服务端会归一并要求 `enabled=true`。
  ChangeUserCountryResponse:
    type: object
    properties:
      user:
        $ref: "#/definitions/User"
      next_change_allowed_at_ms:
        type: integer
        format: int64
  Country:
    type: object
    properties:
      country_id:
        type: integer
        format: int64
      country_name:
        type: string
        maxLength: 128
      country_code:
        type: string
        maxLength: 3
        pattern: "^[A-Z]{2,3}$"
      iso_alpha3:
        type: string
        maxLength: 3
      iso_numeric:
        type: string
        description: 三位 numeric 字符串。
      country_display_name:
        type: string
        maxLength: 128
      phone_country_code:
        type: string
      enabled:
        type: boolean
      flag:
        type: string
      sort_order:
        type: integer
        format: int32
      created_at_ms:
        type: integer
        format: int64
      updated_at_ms:
        type: integer
        format: int64
  Region:
    type: object
    properties:
      region_id:
        type: integer
        format: int64
      region_code:
        type: string
        pattern: "^[A-Za-z0-9][A-Za-z0-9 _-]{1,63}$"
        description: 支持短业务码，也支持 countries.json 的 subregion 原值。
      name:
        type: string
        maxLength: 128
      status:
        type: string
        enum:
          - active
          - disabled
      countries:
        type: array
        items:
          type: string
          maxLength: 3
          pattern: "^[A-Z]{2,3}$"
      sort_order:
        type: integer
        format: int32
      created_at_ms:
        type: integer
        format: int64
      updated_at_ms:
        type: integer
        format: int64
  CreateCountryRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      country_name:
        type: string
        maxLength: 128
      country_code:
        type: string
        maxLength: 3
        pattern: "^[A-Za-z]{2,3}$"
      iso_alpha3:
        type: string
        maxLength: 3
      iso_numeric:
        type: string
        description: 三位 numeric 字符串。
      country_display_name:
        type: string
        maxLength: 128
      phone_country_code:
        type: string
      enabled:
        type: boolean
        description: 不传时默认 true。
      flag:
        type: string
      sort_order:
        type: integer
        format: int32
      operator_user_id:
        type: integer
        format: int64
  ListCountriesRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      enabled:
        type: boolean
        description: 可选过滤；不传返回全部 enabled 状态。
  ListCountriesResponse:
    type: object
    properties:
      countries:
        type: array
        items:
          $ref: "#/definitions/Country"
  UpdateCountryRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      country_id:
        type: integer
        format: int64
      country_name:
        type: string
        maxLength: 128
      iso_alpha3:
        type: string
        maxLength: 3
      iso_numeric:
        type: string
        description: 三位 numeric 字符串。
      country_display_name:
        type: string
        maxLength: 128
      phone_country_code:
        type: string
      flag:
        type: string
      sort_order:
        type: integer
        format: int32
      operator_user_id:
        type: integer
        format: int64
  EnableCountryRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      country_id:
        type: integer
        format: int64
      operator_user_id:
        type: integer
        format: int64
  DisableCountryRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      country_id:
        type: integer
        format: int64
      operator_user_id:
        type: integer
        format: int64
  CountryResponse:
    type: object
    properties:
      country:
        $ref: "#/definitions/Country"
  ListRegistrationCountriesRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
  ListRegistrationCountriesResponse:
    type: object
    properties:
      countries:
        type: array
        items:
          $ref: "#/definitions/Country"
  CreateRegionRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      region_code:
        type: string
        pattern: "^[A-Za-z0-9][A-Za-z0-9 _-]{1,63}$"
      name:
        type: string
        maxLength: 128
      countries:
        type: array
        items:
          type: string
          maxLength: 3
          pattern: "^[A-Za-z]{2,3}$"
      sort_order:
        type: integer
        format: int32
      operator_user_id:
        type: integer
        format: int64
  ListRegionsRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      status:
        type: string
  ListRegionsResponse:
    type: object
    properties:
      regions:
        type: array
        items:
          $ref: "#/definitions/Region"
  GetRegionRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      region_id:
        type: integer
        format: int64
  UpdateRegionRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      region_id:
        type: integer
        format: int64
      region_code:
        type: string
        pattern: "^[A-Za-z0-9][A-Za-z0-9 _-]{1,63}$"
      name:
        type: string
        maxLength: 128
      sort_order:
        type: integer
        format: int32
      operator_user_id:
        type: integer
        format: int64
  ReplaceRegionCountriesRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      region_id:
        type: integer
        format: int64
      countries:
        type: array
        items:
          type: string
          maxLength: 3
          pattern: "^[A-Za-z]{2,3}$"
      operator_user_id:
        type: integer
        format: int64
  DisableRegionRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      region_id:
        type: integer
        format: int64
      operator_user_id:
        type: integer
        format: int64
  RegionResponse:
    type: object
    properties:
      region:
        $ref: "#/definitions/Region"
  UserIdentity:
    type: object
    properties:
      user_id:
        type: integer
        format: int64
        description: 数值型用户 ID；服务间和客户端响应统一使用该 ID。
      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
  GetUserIdentityRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
  GetUserIdentityResponse:
    type: object
    properties:
      identity:
        $ref: "#/definitions/UserIdentity"
  ResolveDisplayUserIDRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      display_user_id:
        type: string
  ResolveDisplayUserIDResponse:
    type: object
    properties:
      identity:
        $ref: "#/definitions/UserIdentity"
  ChangeDisplayUserIDRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      new_display_user_id:
        type: string
      reason:
        type: string
      operator_user_id:
        type: integer
        format: int64
  ChangeDisplayUserIDResponse:
    type: object
    properties:
      identity:
        $ref: "#/definitions/UserIdentity"
  ApplyPrettyDisplayUserIDRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      pretty_display_user_id:
        type: string
      lease_duration_ms:
        type: integer
        format: int64
      payment_receipt_id:
        type: string
  ApplyPrettyDisplayUserIDResponse:
    type: object
    properties:
      identity:
        $ref: "#/definitions/UserIdentity"
      lease_id:
        type: string
  ExpirePrettyDisplayUserIDRequest:
    type: object
    properties:
      meta:
        $ref: "#/definitions/RequestMeta"
      user_id:
        type: integer
        format: int64
      lease_id:
        type: string
  ExpirePrettyDisplayUserIDResponse:
    type: object
    properties:
      identity:
        $ref: "#/definitions/UserIdentity"
  HealthCheckRequest:
    type: object
    properties:
      service:
        type: string
        description: 空字符串或 `user-service`。
  HealthCheckResponse:
    type: object
    properties:
      status:
        type: integer
        format: int32
        enum:
          - 0
          - 1
          - 2
          - 3
        x-enum-names:
          - UNKNOWN
          - SERVING
          - NOT_SERVING
          - SERVICE_UNKNOWN
  GRPCError:
    type: object
    properties:
      grpc_code:
        type: string
        enum:
          - InvalidArgument
          - NotFound
          - FailedPrecondition
          - Unauthenticated
          - PermissionDenied
          - Unavailable
          - AlreadyExists
          - Internal
          - Aborted
      reason:
        type: string
        enum:
          - INVALID_ARGUMENT
          - NOT_FOUND
          - CONFLICT
          - UNAUTHORIZED
          - PERMISSION_DENIED
          - UNAVAILABLE
          - INTERNAL_ERROR
          - AUTH_FAILED
          - PASSWORD_ALREADY_SET
          - USER_DISABLED
          - SESSION_EXPIRED
          - SESSION_REVOKED
          - DISPLAY_USER_ID_INVALID
          - DISPLAY_USER_ID_EXISTS
          - DISPLAY_USER_ID_COOLDOWN
          - COUNTRY_CHANGE_COOLDOWN
          - DISPLAY_USER_ID_NOT_FOUND
          - DISPLAY_USER_ID_ALLOCATE_FAILED
          - DISPLAY_USER_ID_PRETTY_NOT_AVAILABLE
          - DISPLAY_USER_ID_PRETTY_ACTIVE
          - DISPLAY_USER_ID_LEASE_EXPIRED
          - DISPLAY_USER_ID_PAYMENT_REQUIRED
      message:
        type: string
      error_info_domain:
        type: string
        enum:
          - hyapp
