456 lines
11 KiB
Markdown
456 lines
11 KiB
Markdown
# CP关系Flutter对接
|
||
|
||
本文只写 App 需要对接的地址、参数、返回值和相关 IM。CP 关系包含 `cp`、`brother`、`sister` 三种类型;每个用户同时只能有一个 active 关系,不需要佩戴接口,也没有后台数量配置。
|
||
|
||
## 1. 礼物面板CP Tab
|
||
|
||
地址:
|
||
|
||
```http
|
||
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礼物
|
||
|
||
地址:
|
||
|
||
```http
|
||
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 |
|
||
|
||
示例:
|
||
|
||
```json
|
||
{
|
||
"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. 查询我的申请
|
||
|
||
地址:
|
||
|
||
```http
|
||
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 |
|
||
|
||
返回值:
|
||
|
||
```json
|
||
{
|
||
"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. 同意申请
|
||
|
||
地址:
|
||
|
||
```http
|
||
POST /api/v1/cp/applications/{application_id}/accept
|
||
Authorization: Bearer <access_token>
|
||
X-App-Code: lalu
|
||
```
|
||
|
||
参数:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `application_id` | string | 是 | 申请 ID |
|
||
|
||
返回值:
|
||
|
||
```json
|
||
{
|
||
"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. 拒绝申请
|
||
|
||
地址:
|
||
|
||
```http
|
||
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 | 否 | 拒绝原因 |
|
||
|
||
返回值:
|
||
|
||
```json
|
||
{
|
||
"application": {
|
||
"application_id": "cp_app_10001_10002_cp",
|
||
"relation_type": "cp",
|
||
"status": "rejected",
|
||
"decided_at_ms": 1779259100000
|
||
}
|
||
}
|
||
```
|
||
|
||
## 6. 查询我的关系
|
||
|
||
地址:
|
||
|
||
```http
|
||
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 |
|
||
|
||
返回值:
|
||
|
||
```json
|
||
{
|
||
"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:
|
||
|
||
```json
|
||
{
|
||
"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:
|
||
|
||
```json
|
||
{
|
||
"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 里按礼物价格推算。
|