hyapp-server/docs/flutter对接/CP关系Flutter对接.md
2026-06-08 19:39:22 +08:00

14 KiB
Raw Permalink Blame History

CP关系Flutter对接

本文只写 App 需要对接的地址、参数、返回值和相关 IM。CP 关系包含 cpbrothersister 三种类型CP 关系每个用户最多 1 个,兄弟和姐妹数量由后台 CP 配置控制;不需要佩戴接口。

1. 礼物面板CP Tab

地址:

GET /api/v1/rooms/{room_id}/gift-panel
Authorization: Bearer <access_token>
X-App-Code: lalu

参数:

参数 类型 必填 说明
room_id string 当前房间 ID

返回值:

tabs 里会有 gift_type_code=cp 的 Tab。CP 礼物会带:

字段 类型 说明
gift_id string 送礼时使用
gift_type_code string CP 礼物为 cp
cp_relation_type string cpbrothersister
name string 礼物名
coin_price int64 展示价格,扣费以后端为准
resource.asset_url string 礼物 icon
resource.animation_url string 礼物动效,可为空

Flutter 在 CP 礼物 Tab 下按 cp_relation_type 分成 CP、兄弟、姐妹三个二级 Tab。

2. 发送CP礼物

地址:

POST /api/v1/rooms/gift/send
Authorization: Bearer <access_token>
X-App-Code: lalu
Content-Type: application/json

参数:

字段 类型 必填 说明
room_id string 当前房间 ID
command_id string 本次送礼幂等键,重试必须复用
target_type string 当前传 user
target_user_ids int64[] CP 礼物只传 1 个收礼人
gift_id string CP 礼物 ID
gift_count int32 必须大于 0

示例:

{
  "room_id": "room_1001",
  "command_id": "cp_gift_1779259000000_10001_10002",
  "target_type": "user",
  "target_user_ids": [10002],
  "gift_id": "cp_ring",
  "gift_count": 1
}

返回值:

沿用普通送礼响应。关系申请通过 IM 到达,送礼响应不保证返回申请 ID。

字段 类型 说明
result.applied bool 本次命令是否落地
billing_receipt_id string 扣费回执
room_heat int64 最新房间热度
gift_rank array 房间礼物榜
room object 房间快照
lucky_gift object 幸运礼物结果,普通 CP 礼物为空

3. 查询我的申请

地址:

GET /api/v1/cp/applications?direction=incoming&status=pending&page=1&page_size=20
Authorization: Bearer <access_token>
X-App-Code: lalu

参数:

参数 类型 必填 说明
direction string incoming 我收到的;outgoing 我发出的
status string pendingacceptedrejectedexpiredblocked
page int32 默认 1
page_size int32 默认 20

返回值:

{
  "applications": [
    {
      "application_id": "cp_app_10001_10002_cp",
      "relation_type": "cp",
      "status": "pending",
      "requester": {
        "user_id": "10001",
        "display_user_id": "123456",
        "username": "Alice",
        "avatar": "https://cdn.example/a.png"
      },
      "target": {
        "user_id": "10002",
        "display_user_id": "654321",
        "username": "Bob",
        "avatar": "https://cdn.example/b.png"
      },
      "room_id": "room_1001",
      "room_region_id": 86,
      "gift": {
        "gift_id": "cp_ring",
        "gift_name": "CP Ring",
        "gift_icon_url": "https://cdn.example/cp-ring.png",
        "gift_animation_url": "",
        "gift_count": 1,
        "gift_value": 520,
        "billing_receipt_id": "gift_tx_1"
      },
      "created_at_ms": 1779259000000,
      "updated_at_ms": 1779259000000,
      "expires_at_ms": 1779345400000,
      "decided_at_ms": 0
    }
  ],
  "total": 1,
  "server_time_ms": 1779259001000
}

4. 同意申请

地址:

POST /api/v1/cp/applications/{application_id}/accept
Authorization: Bearer <access_token>
X-App-Code: lalu

参数:

参数 类型 必填 说明
application_id string 申请 ID

返回值:

{
  "application": {
    "application_id": "cp_app_10001_10002_cp",
    "relation_type": "cp",
    "status": "accepted",
    "decided_at_ms": 1779259100000
  },
  "relationship": {
    "relationship_id": "cp_rel_10001_10002",
    "relation_type": "cp",
    "status": "active",
    "me": {
      "user_id": "10002",
      "display_user_id": "654321",
      "username": "Bob",
      "avatar": "https://cdn.example/b.png"
    },
    "partner": {
      "user_id": "10001",
      "display_user_id": "123456",
      "username": "Alice",
      "avatar": "https://cdn.example/a.png"
    },
    "intimacy_value": 0,
    "level": 1,
    "formed_at_ms": 1779259100000,
    "updated_at_ms": 1779259100000
  }
}

