XuqmGroup-Server/docs/API_ACCESS.md
XuqmGroup d13c6c9bc5 docs(api): 添加联调接口文档并实现功能服务管理
- 创建了完整的 API 联调文档,包含各服务地址、ID 约定和鉴权规则
- 实现了 FeatureServiceManager 用于管理服务激活和配置功能
- 添加了安全配置确保各服务间正确的身份验证机制
- 定义了统一的响应格式和错误码处理规范
- 集成了 IM、推送和更新服务的管理接口实现
2026-04-29 16:07:22 +08:00

443 行
18 KiB
Markdown

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

# XuqmGroup Server 联调接口文档
> 最后更新2026-04-28
## 线上入口
| 服务 | 地址 | 说明 |
|------|------|------|
| 租户服务 | `https://dev.xuqinmin.com/api/` | 认证、应用、子账号、运营平台 |
| IM HTTP | `https://dev.xuqinmin.com/api/im/` | IM 登录、消息发送、撤回、历史消息 |
| IM WebSocket | `wss://dev.xuqinmin.com/ws/im` | 实时消息 |
| 文件服务 | `https://file.dev.xuqinmin.com/api/file/` | 文件上传、下载、缩略图 |
| App 更新 | `https://dev.xuqinmin.com/api/v1/updates/` | 原生版本管理 |
| RN 热更新 | `https://dev.xuqinmin.com/api/v1/rn/` | Bundle 热更新 |
## ID 约定
这套工程里同时存在两种“应用标识”,不要混用:
| 名称 | 含义 | 常见位置 |
|------|------|----------|
| `tenant appId` | 租户平台应用主键,`tenant-service` 的 `/api/apps/{id}`、`/api/apps/{appId}/services` 使用它 | 租户平台、服务配置页 |
| `IM appKey` | IM 业务域作用域标识,`im-service` 的管理接口和消息接口虽然参数名仍叫 `appId`,但实际传的是这个值 | IM 管理页、IM HTTP 接口 |
结论:
- `tenant-platform` 的“服务配置”只认租户 `app.id`
- `tenant-platform` 的“IM 管理”必须带 `appKey`
- `tenant-platform` 的“离线推送 / 版本管理”只需要一次开通,平台差异配置在各自的管理页里维护,不再按 Android / iOS / 鸿蒙分别申请开通
- `im-service` 代码里沿用旧参数名 `appId`,但这是历史命名,调用方传的是 `appKey`
## 初始化管理员账号
| 字段 | 值 |
|------|----|
| 用户名 | `admin` |
| 初始密码 | `Admin@123456` |
| 登录接口 | `POST /api/auth/ops/login` |
## 统一响应
```json
{
"code": 200,
"status": "0",
"data": {},
"message": "success"
}
```
## 常见错误码
| `code` | `status` | 说明 |
|--------|----------|------|
| `200` | `"0"` | 成功 |
| `400` | `"1"` | 参数校验失败或请求不合法 |
| `401` | `"1"` | 未登录、Token 无效或已过期 |
| `403` | `"1"` | 无权限访问 |
| `500` | `"1"` | 服务端内部错误 |
## 鉴权规则
| 场景 | 鉴权方式 |
|------|----------|
| 租户平台接口 | `Authorization: Bearer <tenant_jwt>` |
| 运营平台接口 | `Authorization: Bearer <ops_jwt>` |
| IM HTTP 接口 | `Authorization: Bearer <im_jwt>` |
| IM WebSocket | `?token=<im_jwt>` |
| App 更新检查 | 无需登录 |
| RN 更新检查 | 无需登录 |
| Bundle 下载 | 无需登录 |
## 核心接口清单
### tenant-service
| 方法 | 路径 | 鉴权 | 说明 |
|------|------|------|------|
| GET | `/api/auth/captcha` | 否 | 获取图形验证码 |
| POST | `/api/auth/send-email-code` | 否 | 发送邮箱验证码 |
| POST | `/api/auth/register` | 否 | 注册主账号 |
| POST | `/api/auth/login` | 否 | 租户登录 |
| POST | `/api/auth/forgot-password` | 否 | 发送找回密码邮件 |
| POST | `/api/auth/reset-password` | 否 | 重置密码 |
| GET | `/api/apps` | 是 | 应用列表 |
| GET | `/api/apps/{id}` | 是 | 应用详情 |
| POST | `/api/apps` | 是 | 创建应用 |
| PUT | `/api/apps/{id}` | 是 | 更新应用 |
| DELETE | `/api/apps/{id}` | 是 | 删除应用 |
| GET | `/api/apps/{appId}/services` | 是 | 服务列表 |
| POST | `/api/apps/{appId}/services/toggle` | 是 | 开关服务 |
| POST | `/api/apps/{appId}/services/{id}/regenerate-key` | 是 | 重新生成服务密钥 |
| GET | `/api/sub-accounts` | 是 | 子账号列表 |
| POST | `/api/sub-accounts/send-verify-code` | 是 | 子账号邮箱验证码 |
| POST | `/api/sub-accounts/verify-email` | 是 | 校验子账号邮箱 |
| POST | `/api/sub-accounts` | 是 | 创建子账号 |
| DELETE | `/api/sub-accounts/{id}` | 是 | 禁用子账号 |
| POST | `/api/auth/ops/login` | 否 | 运营管理员登录 |
| GET | `/api/ops/tenants` | 是 | 运营租户列表 |
| POST | `/api/ops/tenants/{id}/toggle-status` | 是 | 租户启停 |
| GET | `/api/ops/statistics` | 是 | 统计面板 |
### im-service
| 方法 | 路径 | 鉴权 | 说明 |
|------|------|------|------|
| POST | `/api/im/auth/login` | 否 | 获取 IM Token;需要 `X-App-Timestamp` / `X-App-Nonce` / `X-App-Signature` |
| GET | `/api/im/accounts/{userId}` | 是 | 查询用户资料 |
| PUT | `/api/im/accounts/{userId}` | 是 | 更新自己的资料 |
| GET | `/api/im/accounts/search` | 否 | 搜索账号 |
| POST | `/api/im/accounts/import` | 是 | 导入单个账号 |
| POST | `/api/im/accounts/import/batch` | 是 | 批量导入账号 |
| DELETE | `/api/im/accounts/{userId}` | 是 | 删除账号 |
| GET | `/api/im/accounts/{userId}/exists` | 是 | 检查账号是否存在 |
| GET | `/api/im/groups/{groupId}/members` | 是 | 查询群成员列表 |
| GET | `/api/im/groups/{groupId}/members/search` | 是 | 搜索群成员 |
| POST | `/api/im/messages/send` | 是 | 发送消息TEXT / IMAGE / AUDIO / VIDEO / FILE / LOCATION / CUSTOM / NOTIFY / RICH_TEXT / CALL_AUDIO / CALL_VIDEO / FORWARD / QUOTE / MERGE |
| GET | `/api/im/messages/search` | 是 | 云端消息搜索 |
| PUT | `/api/im/messages/{id}` | 是 | 编辑自己发送的文本消息 |
| POST | `/api/im/messages/{id}/revoke` | 是 | 撤回消息 |
| GET | `/api/im/messages/history/{toId}` | 是 | 查询历史消息 |
| WS | `/ws/im` | IM Token | 建立实时连接 |
### push-service
| 方法 | 路径 | 鉴权 | 说明 |
|------|------|------|------|
| POST | `/api/push/register` | 是 | 注册设备 token |
| POST | `/api/push/receive-push` | 是 | 开启或关闭接收推送 |
| POST | `/api/push/send` | 是 | 发送推送通知 |
### file-service
| 方法 | 路径 | 鉴权 | 说明 |
|------|------|------|------|
| POST | `/api/file/upload` | 是 | 文件上传,按 SHA-256 去重 |
| GET | `/api/file/{hash}` | 否 | 按 hash 获取文件 |
| GET | `/api/file/{hash}/thumbnail` | 否 | 按 hash 获取缩略图 |
### update-service
| 方法 | 路径 | 鉴权 | 说明 |
|------|------|------|------|
| GET | `/api/v1/updates/app/check` | 否 | 检查 App 更新 |
| POST | `/api/v1/updates/app/upload` | 是 | 上传 App 版本,支持即时发布 / 定时发布 / 市场提交配置;Harmony 版本仅保存市场链接,不提供本地安装包下载 |
| POST | `/api/v1/updates/app/{id}/publish` | 是 | 发布 App 版本 |
| GET | `/api/v1/updates/app/list` | 是 | App 版本列表 |
| GET | `/api/v1/updates/files/apk/{filename}` | 否 | 下载 APK |
| GET | `/api/v1/rn/update/check` | 否 | 检查 RN 热更新 |
| POST | `/api/v1/rn/upload` | 是 | 上传 Bundle |
| POST | `/api/v1/rn/{id}/publish` | 是 | 发布 Bundle |
| GET | `/api/v1/rn/files/{appId}/{platform}/{moduleId}` | 否 | 下载 Bundle |
说明:这里的 `appId``appKey` 解析。当前 demo 默认使用 `ak_demo_chat`,`tenant-service` 会优先复用数据库里已有的应用;如果没有,会自动补一条默认 demo 应用和基础服务配置。`POST /api/im/auth/login` 还要求 demo-service 通过 AppSecret 生成签名头再转发给 IM 服务。
## curl 示例
### 运营平台登录
```bash
curl -X POST 'https://dev.xuqinmin.com/api/auth/ops/login' \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"Admin@123456"}'
```
### App 更新检查
```bash
curl 'https://dev.xuqinmin.com/api/v1/updates/app/check?appId=ak_demo_chat&platform=ANDROID&currentVersionCode=1'
curl 'https://dev.xuqinmin.com/api/v1/updates/app/check?appId=ak_demo_chat&platform=HARMONY&currentVersionCode=1'
```
Harmony 平台只跳转应用市场,不提供本地安装包下载。
### RN 热更新检查
```bash
curl 'https://dev.xuqinmin.com/api/v1/rn/update/check?appId=ak_demo_chat&platform=ANDROID&moduleId=chat-home&currentVersion=1.0.0'
```
### IM 登录
```bash
curl -X POST 'https://dev.xuqinmin.com/api/im/auth/login?appId=ak_demo_chat&userId=demo_alice'
```
返回示例中的 `data` 会同时包含 `token``expiresAt`,用于客户端提前做静默续签。
### Demo IM 刷新
```bash
curl -X POST 'https://dev.xuqinmin.com/api/demo/auth/refresh-im?appId=ak_demo_chat' \
-H 'Authorization: Bearer <demo_jwt>'
```
该接口会基于 demo 登录态重新签发 IM token,并返回新的 `expiresAt`
### IM 会话与关系链
```bash
curl 'https://dev.xuqinmin.com/api/im/accounts/user_001?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/accounts/import?appId=ak_demo_chat' \
-H 'Content-Type: application/json' \
-d '{"userId":"user_001","nickname":"Alice"}'
curl -X POST 'https://dev.xuqinmin.com/api/im/accounts/import/batch?appId=ak_demo_chat' \
-H 'Content-Type: application/json' \
-d '[{"userId":"user_001"},{"userId":"user_002"}]'
curl -X DELETE 'https://dev.xuqinmin.com/api/im/accounts/user_001?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/accounts/user_001/exists?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/conversations?appId=ak_demo_chat'
curl -X PUT 'https://dev.xuqinmin.com/api/im/conversations/user_002/pinned?appId=ak_demo_chat&chatType=SINGLE&pinned=true'
curl -X PUT 'https://dev.xuqinmin.com/api/im/conversations/user_002/draft?appId=ak_demo_chat&chatType=SINGLE&draft=hello'
curl -X DELETE 'https://dev.xuqinmin.com/api/im/conversations/user_002?appId=ak_demo_chat&chatType=SINGLE'
curl 'https://dev.xuqinmin.com/api/im/groups?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/groups/public?appId=ak_demo_chat&keyword=demo'
curl 'https://dev.xuqinmin.com/api/im/groups/search?appId=ak_demo_chat&keyword=demo&size=20'
curl 'https://dev.xuqinmin.com/api/im/groups/group_001/members?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/groups/group_001/members/search?appId=ak_demo_chat&keyword=demo&size=20'
curl 'https://dev.xuqinmin.com/api/im/messages/search?appId=ak_demo_chat&keyword=hello&page=0&size=20'
curl 'https://dev.xuqinmin.com/api/im/admin/webhooks?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests?appId=ak_demo_chat&remark=申请加入'
curl 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests/req_001/accept?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests/req_001/reject?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/blacklist?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/friend-requests?appId=ak_demo_chat&direction=incoming'
```
### IM 管理后台接口
```bash
curl 'https://dev.xuqinmin.com/api/im/admin/users?appId=ak_demo_chat&page=0&size=20'
curl 'https://dev.xuqinmin.com/api/im/admin/groups?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/admin/messages?appId=ak_demo_chat&userA=user_001&userB=user_002&page=0&size=20'
curl 'https://dev.xuqinmin.com/api/im/admin/operation-logs?appId=ak_demo_chat&page=0&size=20'
curl -X POST 'https://dev.xuqinmin.com/api/im/admin/messages/msg_001/revoke?appId=ak_demo_chat'
curl -X DELETE 'https://dev.xuqinmin.com/api/im/admin/groups/group_001'
curl 'https://dev.xuqinmin.com/api/im/admin/friend-requests?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/admin/friend-requests/req_001/accept?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/admin/blacklist?appId=ak_demo_chat' \
-H 'Content-Type: application/json' \
-d '{"userId":"user_001","blockedUserId":"user_002"}'
curl 'https://dev.xuqinmin.com/api/im/admin/groups/group_001/members?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/admin/groups/group_001/join-requests?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/admin/groups/group_001/join-requests/req_001/accept?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/admin/keyword-filters?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/admin/global-mute?appId=ak_demo_chat'
```
### IM Webhook 回调
IM 服务会在消息、好友和已读等事件发生后,以 `POST` 方式向你配置的回调地址推送事件。
请求头:
- `Content-Type: application/json`
- `X-App-Id`: 应用 `appId`
- `X-App-Timestamp`: 请求时间戳
- `X-App-Nonce`: 随机串
- `X-App-Signature`: 签名结果
签名规则:
```text
HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(body))
```
统一请求体结构:
```json
{
"callbackId": "uuid",
"callbackType": "message",
"callbackEvent": "message.sent",
"requestTime": 1714360000000,
"payload": {},
"signature": null,
"appId": "ak_demo_chat"
}
```
字段说明:
| 字段 | 说明 |
|------|------|
| `callbackId` | 回调唯一 ID,用于去重和排障 |
| `callbackType` | 回调大类,例如 `message`、`friend`、`group`、`blacklist` |
| `callbackEvent` | 具体事件,例如 `message.sent`、`friend.request.sent` |
| `requestTime` | 毫秒时间戳 |
| `payload` | 事件数据,结构随事件变化 |
| `signature` | 预留字段,当前由请求头承载 |
| `appId` | 回调所属应用 ID |
`payload` 会根据事件类型变化:
| 事件 | payload 类型 | 说明 |
|------|-------------|------|
| `message.sent` | `ImMessageEntity` | 发送消息成功后触发 |
| `message.revoked` | `ImMessageEntity` | 撤回消息后触发 |
| `message.edited` | `ImMessageEntity` | 编辑文本消息后触发 |
| `message.read` | `MessageReadCallbackPayload` | 已读回执同步后触发 |
| `friend.request.sent` | `FriendRequestCallbackPayload` | 好友申请创建后触发 |
| `friend.request.accepted` | `FriendRequestCallbackPayload` | 好友申请通过后触发 |
| `friend.request.rejected` | `FriendRequestCallbackPayload` | 好友申请拒绝后触发 |
| `group.join.request.sent` | `GroupJoinRequestCallbackPayload` | 入群申请创建后触发 |
| `group.join.request.accepted` | `GroupJoinRequestCallbackPayload` | 入群申请通过后触发 |
| `group.join.request.rejected` | `GroupJoinRequestCallbackPayload` | 入群申请拒绝后触发 |
| `blacklist.added` | `BlacklistCallbackPayload` | 黑名单记录新增后触发 |
| `blacklist.removed` | `BlacklistCallbackPayload` | 黑名单记录移除后触发 |
各 payload 字段如下:
#### `MessageReadCallbackPayload`
```json
{
"appId": "ak_demo_chat",
"readerId": "user_001",
"peerId": "user_002",
"groupId": null,
"chatType": "SINGLE",
"readAt": 1714360000000,
"messageIds": ["msg_001", "msg_002"]
}
```
#### `FriendRequestCallbackPayload`
```json
{
"appId": "ak_demo_chat",
"requestId": "req_001",
"fromUserId": "user_001",
"toUserId": "user_002",
"remark": "hi",
"status": "PENDING",
"reviewedAt": null
}
```
#### `GroupJoinRequestCallbackPayload`
```json
{
"appId": "ak_demo_chat",
"requestId": "req_001",
"groupId": "group_001",
"groupName": "技术群",
"requesterId": "user_003",
"remark": "申请加入",
"status": "PENDING",
"reviewedAt": null
}
```
#### `BlacklistCallbackPayload`
```json
{
"appId": "ak_demo_chat",
"id": "blk_001",
"userId": "user_001",
"blockedUserId": "user_002",
"action": "ADD",
"createdAt": 1714360000000
}
```
说明:
- 回调发送失败不会影响主流程,只记录日志。
- 当前版本的回调配置是按应用维度管理的,多个地址会同时接收同一事件。
- 新增的好友、入群、黑名单回调都复用同一 envelope 和签名规则,payload 字段按上表中的类型序列化。
- 接收方建议用 `callbackId` 做幂等去重,避免重复投递造成重复处理。
- 服务端当前不做重试队列,失败只会在调用端记录日志。
示例验签:
```ts
import crypto from 'crypto'
export function verifyWebhook({
appId,
timestamp,
nonce,
body,
signature,
appSecret,
}: {
appId: string
timestamp: string
nonce: string
body: string
signature: string
appSecret: string
}) {
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex')
const payload = `${appId}\n${timestamp}\n${nonce}\n${bodyHash}`
const expected = crypto.createHmac('sha256', appSecret).update(payload, 'utf8').digest('hex')
return expected === signature
}
```
### 好友申请 / 黑名单
```bash
curl -X POST 'https://dev.xuqinmin.com/api/im/friend-requests?appId=ak_demo_chat&toUserId=user_002&remark=hi'
curl -X POST 'https://dev.xuqinmin.com/api/im/friend-requests/req_001/accept?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/friend-requests/req_001/reject?appId=ak_demo_chat'
curl 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests?appId=ak_demo_chat&remark=申请加入'
curl -X POST 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests/req_001/accept?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/groups/group_001/join-requests/req_001/reject?appId=ak_demo_chat'
curl -X POST 'https://dev.xuqinmin.com/api/im/blacklist?appId=ak_demo_chat&blockedUserId=user_002'
curl -X DELETE 'https://dev.xuqinmin.com/api/im/blacklist?appId=ak_demo_chat&blockedUserId=user_002'
```
### IM 历史过滤查询
```bash
curl 'https://dev.xuqinmin.com/api/im/messages/history/user_002?appId=ak_demo_chat&page=0&size=20&keyword=hello&msgType=TEXT'
curl 'https://dev.xuqinmin.com/api/im/messages/group-history/group_001?appId=ak_demo_chat&page=0&size=20&keyword=user_002&startTime=2026-04-27T00:00:00&endTime=2026-04-27T23:59:59'
```
支持的筛选参数:`keyword`、`msgType`、`startTime`、`endTime`。
### IM 媒体消息
```bash
curl -X POST 'https://file.dev.xuqinmin.com/api/file/upload' \
-H 'Authorization: Bearer <im_jwt>' \
-F 'file=@image.jpg'
curl -X POST 'https://im.dev.xuqinmin.com/api/im/messages/send?appId=ak_demo_chat' \
-H 'Authorization: Bearer <im_jwt>' \
-H 'Content-Type: application/json' \
-d '{"toId":"user_002","chatType":"SINGLE","msgType":"IMAGE","content":"{\"url\":\"https://file.dev.xuqinmin.com/api/file/abc\"}"}'
```
当前 `im-service` 的消息发送接口已经支持 `AUDIO`、`LOCATION`、`CUSTOM`、`RICH_TEXT`、`FORWARD`、`QUOTE`、`MERGE`、`CALL_AUDIO`、`CALL_VIDEO` 这些通用类型,客户端只需按协议传入对应 JSON 内容即可。
群聊消息在历史查询和实时推送里会携带 `groupReadCount`,用于展示 `N 人已读`