387 lines
9.3 KiB
Markdown
387 lines
9.3 KiB
Markdown
# IM 青少年模式前后端对接文档
|
||
|
||
## 1. 基本约定
|
||
|
||
青少年模式相关接口均为登录后接口。客户端需要携带用户登录 token、普通 API Key 和协议版本头。
|
||
|
||
公共 Header:
|
||
|
||
```http
|
||
Authorization: Bearer <access_token>
|
||
X-MeetingIM-Auth: Bearer <access_token>
|
||
X-MeetingIM-APIKey: <api_key>
|
||
X-IM-Schema: 1
|
||
X-Request-ID: <uuid>
|
||
X-Tenant-Code: default
|
||
```
|
||
|
||
所有写接口还需要携带:
|
||
|
||
```http
|
||
Idempotency-Key: <uuid>
|
||
```
|
||
|
||
测试环境验证码固定为 `123456`。`action_token` 为 5 分钟有效、单次消费,只能用于指定动作,不能在多个接口之间重复使用。
|
||
|
||
当前默认策略项为 `night_quiet`、`content_filter`、`payment_guard` 三条。现阶段后端已完成状态持久化、开启/关闭校验和策略返回;业务接口层的深夜消息免打扰、内容过滤、支付拦截尚未实际强制执行,响应中的 `restriction_items[].enforced=false` 用于明确表达这一点。后续某条规则真正接入后台策略和业务拦截后,应把对应项改为 `true`。
|
||
|
||
## 2. 开启青少年模式
|
||
|
||
### 2.1 查询当前状态
|
||
|
||
```http
|
||
GET /v1/im/settings/youth-mode
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"enabled": false,
|
||
"policy_version": 1,
|
||
"restrictions": ["night_quiet", "content_filter", "payment_guard"],
|
||
"restriction_items": [
|
||
{
|
||
"code": "night_quiet",
|
||
"title": "深夜免打扰",
|
||
"description": "夜间减少消息提醒打扰,帮助用户保持健康作息。",
|
||
"enforced": false
|
||
},
|
||
{
|
||
"code": "content_filter",
|
||
"title": "内容过滤",
|
||
"description": "对不适合青少年的内容进行识别和限制展示。",
|
||
"enforced": false
|
||
},
|
||
{
|
||
"code": "payment_guard",
|
||
"title": "支付守护",
|
||
"description": "限制或确认支付、充值等敏感消费行为。",
|
||
"enforced": false
|
||
}
|
||
],
|
||
"password_set": false,
|
||
"can_enable": false,
|
||
"updated_at": "2026-08-20T09:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
前端优先使用 `restriction_items` 渲染开启确认页,`restrictions` 保留为兼容旧客户端的 code 数组。最终开启时需要提交用户确认过的 `policy_version`。
|
||
|
||
### 2.2 发起短信身份验证
|
||
|
||
```http
|
||
POST /v1/im/security/challenges
|
||
```
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"scene": "set_independent_password",
|
||
"channel": "sms"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"challenge_id": "sec_chg_1",
|
||
"phone_mask": "+861****000",
|
||
"expires_in_seconds": 300,
|
||
"resend_after_seconds": 60
|
||
}
|
||
}
|
||
```
|
||
|
||
手机号由服务端从当前登录用户读取,客户端不提交目标手机号。
|
||
|
||
### 2.3 校验短信验证码
|
||
|
||
```http
|
||
POST /v1/im/security/challenges/{challenge_id}/verify
|
||
```
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"code": "123456"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"action": "set_independent_password",
|
||
"action_token": "set_password_token",
|
||
"expires_at": "2026-08-20T09:05:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
这里拿到的 `action_token` 只用于设置独立密码,不能直接用于开启青少年模式。
|
||
|
||
### 2.4 设置独立密码
|
||
|
||
```http
|
||
POST /v1/im/settings/independent-password
|
||
```
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"password": "6439",
|
||
"purpose": "youth_mode",
|
||
"action_token": "set_password_token"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"status": "ok",
|
||
"password_set": true,
|
||
"purpose": "youth_mode",
|
||
"enable_action_token": "enable_token",
|
||
"enable_action_expires_at": "2026-08-20T09:05:00Z",
|
||
"next_action": "enable_youth_mode"
|
||
}
|
||
}
|
||
```
|
||
|
||
服务端会消费 `set_password_token`,并返回新的 `enable_action_token`。前端下一步必须使用 `enable_action_token`,不要复用 `set_password_token`。
|
||
|
||
独立密码规则:
|
||
|
||
- 必须是 4 位数字。
|
||
- 不能是明显连续或重复组合。
|
||
- 服务端只保存强哈希,不返回明文密码。
|
||
- 前端离开页面后应清空输入内容,不落普通存储。
|
||
|
||
### 2.5 开启青少年模式
|
||
|
||
```http
|
||
POST /v1/im/settings/youth-mode/enable
|
||
```
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"policy_version": 1,
|
||
"action_token": "enable_token"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"enabled": true,
|
||
"policy_version": 1,
|
||
"password_set": true,
|
||
"restrictions": ["night_quiet", "content_filter", "payment_guard"],
|
||
"restriction_items": [
|
||
{
|
||
"code": "night_quiet",
|
||
"title": "深夜免打扰",
|
||
"description": "夜间减少消息提醒打扰,帮助用户保持健康作息。",
|
||
"enforced": false
|
||
},
|
||
{
|
||
"code": "content_filter",
|
||
"title": "内容过滤",
|
||
"description": "对不适合青少年的内容进行识别和限制展示。",
|
||
"enforced": false
|
||
},
|
||
{
|
||
"code": "payment_guard",
|
||
"title": "支付守护",
|
||
"description": "限制或确认支付、充值等敏感消费行为。",
|
||
"enforced": false
|
||
}
|
||
],
|
||
"updated_at": "2026-08-20T09:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
开启成功后,客户端应刷新本地设置页和 `bootstrap` 缓存。服务端会持久化状态,前端不能只依赖本地标志位。
|
||
|
||
## 3. 关闭青少年模式
|
||
|
||
### 3.1 校验独立密码
|
||
|
||
```http
|
||
POST /v1/im/settings/independent-password/verify
|
||
```
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"password": "6439",
|
||
"purpose": "youth_mode",
|
||
"action": "close_youth_mode"
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"action": "close_youth_mode",
|
||
"action_token": "close_token",
|
||
"expires_at": "2026-08-20T09:05:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.2 关闭模式
|
||
|
||
```http
|
||
POST /v1/im/settings/youth-mode/disable
|
||
X-Action-Token: close_token
|
||
```
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"enabled": false,
|
||
"policy_version": 1,
|
||
"password_set": true,
|
||
"updated_at": "2026-08-20T09:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
关闭接口的 `action_token` 通过 Header `X-Action-Token` 传递,不放在 Body 中。
|
||
|
||
## 4. Token 使用规则
|
||
|
||
| 来源 | action | 用途 | 传递方式 | 是否单次消费 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `security/challenges/{id}/verify` | `set_independent_password` | 设置独立密码 | Body `action_token` | 是 |
|
||
| `independent-password` | `enable_youth_mode` | 开启青少年模式 | Body `action_token` | 是 |
|
||
| `independent-password/verify` | `close_youth_mode` | 关闭青少年模式 | Header `X-Action-Token` | 是 |
|
||
|
||
常见错误:
|
||
|
||
- `ACTION_TOKEN_INVALID`:token 已消费、过期、action 不匹配、target 不匹配或用户不匹配。
|
||
- `CODE_INVALID`:短信验证码错误。
|
||
- `CHALLENGE_EXPIRED`:短信 challenge 已过期。
|
||
- `TOO_MANY_ATTEMPTS`:独立密码或验证码错误次数过多。
|
||
- `VERIFICATION_RATE_LIMITED`:发码过于频繁,按 `retry_after_seconds` 倒计时。
|
||
|
||
## 5. curl 示例
|
||
|
||
开启流程:
|
||
|
||
```bash
|
||
curl -sS 'https://api.118im.com/v1/im/security/challenges' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'Authorization: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-APIKey: <api_key>' \
|
||
-H 'X-IM-Schema: 1' \
|
||
-H 'X-Tenant-Code: default' \
|
||
-H 'X-Request-ID: <uuid>' \
|
||
-H 'Idempotency-Key: <uuid>' \
|
||
--data '{"scene":"set_independent_password","channel":"sms"}'
|
||
```
|
||
|
||
```bash
|
||
curl -sS 'https://api.118im.com/v1/im/security/challenges/<challenge_id>/verify' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'Authorization: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-APIKey: <api_key>' \
|
||
-H 'X-IM-Schema: 1' \
|
||
-H 'X-Tenant-Code: default' \
|
||
-H 'X-Request-ID: <uuid>' \
|
||
-H 'Idempotency-Key: <uuid>' \
|
||
--data '{"code":"123456"}'
|
||
```
|
||
|
||
```bash
|
||
curl -sS 'https://api.118im.com/v1/im/settings/independent-password' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'Authorization: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-APIKey: <api_key>' \
|
||
-H 'X-IM-Schema: 1' \
|
||
-H 'X-Tenant-Code: default' \
|
||
-H 'X-Request-ID: <uuid>' \
|
||
-H 'Idempotency-Key: <uuid>' \
|
||
--data '{"password":"6439","purpose":"youth_mode","action_token":"<set_password_token>"}'
|
||
```
|
||
|
||
```bash
|
||
curl -sS 'https://api.118im.com/v1/im/settings/youth-mode/enable' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'Authorization: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-APIKey: <api_key>' \
|
||
-H 'X-IM-Schema: 1' \
|
||
-H 'X-Tenant-Code: default' \
|
||
-H 'X-Request-ID: <uuid>' \
|
||
-H 'Idempotency-Key: <uuid>' \
|
||
--data '{"policy_version":1,"action_token":"<enable_action_token>"}'
|
||
```
|
||
|
||
关闭流程:
|
||
|
||
```bash
|
||
curl -sS 'https://api.118im.com/v1/im/settings/independent-password/verify' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'Authorization: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-APIKey: <api_key>' \
|
||
-H 'X-IM-Schema: 1' \
|
||
-H 'X-Tenant-Code: default' \
|
||
-H 'X-Request-ID: <uuid>' \
|
||
-H 'Idempotency-Key: <uuid>' \
|
||
--data '{"password":"6439","purpose":"youth_mode","action":"close_youth_mode"}'
|
||
```
|
||
|
||
```bash
|
||
curl -sS 'https://api.118im.com/v1/im/settings/youth-mode/disable' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'Authorization: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-Auth: Bearer <access_token>' \
|
||
-H 'X-MeetingIM-APIKey: <api_key>' \
|
||
-H 'X-IM-Schema: 1' \
|
||
-H 'X-Tenant-Code: default' \
|
||
-H 'X-Request-ID: <uuid>' \
|
||
-H 'Idempotency-Key: <uuid>' \
|
||
-H 'X-Action-Token: <close_token>' \
|
||
--data '{}'
|
||
```
|