错误处理:

code 处理
CONFLICT A/B 任一方已经有 active 关系,或申请已过期;刷新申请和关系列表
NOT_FOUND 申请不存在,刷新申请列表
PERMISSION_DENIED 当前用户不是申请目标用户

5. 拒绝申请

地址:

POST /api/v1/cp/applications/{application_id}/reject
Authorization: Bearer <access_token>
X-App-Code: lalu
Content-Type: application/json

参数:

参数 类型 必填 说明
application_id string 申请 ID
reason string 拒绝原因

返回值:

{
  "application": {
    "application_id": "cp_app_10001_10002_cp",
    "relation_type": "cp",
    "status": "rejected",
    "decided_at_ms": 1779259100000
  }
}

6. 查询我的关系

地址:

GET /api/v1/cp/relationships?relation_type=cp&page=1&page_size=20
Authorization: Bearer <access_token>
X-App-Code: lalu

参数:

参数 类型 必填 说明
relation_type string cpbrothersister;不传返回全部
page int32 默认 1
page_size int32 默认 20

返回值:

{
  "relationships": [
    {
      "relationship_id": "cp_rel_10001_10002",
      "relation_type": "cp",
      "status": "active",
      "me": {
        "user_id": "10001",
        "display_user_id": "123456",
        "username": "Alice",
        "avatar": "https://cdn.example/a.png"
      },
      "partner": {
        "user_id": "10002",
        "display_user_id": "654321",
        "username": "Bob",
        "avatar": "https://cdn.example/b.png"
      },
      "intimacy_value": 1888,
      "level": 2,
      "current_level_threshold": 1000,
      "next_level_threshold": 3000,
      "needed_for_next_level": 1112,
      "level_progress_percent": 44,
      "max_level": false,
      "breakup_cost_coins": 88,
      "formed_at_ms": 1779259100000,
      "ended_at_ms": 0,
      "updated_at_ms": 1779259200000
    }
  ],
  "total": 1
}

说明:

  • CP 关系每个用户最多 1 个;兄弟和姐妹可拥有数量以后台配置为准。
  • A/B 两个用户之间同一时间只能存在一种 active 关系,不能同时是 CP、兄弟或姐妹。
  • 不需要佩戴接口,资料卡直接展示当前 active 关系。
  • 资料卡弹窗只使用后端返回的 intimacy_valuelevelcurrent_level_thresholdnext_level_thresholdneeded_for_next_levellevel_progress_percentmax_level
  • max_level=truenext_level_threshold=0 时表示没有下一等级,隐藏“距离下一级”和进度条。
  • Flutter 不要用本地常量、礼物价格或后台配置接口自行推算进度条和“距离下一级”。
  • breakup_cost_coins 是解除当前关系需要支付的金币数,弹窗和二次确认直接使用该值。

7. 解除关系

地址:

POST /api/v1/cp/relationships/{relationship_id}/break
Authorization: Bearer <access_token>
X-App-Code: lalu
Content-Type: application/json

{
  "command_id": "break_cp_rel_10001_10002_1779259300000"
}

参数:

参数 类型 必填 说明
relationship_id string URL path关系 ID
command_id string 解除关系幂等键,同一次重试必须复用

返回值:

{
  "relationship": {
    "relationship_id": "cp_rel_10001_10002",
    "relation_type": "cp",
    "status": "ended",
    "breakup_cost_coins": 88,
    "formed_at_ms": 1779259100000,
    "ended_at_ms": 1779259300000,
    "updated_at_ms": 1779259300000
  },
  "breakup_cost_coins": 88,
  "wallet_transaction_id": "wallet_tx_break_1",
  "coin_balance_after": 912
}

说明:

  • 解除前先展示 /api/v1/cp/relationships 返回的 breakup_cost_coins
  • 金币不足时接口返回 INSUFFICIENT_BALANCE,关系仍保持 active。
  • 解除成功不发 C2C、房间 IM 或区域 IMFlutter 使用接口返回刷新资料卡和关系列表。

8. 相关IM

所有 IM 都是 Tencent TIMCustomElemMsgContent.Desc 等于下表事件名,MsgContent.Data 是 JSON 字符串。客户端按 event_id 去重。

8.1 收到关系申请

房间 IM 和 C2C IM 都会收到:

字段
MsgContent.Desc cp_application_created
Data.event_type cp_application_created

Data

