# 币商列表 Flutter 对接 本文描述 Flutter App 获取当前用户所在区域启用币商列表的接口。币商身份、状态和区域过滤由 `user-service` 维护;App 只展示 gateway 返回的列表,不传 `region_id`,也不在本地缓存为跨区域通用数据。 ## 接口 `GET /api/v1/wallet/coin-sellers` 登录接口,需要 `Authorization: Bearer `。接口按当前登录用户的 `users.region_id` 查询同区域、状态为 `active`、资产类型为 `COIN_SELLER_COIN` 的币商。 请求头: | Header | 必填 | 说明 | | --- | --- | --- | | `Authorization` | 是 | App 登录 access token。 | | `X-App-Code` | 否 | App 编码,默认 `lalu`。 | | `X-Request-ID` | 否 | 客户端链路 ID;服务端也会生成并在响应中返回。 | Query:无。不要传 `region_id`、`country_id` 或 `user_id`。 示例: ```http GET /api/v1/wallet/coin-sellers Authorization: Bearer X-App-Code: lalu ``` 成功响应: ```json { "code": "OK", "message": "ok", "request_id": "req_abc", "data": { "items": [ { "user_id": "801", "display_user_id": "900801", "username": "Seller One", "avatar": "https://cdn.example/avatar.png", "country_id": 86, "country_code": "CN", "country_name": "China", "country_display_name": "中国", "region_id": 30, "region_code": "east-asia", "region_name": "East Asia", "status": "active", "merchant_asset_type": "COIN_SELLER_COIN", "updated_at_ms": 1700000001000 } ], "total": 1 } } ``` 字段规则: | 字段 | 说明 | | --- | --- | | `user_id` | 币商用户长 ID,JSON 中按字符串返回,避免 Dart/JS 大整数精度问题。 | | `display_user_id` | 当前展示短号,可用于转账页展示和输入确认。 | | `username` | 币商昵称;为空时客户端可展示短号。 | | `avatar` | 币商头像 URL;为空时展示默认头像。 | | `country_id` | 币商用户当前国家 ID。 | | `country_code` | 国家编码,例如 `CN`。 | | `country_name` | 英文或标准国家名。 | | `country_display_name` | 客户端展示优先使用的国家名。 | | `region_id` | 币商用户当前区域 ID;应与当前用户区域一致。 | | `status` | 当前只会返回 `active`。 | | `merchant_asset_type` | 当前固定为 `COIN_SELLER_COIN`。 | | `updated_at_ms` | 币商身份最近更新时间,Unix epoch milliseconds。 | ## Flutter 解析示例 ```dart class CoinSellerList { CoinSellerList({ required this.items, required this.total, }); final List items; final int total; factory CoinSellerList.fromJson(Map json) { return CoinSellerList( items: (json['items'] as List? ?? const []) .map((item) => CoinSeller.fromJson(item as Map)) .toList(), total: (json['total'] as num?)?.toInt() ?? 0, ); } } class CoinSeller { CoinSeller({ required this.userId, required this.displayUserId, required this.username, required this.avatar, required this.countryId, required this.countryDisplayName, required this.regionId, required this.status, required this.merchantAssetType, required this.updatedAtMs, }); final String userId; final String displayUserId; final String username; final String avatar; final int countryId; final String countryDisplayName; final int regionId; final String status; final String merchantAssetType; final int updatedAtMs; String get title => username.isNotEmpty ? username : displayUserId; factory CoinSeller.fromJson(Map json) { return CoinSeller( userId: json['user_id'] as String? ?? '', displayUserId: json['display_user_id'] as String? ?? '', username: json['username'] as String? ?? '', avatar: json['avatar'] as String? ?? '', countryId: (json['country_id'] as num?)?.toInt() ?? 0, countryDisplayName: json['country_display_name'] as String? ?? '', regionId: (json['region_id'] as num?)?.toInt() ?? 0, status: json['status'] as String? ?? '', merchantAssetType: json['merchant_asset_type'] as String? ?? '', updatedAtMs: (json['updated_at_ms'] as num?)?.toInt() ?? 0, ); } } ``` ## 客户端处理 1. 先解析外层 envelope,只有 `code == "OK"` 时读取 `data.items`。 2. 空列表表示当前区域暂无启用币商;展示空状态,不切换到其他区域。 3. 点击币商进入转账或联系流程时,使用 `display_user_id` 做用户可见确认,实际后续接口仍以服务端解析结果为准。 4. 用户切换国家或完成资料变更后重新拉取本接口,不复用旧区域列表。 5. 请求失败时记录 `request_id`,按登录失效、参数错误、服务错误分别处理。