948 lines
25 KiB
Markdown
948 lines
25 KiB
Markdown
# IM_API_NEW 接口文档
|
||
|
||
基于微服务架构的即时通讯系统,包含 6 个独立 WebApi 服务。
|
||
|
||
## 通用说明
|
||
|
||
### 统一响应格式 `Result<T>`
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": { }
|
||
}
|
||
```
|
||
|
||
- `code`:业务状态码,`0` 表示成功(见下方状态码表)
|
||
- `message`:状态描述
|
||
- `data`:业务数据,失败时为 `null`
|
||
|
||
### 认证
|
||
|
||
除特别说明外,所有接口需在请求头携带 JWT:
|
||
|
||
```
|
||
Authorization: Bearer {token}
|
||
```
|
||
|
||
当前用户 ID 从 Token 的 `NameIdentifier` 声明中获取,无需在请求体重复传递。
|
||
|
||
### 路由约定
|
||
|
||
控制器统一采用 `api/[controller]/[action]` 路由模板(FileService 例外,见对应章节)。
|
||
|
||
---
|
||
|
||
## 1. 认证服务 (User.WebApi - Auth)
|
||
|
||
> 以下接口**无需认证**。
|
||
|
||
### POST /api/Auth/Login — 登录
|
||
|
||
请求体:
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userName | string | 是 | 5-20 字符 |
|
||
| password | string | 是 | ≤50 字符 |
|
||
|
||
响应:`Result<LoginResponse>`
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"userName": "zhangsan",
|
||
"password": "P@ssw0rd"
|
||
}
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"userId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"refreshToken": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
|
||
"expired": "2026-06-27T10:30:00Z",
|
||
"userName": "zhangsan",
|
||
"nickName": "张三",
|
||
"avatar": "https://cdn.example.com/avatar/zhangsan.png",
|
||
"creationTime": "2026-01-15T08:00:00+00:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
失败示例(密码错误,code 2002):
|
||
```json
|
||
{
|
||
"code": 2002,
|
||
"message": "密码错误",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### POST /api/Auth/Register — 注册
|
||
|
||
请求体:
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userName | string | 是 | 5-20 字符 |
|
||
| password | string | 是 | 6-50 字符 |
|
||
| nickName | string | 是 | ≤50 字符 |
|
||
|
||
响应:`Result<UserResponse>`
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"userName": "zhangsan",
|
||
"password": "P@ssw0rd",
|
||
"nickName": "张三"
|
||
}
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"userName": "zhangsan",
|
||
"nickName": "张三",
|
||
"email": null,
|
||
"phone": null,
|
||
"region": "",
|
||
"description": "",
|
||
"avatar": null,
|
||
"creationTime": "2026-06-26T08:00:00+00:00",
|
||
"deletion": null
|
||
}
|
||
}
|
||
```
|
||
|
||
### POST /api/Auth/Refresh — 刷新令牌
|
||
|
||
请求体:
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| refreshToken | string | 是 | 刷新令牌 |
|
||
|
||
响应:`Result<LoginResponse>`
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"refreshToken": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
|
||
}
|
||
```
|
||
|
||
响应示例:同 Login 的 `Result<LoginResponse>`。
|
||
|
||
**LoginResponse 结构**
|
||
```json
|
||
{
|
||
"userId": "guid", "token": "string", "refreshToken": "string",
|
||
"expired": "datetime", "userName": "string", "nickName": "string",
|
||
"avatar": "string|null", "creationTime": "datetime"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 用户服务 (User.WebApi - User)
|
||
|
||
> 需认证。
|
||
|
||
### GET /api/User/Me — 当前用户信息
|
||
响应:`Result<UserResponse>`
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"userName": "zhangsan",
|
||
"nickName": "张三",
|
||
"email": "zhangsan@example.com",
|
||
"phone": "13800000000",
|
||
"region": "广东·深圳",
|
||
"description": "这个人很懒,什么都没写",
|
||
"avatar": "https://cdn.example.com/avatar/zhangsan.png",
|
||
"creationTime": "2026-01-15T08:00:00+00:00",
|
||
"deletion": null
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET /api/User/Find?userId={guid} — 查询指定用户
|
||
响应:`Result<UserResponse>`(结构同上)
|
||
|
||
### GET /api/User/FindByUname?username={string} — 按用户名查询
|
||
响应:`Result<UserResponse>`(结构同上)
|
||
|
||
### POST /api/User/Update — 更新资料
|
||
请求体(均可选):`nickName`、`region`、`avatar`、`description`
|
||
响应:`Result<UserResponse>`
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"nickName": "张三丰",
|
||
"region": "湖北·武当山",
|
||
"avatar": "https://cdn.example.com/avatar/new.png",
|
||
"description": "太极宗师"
|
||
}
|
||
```
|
||
|
||
### POST /api/User/GetUsersByIds — 批量查询用户
|
||
请求体:`["guid", "guid"]`(Guid 数组)
|
||
响应:`Result<List<UserResponse>>`
|
||
|
||
请求示例:
|
||
```json
|
||
[
|
||
"8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
|
||
]
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": [
|
||
{ "id": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b", "userName": "zhangsan", "nickName": "张三", "email": null, "phone": null, "region": "广东·深圳", "description": "", "avatar": null, "creationTime": "2026-01-15T08:00:00+00:00", "deletion": null }
|
||
]
|
||
}
|
||
```
|
||
|
||
**UserResponse 结构**
|
||
```json
|
||
{
|
||
"id": "guid", "userName": "string", "nickName": "string",
|
||
"email": "string|null", "phone": "string|null", "region": "string",
|
||
"description": "string", "avatar": "string|null",
|
||
"creationTime": "datetime", "deletion": "datetime|null"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 联系人服务 (ContactService.WebApi)
|
||
|
||
> 需认证。
|
||
|
||
### 好友 (Friend)
|
||
|
||
| 方法 | 路径 | 说明 | 参数 |
|
||
|------|------|------|------|
|
||
| GET | /api/Friend/List | 好友列表 | - |
|
||
| POST | /api/Friend/Delete | 删除好友 | `friendId` (query) |
|
||
| POST | /api/Friend/Block | 拉黑好友 | `friendId` (query) |
|
||
| GET | /api/Friend/CheckFriend | 检查好友关系 | `userId`, `targetId` (query) |
|
||
|
||
`GET /api/Friend/List` 响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": [
|
||
{
|
||
"id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"avatar": "https://cdn.example.com/avatar/lisi.png",
|
||
"nickName": "李四",
|
||
"remarkName": "老李",
|
||
"createTime": "2026-02-01T10:00:00",
|
||
"updateTime": null,
|
||
"status": "Pending"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
`POST /api/Friend/Delete?friendId={guid}` 响应示例:
|
||
```json
|
||
{ "code": 0, "message": "成功", "data": true }
|
||
```
|
||
|
||
`GET /api/Friend/CheckFriend?userId={guid}&targetId={guid}` 响应示例:
|
||
```json
|
||
{ "code": 0, "message": "成功", "data": true }
|
||
```
|
||
|
||
### 好友请求 (FriendRequest)
|
||
|
||
**POST /api/FriendRequest/Add** — 发起好友申请
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| targetId | guid | 是 | 被申请人 |
|
||
| description | string | 否 | 附言 |
|
||
| remarkName | string | 否 | 备注名 |
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"description": "我是张三,加个好友",
|
||
"remarkName": "老李"
|
||
}
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f",
|
||
"ownerId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"ownerNickName": "张三",
|
||
"ownerAvatar": "https://cdn.example.com/avatar/zhangsan.png",
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"targetNickName": "李四",
|
||
"targetAvatar": "https://cdn.example.com/avatar/lisi.png",
|
||
"description": "我是张三,加个好友",
|
||
"state": "Pending",
|
||
"remarkName": "老李",
|
||
"creationTime": "2026-06-26T09:00:00+00:00",
|
||
"deletion": null,
|
||
"modificationTime": null
|
||
}
|
||
}
|
||
```
|
||
|
||
**POST /api/FriendRequest/Handle** — 处理申请
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| requestId | guid | 是 | 请求 ID |
|
||
| action | string | 是 | `"Accpet"` 同意, `"Reject"` 拒绝, `"Block"` 拉黑 |
|
||
| remarkName | string | 同意时必填 | 备注名 |
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"requestId": "c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f",
|
||
"action": "Accpet",
|
||
"remarkName": "张三"
|
||
}
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{ "code": 0, "message": "成功", "data": true }
|
||
```
|
||
|
||
**GET /api/FriendRequest/List** — 我相关的申请列表
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": [
|
||
{
|
||
"id": "c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f",
|
||
"ownerId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"ownerNickName": "张三",
|
||
"ownerAvatar": "https://cdn.example.com/avatar/zhangsan.png",
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"targetNickName": "李四",
|
||
"targetAvatar": "https://cdn.example.com/avatar/lisi.png",
|
||
"description": "我是张三,加个好友",
|
||
"state": "Passed",
|
||
"remarkName": "老李",
|
||
"creationTime": "2026-06-26T09:00:00+00:00",
|
||
"deletion": null,
|
||
"modificationTime": "2026-06-26T09:05:00+00:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**FriendRequestResponse 状态 (State)**:`"Pending"` 待通过, `"Declined"` 已拒绝, `"Passed"` 已同意, `"Blocked"` 已拉黑
|
||
|
||
---
|
||
|
||
## 4. 群组服务 (GroupService.WebApi)
|
||
|
||
> 需认证(GroupMember 部分接口除外)。
|
||
|
||
### 群组 (Group)
|
||
|
||
| 方法 | 路径 | 说明 | 参数 |
|
||
|------|------|------|------|
|
||
| GET | /api/Group/GetAll | 我加入的群列表 | - |
|
||
| GET | /api/Group/GetOne | 群详情 | `groupId` (query) |
|
||
| POST | /api/Group/Create | 创建群 | body: `name` (≤20) |
|
||
| POST | /api/Group/Update | 更新群 | body: 见下 |
|
||
|
||
Update 请求体:
|
||
| 字段 | 类型 | 必填 |
|
||
|------|------|------|
|
||
| groupId | guid | 是 |
|
||
| groupName | string | 否 |
|
||
| avatar | string | 否 |
|
||
| description | string | 否 |
|
||
|
||
`POST /api/Group/Create` 请求示例:
|
||
```json
|
||
{ "name": "技术交流群" }
|
||
```
|
||
|
||
`POST /api/Group/Update` 请求示例:
|
||
```json
|
||
{
|
||
"groupId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"groupName": "技术交流群(2026)",
|
||
"avatar": "https://cdn.example.com/group/tech.png",
|
||
"description": "欢迎交流技术"
|
||
}
|
||
```
|
||
|
||
`GET /api/Group/GetOne?groupId={guid}` 响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"name": "技术交流群",
|
||
"groupMaster": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"authority": "REQUIRE_CONSENT",
|
||
"allMembersBanned": false,
|
||
"status": "Normal",
|
||
"announcement": "欢迎加入",
|
||
"avatar": "https://cdn.example.com/group/tech.png",
|
||
"maxSequenceId": 1024,
|
||
"lastMessage": "晚上好",
|
||
"lastSenderName": "张三",
|
||
"created": "2026-03-01T08:00:00+00:00",
|
||
"updated": "2026-06-26T09:00:00+00:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 群成员 (GroupMember)
|
||
|
||
| 方法 | 路径 | 说明 | 参数 |
|
||
|------|------|------|------|
|
||
| GET | /api/GroupMember/CheckMember | 检查成员 | `userId`, `groupId` (query) |
|
||
| GET | /api/GroupMember/List | 成员列表 | `groupId` (query) |
|
||
| POST | /api/GroupMember/Delete | 移除成员 | `memberId` (query) |
|
||
|
||
`GET /api/GroupMember/List?groupId={guid}` 响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": [
|
||
{
|
||
"id": "f1e2d3c4-b5a6-9788-1a2b-3c4d5e6f7a8b",
|
||
"userId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"groupNickName": "群主张三",
|
||
"avatar": "https://cdn.example.com/avatar/zhangsan.png",
|
||
"groupId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"role": "Master",
|
||
"created": "2026-03-01T08:00:00+00:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**成员角色 (Role)**:`"Normal"` 普通成员, `"Administrator"` 管理员, `"Master"` 群主
|
||
|
||
### 群邀请 (GroupInvitation)
|
||
|
||
| 方法 | 路径 | 说明 | 参数 |
|
||
|------|------|------|------|
|
||
| POST | /api/GroupInvitation/Send | 发送邀请 | body: `groupId`, `userId` |
|
||
| GET | /api/GroupInvitation/Get | 邀请详情 | `invitationId` (query) |
|
||
| POST | /api/GroupInvitation/Handle | 处理邀请 | `invitationId`, `action` (query) |
|
||
|
||
`POST /api/GroupInvitation/Send` 请求示例:
|
||
```json
|
||
{
|
||
"groupId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"userId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
|
||
}
|
||
```
|
||
|
||
`GET /api/GroupInvitation/Get?invitationId={guid}` 响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e",
|
||
"groupId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"groupAvatar": "https://cdn.example.com/group/tech.png",
|
||
"groupName": "技术交流群",
|
||
"userId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"userAvatar": "https://cdn.example.com/avatar/lisi.png",
|
||
"userNickName": "李四",
|
||
"operatorId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"operatorName": "张三",
|
||
"operatorAvatar": "https://cdn.example.com/avatar/zhangsan.png",
|
||
"state": "Pending",
|
||
"created": "2026-06-26T09:00:00+00:00",
|
||
"updated": "2026-06-26T09:00:00+00:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
**邀请处理 action**:`"Accept"` 接受, `"Reject"` 拒绝
|
||
**邀请状态 (State)**:`"Pending"` 待被邀请人同意, `"Passed"` 已同意, `"Reject"` 拒绝
|
||
|
||
### 入群请求 (GroupRequest)
|
||
|
||
| 方法 | 路径 | 说明 | 参数 |
|
||
|------|------|------|------|
|
||
| POST | /api/GroupRequest/Send | 申请入群 | body: `groupId`, `desc` (≤20) |
|
||
| POST | /api/GroupRequest/Handle | 处理申请 | `requestId`, `action` (query) |
|
||
| GET | /api/GroupRequest/Find | 申请详情 | `id` (query) |
|
||
| GET | /api/GroupRequest/List | 申请列表 | - |
|
||
|
||
`POST /api/GroupRequest/Send` 请求示例:
|
||
```json
|
||
{
|
||
"groupId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"desc": "我想加入学习"
|
||
}
|
||
```
|
||
|
||
`GET /api/GroupRequest/Find?id={guid}` 响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f",
|
||
"groupId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"groupAvatar": "https://cdn.example.com/group/tech.png",
|
||
"groupName": "技术交流群",
|
||
"userId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"userAvatar": "https://cdn.example.com/avatar/lisi.png",
|
||
"userNickName": "李四",
|
||
"operatorId": "00000000-0000-0000-0000-000000000000",
|
||
"operatorName": "",
|
||
"operatorAvatar": null,
|
||
"state": "Pending",
|
||
"description": "我想加入学习",
|
||
"created": "2026-06-26T09:00:00+00:00",
|
||
"updated": "2026-06-26T09:00:00+00:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
**入群处理 action**:`"Accept"` 接受, `"Reject"` 拒绝
|
||
**入群状态 (State)**:`"Pending"` 待管理员同意, `"Declined"` 已拒绝, `"Passed"` 已通过
|
||
|
||
**群权限 (Authority)**:`"REQUIRE_CONSENT"` 需管理员同意, `"ANYONE_CAN_JOIN"` 任意人可加, `"NOT_ALLOWED_TO_JOIN"` 不允许加入
|
||
**群状态 (Status)**:`"Normal"` 正常, `"Blocked"` 封禁
|
||
|
||
---
|
||
|
||
## 5. 消息服务 (MessageService.WebApi)
|
||
|
||
> 需认证。
|
||
|
||
### 会话 (Conversation)
|
||
|
||
| 方法 | 路径 | 说明 | 参数 |
|
||
|------|------|------|------|
|
||
| GET | /api/Conversation/List | 会话列表 | - |
|
||
| GET | /api/Conversation/Get | 会话详情 | `id` (query) |
|
||
| POST | /api/Conversation/MarkRead | 清零未读 | `conversationId` (query) |
|
||
|
||
`GET /api/Conversation/List` 响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": [
|
||
{
|
||
"id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
|
||
"userId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"targetAvatar": "https://cdn.example.com/avatar/lisi.png",
|
||
"targetName": "李四",
|
||
"lastReadSequenceId": 1020,
|
||
"unreadCount": 4,
|
||
"chatType": "PRIVATE",
|
||
"lastMessage": "晚上一起吃饭吗?",
|
||
"dateTime": "2026-06-26T18:30:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
`POST /api/Conversation/MarkRead?conversationId={guid}` 响应示例:
|
||
```json
|
||
{ "code": 0, "message": "成功", "data": null }
|
||
```
|
||
|
||
### 消息 (Message)
|
||
|
||
**POST /api/Message/Send** — 发送消息
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| clientMsgId | guid | 是 | 客户端消息 ID(去重) |
|
||
| targetId | guid | 是 | 接收方(单聊=用户,群聊=群) |
|
||
| chatType | string | 是 | `"PRIVATE"` 单聊, `"GROUP"` 群聊 |
|
||
| msgType | string | 是 | 见消息类型表 |
|
||
| quoteMessageId | guid | 否 | 引用消息 ID |
|
||
| ext | object | 否 | 扩展字段 (键值对) |
|
||
| text | string | 否 | 文本内容 |
|
||
| url | string | 否 | 媒体 URL |
|
||
| width / height | int | 否 | 图片/视频尺寸 |
|
||
| thumb | string | 否 | 缩略图 |
|
||
| duration | int | 否 | 时长(语音/视频) |
|
||
|
||
文本消息请求示例:
|
||
```json
|
||
{
|
||
"clientMsgId": "7e8f9a0b-1c2d-3e4f-5a6b-7c8d9e0f1a2b",
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"chatType": "PRIVATE",
|
||
"msgType": "Text",
|
||
"text": "晚上一起吃饭吗?"
|
||
}
|
||
```
|
||
|
||
图片消息请求示例:
|
||
```json
|
||
{
|
||
"clientMsgId": "8f9a0b1c-2d3e-4f5a-6b7c-8d9e0f1a2b3c",
|
||
"targetId": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"chatType": "GROUP",
|
||
"msgType": "Image",
|
||
"url": "https://cdn.example.com/img/photo.jpg",
|
||
"width": 1920,
|
||
"height": 1080,
|
||
"thumb": "https://cdn.example.com/img/photo_thumb.jpg",
|
||
"quoteMessageId": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
|
||
"ext": { "source": "album" }
|
||
}
|
||
```
|
||
|
||
发送响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "9a0b1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d",
|
||
"clientMsgId": "7e8f9a0b-1c2d-3e4f-5a6b-7c8d9e0f1a2b",
|
||
"chatType": "PRIVATE",
|
||
"msgType": "Text",
|
||
"senderId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"state": "Sent",
|
||
"streamKey": "stream:private:xxx",
|
||
"sequenceId": 1021,
|
||
"creationTime": "2026-06-26T18:35:00+00:00",
|
||
"content": {
|
||
"fallback": "晚上一起吃饭吗?",
|
||
"body": { "text": "晚上一起吃饭吗?" },
|
||
"ext": {},
|
||
"quote": null
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**POST /api/Message/WithDraw** — 撤回消息
|
||
参数:`msgId` (query)
|
||
|
||
响应示例:
|
||
```json
|
||
{ "code": 0, "message": "成功", "data": true }
|
||
```
|
||
|
||
**GET /api/Message/GetMessages** — 拉取消息
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| conversationId | guid | 会话 ID |
|
||
| cursor | long? | 游标 |
|
||
| direction | int | 方向 |
|
||
| limit | int | 数量 |
|
||
|
||
请求示例:
|
||
```
|
||
GET /api/Message/GetMessages?conversationId=d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a&cursor=1021&direction=0&limit=20
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"messages": [
|
||
{
|
||
"id": "9a0b1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d",
|
||
"clientMsgId": "7e8f9a0b-1c2d-3e4f-5a6b-7c8d9e0f1a2b",
|
||
"chatType": "PRIVATE",
|
||
"msgType": 0,
|
||
"senderId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"targetId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
||
"state": "Sent",
|
||
"streamKey": "stream:private:xxx",
|
||
"sequenceId": 1021,
|
||
"creationTime": "2026-06-26T18:35:00+00:00",
|
||
"content": { "fallback": "晚上一起吃饭吗?", "body": { "text": "晚上一起吃饭吗?" }, "ext": {}, "quote": null }
|
||
}
|
||
],
|
||
"hasmore": true
|
||
}
|
||
}
|
||
```
|
||
|
||
**消息类型 (MsgType)**:`"Text"` 文本, `"Image"` 图片, `"Voice"` 语音, `"Video"` 视频, `"File"` 文件, `"VoiceChat"` 语音通话, `"VideoChat"` 视频通话
|
||
**会话类型 (ChatType)**:`"PRIVATE"` 单聊, `"GROUP"` 群聊
|
||
|
||
---
|
||
|
||
## 6. 文件服务 (FileService.WebApi)
|
||
|
||
> 需认证。路由为 `api/File` 与 `api/FileTask`。
|
||
|
||
### 文件 (File) — 小文件直传/下载
|
||
|
||
**POST /api/File/simple-upload** — 单次直传(头像/封面等),**支持秒传**
|
||
- Content-Type: `multipart/form-data`
|
||
- 表单字段:`file` (文件), `isPublic` (bool,true 落公开桶返回直链)
|
||
- 秒传:服务端计算文件 MD5 后,若已存在相同 checksum 的文件则直接返回已有记录,**跳过存储写入**
|
||
- 响应:`Result<FileResponse>`
|
||
|
||
请求示例(form-data):
|
||
```
|
||
file: (二进制文件)
|
||
isPublic: true
|
||
```
|
||
|
||
秒传命中响应(与正常上传返回一致,但实际未写入存储):
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "a0b1c2d3-e4f5-6a7b-8c9d-0e1f2a3b4c5d",
|
||
"ownerId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"fileName": "avatar.png",
|
||
"fileSize": 20480,
|
||
"contentType": "image/png",
|
||
"state": "Uploaded",
|
||
"storageLocation": {
|
||
"storageProvider": "local",
|
||
"bucket": "im-public",
|
||
"objectKey": "2026/06/26/abc123.png",
|
||
"region": "local"
|
||
},
|
||
"checkSum": "d41d8cd98f00b204e9800998ecf8427e",
|
||
"created": "2026-06-26T09:00:00+00:00",
|
||
"updated": "2026-06-26T09:00:00+00:00",
|
||
"url": "https://cdn.example.com/public/avatar.png"
|
||
}
|
||
}
|
||
```
|
||
|
||
正常上传响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"id": "a0b1c2d3-e4f5-6a7b-8c9d-0e1f2a3b4c5d",
|
||
"ownerId": "8f3a2c10-1b2c-4d5e-9a8b-7c6d5e4f3a2b",
|
||
"fileName": "avatar.png",
|
||
"fileSize": 20480,
|
||
"contentType": "image/png",
|
||
"state": "Uploaded",
|
||
"storageLocation": {
|
||
"storageProvider": "local",
|
||
"bucket": "im-public",
|
||
"objectKey": "2026/06/26/abc123.png",
|
||
"region": "local"
|
||
},
|
||
"checkSum": "d41d8cd98f00b204e9800998ecf8427e",
|
||
"created": "2026-06-26T09:00:00+00:00",
|
||
"updated": "2026-06-26T09:00:00+00:00",
|
||
"url": "https://cdn.example.com/public/avatar.png"
|
||
}
|
||
}
|
||
```
|
||
|
||
**GET /api/File/{id}** — 获取文件信息(含直链 Url,私有文件 Url 为 null)
|
||
响应:`Result<FileResponse>`(结构同上;私有文件 `url` 为 `null`)
|
||
|
||
**GET /api/File/{id}/content** — 鉴权下载文件内容(私有文件预览/下载)
|
||
响应:文件流 (`File` 结果,二进制;非 `Result<T>` 包装)
|
||
|
||
### 分片上传任务 (FileTask) — 大文件分片
|
||
|
||
> 完整流程:Init → GetUploadUrl → UploadPart → Complete(四步)
|
||
> 支持秒传、分片大小校验、上传进度查询。
|
||
|
||
**POST /api/FileTask/init** — 初始化上传任务(支持秒传)
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| conversationId | guid | 会话 ID |
|
||
| fileName | string | 文件名 |
|
||
| fileSize | long | 文件大小(≤ 1GB,由 `MaxObjectSizeBytes` 控制) |
|
||
| contentType | string | MIME 类型 |
|
||
| checkSum | string | 校验和(必须,用于秒传去重) |
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"conversationId": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
|
||
"fileName": "movie.mp4",
|
||
"fileSize": 104857600,
|
||
"contentType": "video/mp4",
|
||
"checkSum": "9e107d9d372bb6826bd81d3542a419d6"
|
||
}
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"taskId": "b1c2d3e4-f5a6-7b8c-9d0e-1f2a3b4c5d6e",
|
||
"uploadSessionId": "sess_abc123",
|
||
"storageLocation": {
|
||
"storageProvider": "local",
|
||
"bucket": "im-local",
|
||
"objectKey": "data/im-files/2026/06/27/movie.mp4",
|
||
"region": "local"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
秒传命中(checksum 匹配已完成的文件,跳过初始化):
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"taskId": "a0b1c2d3-e4f5-6a7b-8c9d-0e1f2a3b4c5d",
|
||
"uploadSessionId": "a0b1c2d3-e4f5-6a7b-8c9d-0e1f2a3b4c5d",
|
||
"storageLocation": {
|
||
"storageProvider": "local",
|
||
"bucket": "im-local",
|
||
"objectKey": "2026/06/26/existing.png",
|
||
"region": "local"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
此时 `taskId` 即为已存在文件的 ID,前端可直接跳到 Complete。
|
||
|
||
文件过大失败:
|
||
```json
|
||
{ "code": 2402, "message": "文件大小超限", "data": null }
|
||
```
|
||
|
||
**GET /api/FileTask/Getuploadurl** — 获取分片上传地址
|
||
参数:`sessionId`, `partNum` (query)
|
||
- `partNum` 必须满足 `1 ≤ partNum ≤ totalPartCount`,越界返回 `3206`(无效分片号)
|
||
|
||
请求示例:
|
||
```
|
||
GET /api/FileTask/Getuploadurl?sessionId=sess_abc123&partNum=1
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": "https://oss.example.com/upload?sessionId=sess_abc123&partNum=1&signature=xxx"
|
||
}
|
||
```
|
||
|
||
**GET /api/FileTask/progress** — **查询上传进度(新增)**
|
||
参数:`sessionId` (query)
|
||
|
||
请求示例:
|
||
```
|
||
GET /api/FileTask/progress?sessionId=sess_abc123
|
||
```
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "成功",
|
||
"data": {
|
||
"sessionId": "sess_abc123",
|
||
"taskId": "b1c2d3e4-f5a6-7b8c-9d0e-1f2a3b4c5d6e",
|
||
"fileSize": 104857600,
|
||
"totalPartCount": 20,
|
||
"completedPartCount": 7,
|
||
"uploadedBytes": 36700160,
|
||
"progressPercent": 35
|
||
}
|
||
}
|
||
```
|
||
|
||
**POST /api/FileTask/complete** — 完成上传(合并分片)
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| sessionId | string | 会话 ID |
|
||
| parts | UploadPart[] | 分片列表(**必须与 `totalPartCount` 数量一致**) |
|
||
|
||
分片数量不匹配返回 `3204`(分片数量不匹配)。
|
||
|
||
请求示例:
|
||
```json
|
||
{
|
||
"sessionId": "sess_abc123",
|
||
"parts": [
|
||
{ "partNumber": 1, "eTag": "etag-part-1", "size": 5242880 },
|
||
{ "partNumber": 2, "eTag": "etag-part-2", "size": 5242880 }
|
||
]
|
||
}
|
||
```
|
||
|
||
响应示例:`Result<FileResponse>`(结构同 simple-upload)
|
||
|
||
**POST /api/FileTask/local/parts/upload** — 本地分片上传(支持分片大小校验)
|
||
表单字段:`sessionId`, `partNumber`, `file`
|
||
- 非末片大小 ≥ `MinPartSizeBytes`(默认 5MB),否则返回 `3203`(分片过小)
|
||
- 末片豁免最小值校验
|
||
|
||
请求示例(form-data):
|
||
```
|
||
sessionId: sess_abc123
|
||
partNumber: 1
|
||
file: (二进制分片)
|
||
```
|
||
|
||
---
|
||
|
||
## 附录:业务状态码表
|
||
|
||
| 范围 | 分类 | 示例 |
|
||
|------|------|------|
|
||
| 0 | 成功 | 0 成功 |
|
||
| 1000-1999 | 系统级 | 1003 参数错误, 1005 权限不足, 1006 认证失败 |
|
||
| 2000-2099 | 用户 | 2000 用户不存在, 2001 用户已存在, 2002 密码错误 |
|
||
| 2100-2199 | 好友 | 2100 好友申请已存在, 2102 已经是好友 |
|
||
| 2200-2299 | 群聊 | 2200 群不存在, 2201 已在群中, 2202 群成员已满 |
|
||
| 2300-2399 | 消息 | 2300 发送失败, 2301 消息不存在, 2302 撤回失败 |
|
||
| 2400-2499 | 文件 | 2400 上传失败, 2401 文件不存在, 2402 大小超限 |
|
||
| 3000-3099 | 管理后台 | 3000 管理员不存在, 3003 权限不足 |
|
||
| 3100-3199 | 会话 | 3100 会话不存在 |
|
||
| 3200-3299 | 分片 | 3201 分片不存在, 3202 分片合并失败, **3203 分片过小**, **3204 分片数不匹配**, **3205 会话过期**, **3206 分片号无效** |
|