Files
IM/frontend/pc/IM/API.md
T

16 KiB
Raw Blame History

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 520 字符;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 520 字符;password 650 字符;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.statePendingDeclinedPassedBlocked

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.roleNormalAdministratorMaster

listdeleteleave 均要求登录。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`

邀请状态:PendingPassedReject

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 字符;状态为 PendingDeclinedPassed

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"
}
  • clientMsgIdtargetId 必填。
  • Text 要求 text
  • ImageVideoVoice 至少提供 urlfileId
  • File 要求 fileIdfileNamefileSizefileFormat;服务端已支持构建和保存文件消息。
  • VoiceChatVideoChat 仍未实现,发送会返回 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 范围为 1100。

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: trueuploadMode: "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-datasessionIdpartNumberfile。返回 Result<CompleteUploadResult>,其中包含 locationeTagsizechecksumversionId

POST /api/fileTask/complete

{
  "sessionId": "string",
  "parts": [
    { "partNumber": 1, "eTag": "string", "size": 123, "checksum": null }
  ]
}

成功接受异步合并时返回 HTTP 202 + Result<UploadTaskResponse>,其中 stateMerging

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 成功
10001006 系统、超时、参数、数据库、权限、认证错误
20002004 用户不存在、已存在、密码错误、禁用、登录过期
21002107 好友申请、好友关系和操作错误
22002206 群不存在、已入群、群满、权限、邀请、申请、成员错误
23002303 消息发送、消息不存在、撤回、不支持的消息类型
24002403 文件上传、不存在、过大、类型不支持
30003004 管理后台错误
3100 会话不存在
32013206 分片不存在、合并失败、分片过小/数量不符、会话过期、分片号无效

完整名称与中文说明以 IM.Commons/ResultCode.cs 为准。