138 lines
4.2 KiB
Markdown
138 lines
4.2 KiB
Markdown
# Flutter 金币流水对接
|
||
|
||
本文只描述 Flutter App 读取当前登录用户金币流水的接口。金币余额和流水事实由 `wallet-service` 维护;客户端只展示 gateway 返回的分录投影,不在本地推算余额。
|
||
|
||
## 接口
|
||
|
||
`GET /api/v1/wallet/coin-transactions`
|
||
|
||
登录接口,需要 `Authorization: Bearer <access_token>`。接口固定查询当前登录用户的 `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 <access_token>
|
||
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<CoinTransaction> items;
|
||
final int total;
|
||
final int page;
|
||
final int pageSize;
|
||
|
||
factory CoinTransactionPage.fromJson(Map<String, dynamic> json) {
|
||
return CoinTransactionPage(
|
||
items: (json['items'] as List<dynamic>? ?? const [])
|
||
.map((item) => CoinTransaction.fromJson(item as Map<String, dynamic>))
|
||
.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<String, dynamic> 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` 记录到客户端日志。
|