16 KiB
IM_NEW 接口文档
源码基线:
IM_NEW/codex/api-alignment-fixes(起点32177a7293ec9eb7bd731467953da3c51e561beb) 文档以控制器、DTO、枚举、序列化配置和 Nginx 网关配置为准;“当前实现注意事项”用于标明源码中的实际限制,不代表理想设计。
1. 通用约定
1.1 网关与路径大小写
- 网关默认端口:
8009。 - HTTP API 前缀:
/api。 - SignalR Hub:
/chat。 - Nginx
location匹配区分大小写,因此应使用本文给出的全小写控制器前缀,例如/api/user/me。/api/User/Me在当前网关会返回 404。
1.2 认证
除登录、注册以及下文特别标注的接口外,请携带:
Authorization: Bearer <JWT>
SignalR 客户端通过 /chat?access_token=<JWT> 完成握手;使用官方 SignalR 客户端时由 accessTokenFactory 自动添加。
Token 缺失、无效或过期时返回 HTTP 401,业务响应为:
{ "code": 1006, "message": "认证失败", "data": null }
已认证但权限不足时返回 HTTP 403,业务码为 1005。新版前端在滚动发布期间仍兼容旧服务的 HTTP 200 + code=1006。
1.3 统一响应
除文件内容下载外,接口通常返回:
{
"code": 0,
"message": "成功",
"data": {}
}
- JSON 属性使用 camelCase。
code === 0表示业务成功。- 参数校验失败也返回 HTTP 200,业务码为
1003。 - 枚举通过
JsonStringEnumConverter序列化为字符串,并接受字符串枚举请求值。 - 未处理异常统一返回 HTTP 500 +
code=1000,响应头X-Correlation-ID可用于关联服务端日志。
2. 认证与用户
2.1 POST /api/auth/login
无需认证。请求:
{ "userName": "user1", "password": "******" }
校验:userName 5–20 字符;password 非空且不超过 50 字符。
成功返回 Result<LoginResponse>:
{
"code": 0,
"message": "成功",
"data": {
"userId": "guid",
"token": "jwt",
"refreshToken": "string",
"expired": null,
"userName": "string",
"nickName": "string",
"avatar": "string|null",
"creationTime": "datetime-offset"
}
}
当前实现创建 LoginResponse 时将 expired 传为 null。
2.2 POST /api/auth/register
无需认证。请求:
{ "userName": "newuser", "password": "******", "nickName": "新用户" }
校验:userName 5–20 字符;password 6–50 字符;nickName 非空且不超过 50 字符。返回 Result<UserResponse>。
2.3 POST /api/auth/refresh
无需 Access Token。请求:
{ "refreshToken": "string" }
返回新的 Result<LoginResponse>。
2.4 用户接口
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | /api/user/me |
无 | Result<UserResponse> |
| GET | /api/user/find?userId={guid} |
query: userId |
Result<UserResponse> |
| GET | /api/user/findByUname?username={value} |
query: username |
Result<UserResponse> |
| POST | /api/user/update |
body: UserUpdateRequest |
Result<UserResponse> |
| POST | /api/user/getUsersByIds |
body: GUID 数组 | Result<UserResponse[]> |
UserUpdateRequest 的字段均可省略:
{
"nickName": "string|null",
"region": "string|null",
"avatar": "string|null",
"description": "string|null"
}
UserResponse:
{
"id": "guid",
"userName": "string",
"nickName": "string",
"email": "string|null",
"phone": "string|null",
"region": "string",
"description": "string",
"avatar": "string|null",
"creationTime": "datetime-offset",
"deletion": "datetime-offset|null"
}
3. 好友与好友申请
所有接口均要求认证。
3.1 好友
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | /api/friend/list |
无 | Result<FriendResponse[]> |
| POST | /api/friend/delete?friendId={guid} |
好友关系记录 ID,不是对方用户 ID | Result<object>,成功时 data: null |
| POST | /api/friend/block?friendId={guid} |
好友关系记录 ID | Result<object> |
| GET | /api/friend/checkFriend?userId={guid}&targetId={guid} |
两个用户 ID | Result<boolean> |
FriendResponse:
{
"id": "friend-relation-guid",
"targetId": "peer-user-guid",
"avatar": "string|null",
"nickName": "string",
"remarkName": "string|null",
"createTime": "datetime",
"updateTime": "datetime|null",
"status": "Pending|Added|Declined|Blocked"
}
3.2 好友申请
POST /api/friendRequest/add
{
"targetId": "guid",
"description": "string|null",
"remarkName": "string|null"
}
targetId 必填。返回 Result<FriendRequestResponse>。
POST /api/friendRequest/handle
{
"requestId": "guid",
"action": "Accpet|Reject|Block",
"remarkName": "string|null"
}
注意:后端枚举当前拼写为 Accpet,接受申请时 remarkName 必填。
GET /api/friendRequest/list
返回与当前用户相关的申请:Result<FriendRequestResponse[]>。
FriendRequestResponse.state:Pending、Declined、Passed、Blocked。
4. 群组
4.1 群信息
| 方法 | 路径 | 请求 | 返回 |
|---|---|---|---|
| GET | /api/group/getAll |
无 | Result<GroupResponse[]> |
| GET | /api/group/getOne?groupId={guid} |
query: groupId |
Result<GroupResponse> |
| POST | /api/group/create |
`{ "name": "string | null" }` |
| POST | /api/group/update |
GroupUpdateRequest |
Result<GroupResponse> |
| POST | /api/group/dissolve?groupId={guid} |
群 ID;仅群主 | Result<object> |
name 最大 20 字符。更新请求:
{
"groupId": "guid",
"groupName": "string|null",
"avatar": "string|null",
"description": "string|null"
}
description 实际用于更新群公告 announcement。
GroupResponse:
{
"id": "guid",
"name": "string",
"groupMaster": "guid",
"authority": "REQUIRE_CONSENT|ANYONE_CAN_JOIN|NOT_ALLOWED_TO_JOIN",
"allMembersBanned": false,
"status": "Normal|Blocked",
"announcement": "string",
"avatar": "string|null",
"maxSequenceId": 0,
"lastMessage": "string",
"lastSenderName": "string",
"created": "datetime-offset",
"updated": "datetime-offset"
}
getAll 按当前用户的有效群成员关系返回群,普通成员、管理员和群主均可看到已加入群。
4.2 群成员
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | /api/groupMember/checkMember?userId={guid}&groupId={guid} |
用户 ID、群 ID | Result<boolean> |
| GET | /api/groupMember/list?groupId={guid} |
群 ID | Result<GroupMemberResponse[]> |
| POST | /api/groupMember/delete?memberId={guid} |
群成员记录 ID | Result<object> |
| POST | /api/groupMember/leave?groupId={guid} |
群 ID | Result<object> |
GroupMemberResponse.role:Normal、Administrator、Master。
list、delete、leave 均要求登录。delete 是管理操作,只能移除角色低于操作者的其他成员,禁止移除群主或自己;群主必须使用 dissolve,不能使用 leave。
checkMember 是服务间内部接口,必须携带 X-Internal-Api-Key,不应通过公网网关暴露。
4.3 群邀请
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| POST | /api/groupInvitation/send |
body: { "groupId": "guid", "userId": "guid" } |
Result<GroupInvitationResponse> |
| GET | /api/groupInvitation/get?invitationId={guid} |
邀请 ID | Result<GroupInvitationResponse> |
| POST | /api/groupInvitation/handle?invitationId={guid}&action={value} |
`action=Accept | Reject` |
邀请状态:Pending、Passed、Reject。
4.4 入群申请
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| POST | /api/groupRequest/send |
body: `{ "groupId": "guid", "desc": "string | null" }` |
| POST | /api/groupRequest/handle?requestId={guid}&action={value} |
`action=Accept | Reject` |
| GET | /api/groupRequest/find?id={guid} |
申请 ID | Result<GroupRequestResponse> |
| GET | /api/groupRequest/list |
无 | Result<GroupRequestResponse[]> |
desc 最大 20 字符;状态为 Pending、Declined 或 Passed。
list 返回当前用户提交的申请,以及当前用户作为管理员或群主有权处理的群申请。
5. 会话与消息
所有接口均要求认证。
5.1 会话
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| GET | /api/conversation/list |
无 | Result<ConversationResponse[]> |
| GET | /api/conversation/get?id={guid} |
会话 ID | Result<ConversationResponse> |
| POST | /api/conversation/markRead?conversationId={guid} |
会话 ID | Result<object> |
ConversationResponse:
{
"id": "guid",
"userId": "guid",
"targetId": "guid",
"targetAvatar": "string",
"targetName": "string",
"lastReadSequenceId": 0,
"unreadCount": 0,
"chatType": "PRIVATE|GROUP",
"lastMessage": "string",
"dateTime": "datetime"
}
5.2 POST /api/message/send
{
"clientMsgId": "guid",
"targetId": "guid",
"chatType": "PRIVATE|GROUP",
"msgType": "Text|Image|Voice|Video|File|VoiceChat|VideoChat",
"quoteMessageId": "guid|null",
"ext": { "key": "value" },
"text": "string|null",
"url": "string|null",
"width": 0,
"height": 0,
"thumb": "string|null",
"duration": 0,
"fileId": "guid|null",
"fileName": "string|null",
"fileSize": 0,
"fileFormat": "mime/type|null"
}
clientMsgId、targetId必填。Text要求text。Image、Video、Voice至少提供url或fileId。File要求fileId、fileName、fileSize、fileFormat;服务端已支持构建和保存文件消息。VoiceChat、VideoChat仍未实现,发送会返回2303。
成功返回 Result<MessageResponse>:
{
"id": "guid",
"clientMsgId": "guid",
"chatType": "PRIVATE",
"msgType": "Text",
"senderId": "guid",
"targetId": "guid",
"state": "Sent|Withdrwan",
"streamKey": "string",
"sequenceId": 1,
"creationTime": "datetime-offset",
"content": {
"fallback": "string",
"body": {},
"ext": {},
"quote": null
}
}
5.3 其他消息接口
| 方法 | 路径 | 参数 | 返回 |
|---|---|---|---|
| POST | /api/message/withDraw?msgId={guid} |
消息 ID | Result<object> |
| GET | /api/message/getMessages?conversationId={guid}&cursor={long?}&direction={int}&limit={int} |
会话、游标、方向、条数 | Result<GetMessagesResponse> |
GetMessagesResponse:
{ "messages": [], "hasmore": false }
方向约定:0 查询 SequenceId < cursor 的历史消息;1 查询 SequenceId > cursor 的增量消息。两个方向都查询 limit + 1 条判断 hasmore,但响应最多返回 limit 条并按序列号升序;limit 范围为 1–100。
6. 文件服务
所有接口均要求认证。
6.1 文件
POST /api/file/simple-upload
multipart/form-data:
| 字段 | 类型 | 必填 |
|---|---|---|
file |
文件 | 是 |
isPublic |
boolean | 是 |
服务端计算 MD5 并执行安全秒传:公开文件可复用,私有文件只允许同一所有者复用。返回 Result<FileResponse>。
GET /api/file/{id}
返回文件信息 Result<FileResponse>。
GET /api/file/{id}/content
返回鉴权后的二进制文件流,支持 HTTP Range,不使用 Result<T> 包装。无权限返回 HTTP 403,文件不存在返回 HTTP 404。
FileResponse 为扁平结构,不暴露存储桶、对象键等内部位置:
{
"id": "guid",
"ownerId": "guid",
"fileName": "avatar.png",
"fileSize": 123,
"contentType": "image/png",
"state": "Uploaded",
"checkSum": "md5-hex",
"chatType": "PRIVATE|GROUP|null",
"targetId": "guid|null",
"isPublic": false,
"created": "datetime-offset",
"updated": "datetime-offset",
"url": "string|null"
}
url 仅在存储提供方能够生成公开地址时存在。
6.2 分片上传
POST /api/fileTask/init
{
"conversationId": "guid",
"chatType": "PRIVATE|GROUP",
"targetId": "peer-or-group-guid",
"fileName": "string",
"fileSize": 123,
"contentType": "mime/type",
"checkSum": "md5"
}
返回:
{
"taskId": "guid",
"uploadSessionId": "string",
"instant": false,
"uploadMode": "LocalMultipart|Presigned",
"totalPartCount": 1,
"partSizeBytes": 5242880,
"file": null
}
秒传命中时返回 instant: true、uploadMode: "Instant" 和最终 file;客户端直接使用该文件,不再调用 complete。
GET /api/fileTask/getuploadurl?sessionId={value}&partNum={n}
返回 Result<PresignedUrl>,不是字符串:
{
"url": "string",
"method": "PUT|POST",
"headers": {},
"expiresAt": "datetime-offset"
}
GET /api/fileTask/progress?sessionId={value}
{
"sessionId": "string",
"taskId": "string",
"fileSize": 123,
"totalPartCount": 1,
"completedPartCount": 0,
"uploadedBytes": 0,
"progressPercent": 0
}
POST /api/fileTask/local/parts/upload
multipart/form-data:sessionId、partNumber、file。返回 Result<CompleteUploadResult>,其中包含 location、eTag、size、checksum 和 versionId。
POST /api/fileTask/complete
{
"sessionId": "string",
"parts": [
{ "partNumber": 1, "eTag": "string", "size": 123, "checksum": null }
]
}
成功接受异步合并时返回 HTTP 202 + Result<UploadTaskResponse>,其中 state 为 Merging。
GET /api/fileTask/status?taskId={guid}
客户端轮询此接口。处理中返回 Uploading|Merging;失败返回 state: "Failed" 与 failureReason;完成后返回:
{
"id": "upload-task-guid",
"uploaderId": "guid",
"conversationId": "guid",
"fileName": "string",
"fileSize": 123,
"contentType": "mime/type",
"storageLocation": {},
"state": "Completed",
"checkSum": "string",
"resultFileId": "guid",
"failureReason": null,
"file": { "id": "guid", "fileName": "string", "fileSize": 123, "url": null }
}
任务、分片、进度、完成和状态接口都会校验当前用户是上传者。私有文件的 url 为 null,使用 /api/file/{id}/content 鉴权读取。
7. SignalR
- Hub:
/chat - 服务端事件:
ReceiveNewMessage - 当前 Hub 没有
clearUnreadCount方法;清零未读应调用 HTTP/api/conversation/markRead。
推送载荷与 HTTP MessageResponse 略有不同:
{
"id": "guid",
"clientId": "guid",
"chatType": "PRIVATE|GROUP",
"msgType": "Text|Image|Voice|Video",
"senderId": "guid",
"targetId": "guid",
"state": "Sent|Withdrwan",
"streamKey": "string",
"sequenceId": 1,
"pushTimestamp": 0,
"content": {
"fallback": "string",
"body": {},
"ext": {},
"quote": null
}
}
8. 业务状态码
| 范围/代码 | 含义 |
|---|---|
0 |
成功 |
1000–1006 |
系统、超时、参数、数据库、权限、认证错误 |
2000–2004 |
用户不存在、已存在、密码错误、禁用、登录过期 |
2100–2107 |
好友申请、好友关系和操作错误 |
2200–2206 |
群不存在、已入群、群满、权限、邀请、申请、成员错误 |
2300–2303 |
消息发送、消息不存在、撤回、不支持的消息类型 |
2400–2403 |
文件上传、不存在、过大、类型不支持 |
3000–3004 |
管理后台错误 |
3100 |
会话不存在 |
3201–3206 |
分片不存在、合并失败、分片过小/数量不符、会话过期、分片号无效 |
完整名称与中文说明以 IM.Commons/ResultCode.cs 为准。