{
  "event_id": "cp_application:created:10002:1",
  "event_type": "cp_application_created",
  "application_id": "cp_app_10001_10002_cp",
  "relation_type": "cp",
  "status": "pending",
  "requester": {
    "user_id": "10001",
    "display_user_id": "123456",
    "username": "Alice",
    "avatar": "https://cdn.example/a.png"
  },
  "target": {
    "user_id": "10002",
    "display_user_id": "654321",
    "username": "Bob",
    "avatar": "https://cdn.example/b.png"
  },
  "room_id": "room_1001",
  "room_region_id": 86,
  "gift": {
    "gift_id": "cp_ring",
    "gift_name": "CP Ring",
    "gift_icon_url": "https://cdn.example/cp-ring.png",
    "gift_animation_url": "",
    "gift_count": 1,
    "gift_value": 520,
    "billing_receipt_id": "gift_tx_1"
  },
  "expires_at_ms": 1779345400000,
  "decided_at_ms": 0,
  "source_created_at_ms": 1779259000000,
  "recipient_user_id": "10002"
}

处理:

  • 房间 IM 只有 recipient_user_id 等于当前用户时显示同意/拒绝按钮。
  • C2C IM 在私聊里显示同意/拒绝按钮。
  • expires_at_ms <= server_time_ms 时按钮置灰并刷新申请列表。

8.2 申请被同意

C2C 会收到双方互发的两条消息,房间 IM 仍然只给申请发起人 A 展示:

字段
MsgContent.Desc cp_application_accepted
Data.event_type cp_application_accepted

Data 字段和申请消息一致,差异是:

字段
status accepted
decided_at_ms 处理时间
recipient_user_id 房间 IM 中为 A 的 user_id

C2C 差异:

方向 c2c_notice_role message_text
B -> A accepted_to_requester 我同意了你
A -> B accepted_to_acceptor 对方已同意你的申请

处理:

  • A 收到后刷新 /api/v1/cp/relationships
  • B 操作成功后也刷新关系列表。
  • 同一对用户其他 pending 申请需要置灰。

8.3 申请被拒绝

C2C 和房间 IM 都会收到:

字段
MsgContent.Desc cp_application_rejected
Data.event_type cp_application_rejected

Data 字段和申请消息一致,差异是:

字段
status rejected
reason 拒绝原因,可为空
decided_at_ms 处理时间
recipient_user_id 房间 IM 中为 A 的 user_id

8.4 区域关系成立播报

用户组成任意关系后,会发送区域 IM

字段
MsgContent.Desc cp_relationship_created
Data.event_type cp_relationship_created

Data

{
  "event_id": "cp_relationship:created:10002:1",
  "event_type": "cp_relationship_created",
  "relationship_id": "cp_rel_10001_10002",
  "application_id": "cp_app_10001_10002_cp",
  "relation_type": "cp",
  "requester": {
    "user_id": "10001",
    "display_user_id": "123456",
    "username": "Alice",
    "avatar": "https://cdn.example/a.png"
  },
  "target": {
    "user_id": "10002",
    "display_user_id": "654321",
    "username": "Bob",
    "avatar": "https://cdn.example/b.png"
  },
  "gift": {
    "gift_id": "cp_ring",
    "gift_name": "CP Ring",
    "gift_icon_url": "https://cdn.example/cp-ring.png",
    "gift_animation_url": "",
    "gift_count": 1,
    "gift_value": 520,
    "billing_receipt_id": "gift_tx_1"
  },
  "room_id": "room_1001",
  "room_region_id": 86,
  "group_id": "hy_lalu_bc_r_86",
  "source_created_at_ms": 1779259100000
}

处理:

  • 只在区域播报位展示,不当作私聊消息。
  • 点击可进入 room_id 对应房间;房间不存在或进房失败时只关闭播报。

8. 客户端错误提示

code 建议提示
CONFLICT 你或对方已经有关系;如果申请已过期则提示 申请已过期
NOT_FOUND 申请不存在或已失效
PERMISSION_DENIED 只能处理发给你的申请

9. 本地状态规则

  • 不要靠送礼成功直接创建关系。
  • 不要靠 C2C 或房间按钮本地改关系,必须调用同意或拒绝接口。
  • 同一个 event_id 只处理一次。
  • 同一个 application_id 在房间和 C2C 里可能都出现,按钮状态要共享。
  • 申请过期时间以 expires_at_ms 为准。
  • 亲密值和等级以后端关系接口返回为准,不在 Flutter 里按礼物价格推算。
  • 进度条和距离下一级只使用关系接口返回的阈值字段;字段缺失时不展示,不要伪造 next_level_thresholdneeded_for_next_level 或进度百分比。