11 KiB
11 KiB
CP关系Flutter对接
本文只写 App 需要对接的地址、参数、返回值和相关 IM。CP 关系包含 cp、brother、sister 三种类型;每个用户同时只能有一个 active 关系,不需要佩戴接口,也没有后台数量配置。
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 | cp、brother、sister |
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 | 否 | pending、accepted、rejected、expired、blocked |
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 | 否 | cp、brother、sister;不传返回全部 |
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,
"formed_at_ms": 1779259100000,
"updated_at_ms": 1779259200000
}
],
"total": 1
}
说明:
- 每个用户同时只能有一个 active 关系。
- 不需要佩戴接口,资料卡直接展示当前 active 关系。
7. 相关IM
所有 IM 都是 Tencent TIMCustomElem,MsgContent.Desc 等于下表事件名,MsgContent.Data 是 JSON 字符串。客户端按 event_id 去重。
7.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时按钮置灰并刷新申请列表。
7.2 申请被同意
C2C 和房间 IM 都会收到:
| 字段 | 值 |
|---|---|
MsgContent.Desc |
cp_application_accepted |
Data.event_type |
cp_application_accepted |
Data 字段和申请消息一致,差异是:
| 字段 | 值 |
|---|---|
status |
accepted |
decided_at_ms |
处理时间 |
recipient_user_id |
房间 IM 中为 A 的 user_id |
处理:
- A 收到后刷新
/api/v1/cp/relationships。 - B 操作成功后也刷新关系列表。
- 同一对用户其他 pending 申请需要置灰。
7.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 |
7.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 里按礼物价格推算。