hyapp-server/docs/flutter对接/礼物墙Flutter对接.md
2026-06-11 17:11:59 +08:00

5.6 KiB
Raw Permalink Blame History

Flutter 礼物墙对接

本文描述 Flutter App 查询用户礼物墙的 HTTP 接口。礼物墙事实由 wallet-service 在送礼扣费成功后通过异步投影聚合,客户端只展示 gateway 返回的当前投影,不从房间消息或本地缓存推算收礼数量。

接口

GET /api/v1/users/me/gift-wall

GET /api/v1/users/{user_id}/gift-wall

登录接口,需要 Authorization: Bearer <access_token>

  • GET /api/v1/users/me/gift-wall 查询当前登录用户。
  • GET /api/v1/users/{user_id}/gift-wall 查询资料卡目标用户,user_id 是路径参数,必须是正整数。

一致性边界:礼物墙是最终一致读模型,送礼成功后允许延迟数秒到几十秒才出现在接口结果中。客户端不要用该接口判断刚刚送礼是否成功;送礼成功状态仍以送礼接口响应和房间 IM 消息为准。

请求头:

Header 必填 说明
Authorization App 登录 access token。
X-App-Code App 编码,默认 lalu

示例:

GET /api/v1/users/me/gift-wall
Authorization: Bearer <access_token>
X-App-Code: lalu
GET /api/v1/users/10002/gift-wall
Authorization: Bearer <access_token>
X-App-Code: lalu

成功响应:

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_abc",
  "data": {
    "items": [
      {
        "gift_id": "rose",
        "gift_name": "Rose",
        "resource_id": 11,
        "resource": {
          "resource_id": 11,
          "resource_code": "gift_rose",
          "resource_type": "gift",
          "name": "Rose",
          "status": "active",
          "grantable": true,
          "grant_strategy": "increase_quantity",
          "usage_scopes": ["gift"],
          "asset_url": "https://cdn.example/rose.png",
          "preview_url": "",
          "animation_url": "",
          "metadata_json": "{}",
          "sort_order": 10,
          "created_at_ms": 1710000000000,
          "updated_at_ms": 1710000000000
        },
        "resource_snapshot_json": "{\"ResourceID\":11}",
        "gift_type_code": "normal",
        "presentation_json": "{}",
        "gift_count": 5,
        "total_value": 50,
        "total_coin_value": 50,
        "total_diamond_value": 0,
        "total_gift_point": 50,
        "total_heat_value": 50,
        "charge_asset_type": "COIN",
        "last_price_version": "v1",
        "first_received_at_ms": 1710000000000,
        "last_received_at_ms": 1710000005000,
        "sort_order": 10
      }
    ],
    "gift_kind_count": 1,
    "gift_total_count": 5,
    "total_value": 50,
    "total_coin_value": 50,
    "total_diamond_value": 0,
    "total_gift_point": 50,
    "total_heat_value": 50
  }
}

字段规则:

字段 说明
items gift_id 聚合后的礼物列表,固定按后台礼物排序、价值和最近收礼时间排序。
gift_count 当前用户收到该礼物的累计个数。
gift_kind_count 当前用户收到过的礼物种类数,等于 items.length
gift_total_count 当前用户收到的所有礼物数量总和。
total_value 礼物总价值,按送礼时服务端结算的 charge_amount 累加。
total_coin_value / total_diamond_value 按收费资产拆分的总价值;当前普通礼物通常只读 total_coin_value
total_gift_point 主播礼物积分累计值,不等同于可提现余额。
total_heat_value 房间热度累计值,可用于展示贡献热度,不要反推价格。
charge_asset_type 该礼物历史上只用一种资产收费时为 COINDIAMOND;发生过资产切换时为 MIXED
resource 礼物展示资源快照,客户端优先用 asset_url / preview_url / animation_url 渲染。
resource_snapshot_json 原始资源快照 JSON主要用于客户端兜底和日志排查。
*_ms Unix epoch milliseconds展示层按用户本地时区格式化。

Flutter 解析示例

class GiftWall {
  GiftWall({
    required this.items,
    required this.giftKindCount,
    required this.giftTotalCount,
    required this.totalValue,
  });

  final List<GiftWallItem> items;
  final int giftKindCount;
  final int giftTotalCount;
  final int totalValue;

  factory GiftWall.fromJson(Map<String, dynamic> json) {
    return GiftWall(
      items: (json['items'] as List<dynamic>? ?? const [])
          .map((item) => GiftWallItem.fromJson(item as Map<String, dynamic>))
          .toList(),
      giftKindCount: (json['gift_kind_count'] as num?)?.toInt() ?? 0,
      giftTotalCount: (json['gift_total_count'] as num?)?.toInt() ?? 0,
      totalValue: (json['total_value'] as num?)?.toInt() ?? 0,
    );
  }
}

class GiftWallItem {
  GiftWallItem({
    required this.giftId,
    required this.giftName,
    required this.giftCount,
    required this.totalValue,
    required this.assetUrl,
  });

  final String giftId;
  final String giftName;
  final int giftCount;
  final int totalValue;
  final String assetUrl;

  factory GiftWallItem.fromJson(Map<String, dynamic> json) {
    final resource = json['resource'] as Map<String, dynamic>? ?? const {};
    return GiftWallItem(
      giftId: json['gift_id'] as String? ?? '',
      giftName: json['gift_name'] as String? ?? '',
      giftCount: (json['gift_count'] as num?)?.toInt() ?? 0,
      totalValue: (json['total_value'] as num?)?.toInt() ?? 0,
      assetUrl: resource['asset_url'] as String? ?? '',
    );
  }
}

调用时先解析外层 envelope只有 code == "OK" 时读取 data;其他错误按统一网关错误处理,并把 request_id 写入客户端日志。