4.2 KiB
4.2 KiB
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,不要使用业务专属分页名。
示例:
GET /api/v1/wallet/coin-transactions?page=1&page_size=30
Authorization: Bearer <access_token>
X-App-Code: lalu
成功响应:
{
"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 解析示例
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 记录到客户端日志。