# Flutter 金币流水对接 本文只描述 Flutter App 读取当前登录用户金币流水的接口。金币余额和流水事实由 `wallet-service` 维护;客户端只展示 gateway 返回的分录投影,不在本地推算余额。 ## 接口 `GET /api/v1/wallet/coin-transactions` 登录接口,需要 `Authorization: Bearer `。接口固定查询当前登录用户的 `COIN` 资产流水,不接受客户端传 `user_id` 或 `asset_type`。 请求头: | Header | 必填 | 说明 | | --- | --- | --- | | `Authorization` | 是 | App 登录 access token。 | | `X-App-Code` | 否 | App 编码,默认 `lalu`。 | Query: | 字段 | 必填 | 默认 | 说明 | | --- | --- | --- | --- | | `page` | 否 | `1` | 页码,从 1 开始。 | | `page_size` | 否 | `30` | 每页数量。首屏不传时返回最近 30 条。 | 排序固定为 `created_at_ms DESC, entry_id DESC`,即最新流水在前。分页字段使用通用 `page` / `page_size`,不要使用业务专属分页名。 示例: ```http GET /api/v1/wallet/coin-transactions?page=1&page_size=30 Authorization: Bearer X-App-Code: lalu ``` 成功响应: ```json { "code": "OK", "message": "ok", "request_id": "req_abc", "data": { "items": [ { "entry_id": 101, "transaction_id": "wtx_abc", "biz_type": "coin_seller_transfer", "asset_type": "COIN", "available_delta": 3000, "frozen_delta": 0, "available_after": 12800, "frozen_after": 0, "counterparty_user_id": 90001, "room_id": "", "created_at_ms": 1710000000000 } ], "total": 48, "page": 1, "page_size": 30 } } ``` 字段规则: | 字段 | 说明 | | --- | --- | | `available_delta` | 本次可用金币变化;正数是收入,负数是支出。 | | `available_after` | 本条流水落账后的可用金币余额,余额展示以该字段为准。 | | `biz_type` | 业务类型,例如 `coin_seller_transfer`、`gift_debit`、`task_reward`、`game_credit`、`game_debit`、`game_refund`、`vip_purchase`。客户端可本地映射展示文案。 | | `counterparty_user_id` | 对手用户长 ID;没有对手方时为 `0`。 | | `room_id` | 房间相关流水的房间 ID;非房间流水为空字符串。 | | `created_at_ms` | Unix epoch milliseconds;展示层按用户本地时区格式化。 | ## Flutter 解析示例 ```dart class CoinTransactionPage { CoinTransactionPage({ required this.items, required this.total, required this.page, required this.pageSize, }); final List items; final int total; final int page; final int pageSize; factory CoinTransactionPage.fromJson(Map json) { return CoinTransactionPage( items: (json['items'] as List? ?? const []) .map((item) => CoinTransaction.fromJson(item as Map)) .toList(), total: (json['total'] as num?)?.toInt() ?? 0, page: (json['page'] as num?)?.toInt() ?? 1, pageSize: (json['page_size'] as num?)?.toInt() ?? 30, ); } } class CoinTransaction { CoinTransaction({ required this.entryId, required this.transactionId, required this.bizType, required this.availableDelta, required this.availableAfter, required this.createdAtMs, }); final int entryId; final String transactionId; final String bizType; final int availableDelta; final int availableAfter; final int createdAtMs; bool get isIncome => availableDelta > 0; int get amount => availableDelta.abs(); factory CoinTransaction.fromJson(Map json) { return CoinTransaction( entryId: (json['entry_id'] as num).toInt(), transactionId: json['transaction_id'] as String? ?? '', bizType: json['biz_type'] as String? ?? '', availableDelta: (json['available_delta'] as num?)?.toInt() ?? 0, availableAfter: (json['available_after'] as num?)?.toInt() ?? 0, createdAtMs: (json['created_at_ms'] as num?)?.toInt() ?? 0, ); } } ``` 接口调用时,先解析外层 envelope:只有 `code == "OK"` 时读取 `data`;其他 `code` 按登录态、参数错误或服务错误处理,并把 `request_id` 记录到客户端日志。