2026-03-23 14:25:47 +08:00
|
|
|
# Inbox Messages 协议
|
|
|
|
|
|
|
|
|
|
本文档定义 `/api/v1/inbox/messages` 的收件箱消息协议。
|
|
|
|
|
|
|
|
|
|
Base URL: `/api/v1/inbox/messages`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 端点
|
|
|
|
|
|
|
|
|
|
| 方法 | 路径 | 说明 |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| GET | `` | 获取消息列表 |
|
|
|
|
|
| PATCH | `/{message_id}/read` | 标记消息为已读 |
|
2026-03-30 18:36:57 +08:00
|
|
|
| GET | `/stream` | 订阅收件箱实时事件(SSE) |
|
2026-03-23 14:25:47 +08:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 枚举类型
|
|
|
|
|
|
|
|
|
|
### InboxMessageType
|
|
|
|
|
|
|
|
|
|
| 值 | 说明 |
|
|
|
|
|
|---|---|
|
|
|
|
|
| `friend_request` | 好友申请 |
|
|
|
|
|
| `calendar` | 日历消息 |
|
|
|
|
|
| `system` | 系统消息 |
|
|
|
|
|
| `group` | 群组消息 |
|
|
|
|
|
|
|
|
|
|
### InboxMessageStatus
|
|
|
|
|
|
|
|
|
|
| 值 | 说明 |
|
|
|
|
|
|---|---|
|
|
|
|
|
| `pending` | 待处理 |
|
|
|
|
|
| `accepted` | 已接受 |
|
|
|
|
|
| `rejected` | 已拒绝 |
|
|
|
|
|
| `dismissed` | 已忽略 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 消息内容类型
|
|
|
|
|
|
2026-03-30 18:36:57 +08:00
|
|
|
### CalendarInviteContent (schema_version=2)
|
2026-03-23 14:25:47 +08:00
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"type": "invite",
|
2026-03-30 18:36:57 +08:00
|
|
|
"schema_version": 2,
|
|
|
|
|
"item": {
|
|
|
|
|
"id": "uuid",
|
|
|
|
|
"title": "string",
|
|
|
|
|
"description": "string | null",
|
|
|
|
|
"start_at": "datetime",
|
|
|
|
|
"end_at": "datetime | null",
|
|
|
|
|
"timezone": "string"
|
|
|
|
|
},
|
|
|
|
|
"actor": {
|
|
|
|
|
"user_id": "uuid",
|
|
|
|
|
"username": "string",
|
|
|
|
|
"phone": "string | null"
|
|
|
|
|
},
|
|
|
|
|
"summary": "string",
|
2026-03-23 14:25:47 +08:00
|
|
|
"permission": "int (1=view, 4=edit, 8=invite)",
|
|
|
|
|
"action": "pending"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2026-03-30 18:36:57 +08:00
|
|
|
说明:`description/start_at/end_at/timezone/actor.phone` 为 `invite` 类型的扩展字段,
|
|
|
|
|
用于前端展示邀请详情(邀请人、联系电话、时间区间、描述)。
|
|
|
|
|
|
|
|
|
|
### CalendarUpdateContent (schema_version=2)
|
2026-03-23 14:25:47 +08:00
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
2026-03-30 18:36:57 +08:00
|
|
|
"type": "updated",
|
|
|
|
|
"schema_version": 2,
|
|
|
|
|
"item": {
|
|
|
|
|
"id": "uuid",
|
|
|
|
|
"title": "string"
|
|
|
|
|
},
|
|
|
|
|
"actor": {
|
|
|
|
|
"user_id": "uuid",
|
|
|
|
|
"username": "string"
|
|
|
|
|
},
|
|
|
|
|
"summary": "string",
|
|
|
|
|
"changes": [
|
|
|
|
|
{
|
|
|
|
|
"field": "title | description | start_at | end_at | timezone | status",
|
|
|
|
|
"label": "string",
|
|
|
|
|
"before": "any | null",
|
|
|
|
|
"after": "any | null",
|
|
|
|
|
"display_before": "string | null",
|
|
|
|
|
"display_after": "string | null",
|
|
|
|
|
"change_type": "added | removed | modified"
|
|
|
|
|
}
|
|
|
|
|
],
|
2026-03-23 14:25:47 +08:00
|
|
|
"action": "updated"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2026-03-30 18:36:57 +08:00
|
|
|
### CalendarDeleteContent (schema_version=2)
|
2026-03-23 14:25:47 +08:00
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
2026-03-30 18:36:57 +08:00
|
|
|
"type": "deleted",
|
|
|
|
|
"schema_version": 2,
|
|
|
|
|
"item": {
|
|
|
|
|
"id": "uuid",
|
|
|
|
|
"title": "string"
|
|
|
|
|
},
|
|
|
|
|
"actor": {
|
|
|
|
|
"user_id": "uuid",
|
|
|
|
|
"username": "string"
|
|
|
|
|
},
|
|
|
|
|
"summary": "string",
|
|
|
|
|
"changes": [],
|
2026-03-23 14:25:47 +08:00
|
|
|
"action": "deleted"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### FriendshipContent
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"type": "request",
|
|
|
|
|
"message": "string | null"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 数据结构
|
|
|
|
|
|
|
|
|
|
### InboxMessageResponse
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"id": "uuid",
|
|
|
|
|
"recipient_id": "uuid",
|
|
|
|
|
"sender_id": "uuid | null",
|
|
|
|
|
"message_type": "InboxMessageType",
|
|
|
|
|
"schedule_item_id": "uuid | null",
|
|
|
|
|
"friendship_id": "uuid | null",
|
2026-03-25 12:36:31 +08:00
|
|
|
"group_id": "uuid | null",
|
2026-03-23 14:25:47 +08:00
|
|
|
"content": "CalendarInviteContent | CalendarUpdateContent | CalendarDeleteContent | FriendshipContent | null",
|
|
|
|
|
"is_read": "boolean",
|
|
|
|
|
"status": "InboxMessageStatus",
|
|
|
|
|
"created_at": "datetime"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 1) GET `/`
|
|
|
|
|
|
|
|
|
|
获取消息列表。
|
|
|
|
|
|
|
|
|
|
### Query Parameters
|
|
|
|
|
|
|
|
|
|
- `is_read`: boolean | null(可选,筛选已读/未读状态)
|
|
|
|
|
|
|
|
|
|
### Response
|
|
|
|
|
|
|
|
|
|
`InboxMessageResponse` 对象数组。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 2) PATCH `/{message_id}/read`
|
|
|
|
|
|
|
|
|
|
标记指定消息为已读。
|
|
|
|
|
|
|
|
|
|
### Path Parameters
|
|
|
|
|
|
|
|
|
|
- `message_id`: 消息 UUID
|
|
|
|
|
|
|
|
|
|
### Response
|
|
|
|
|
|
|
|
|
|
`InboxMessageResponse` 对象。
|
2026-03-30 18:36:57 +08:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 3) GET `/stream` (SSE)
|
|
|
|
|
|
|
|
|
|
订阅当前登录用户的 inbox 实时增量事件。
|
|
|
|
|
|
|
|
|
|
### Headers
|
|
|
|
|
|
|
|
|
|
- `Accept: text/event-stream`
|
|
|
|
|
- `Last-Event-ID`(可选):断点续流游标,格式 `\d+-\d+`
|
|
|
|
|
|
|
|
|
|
### Query Parameters
|
|
|
|
|
|
|
|
|
|
- `idle_limit`: `1..3600`(可选,默认 `300`),连续空轮询上限,超过后服务端主动结束连接。
|
|
|
|
|
|
|
|
|
|
### SSE 事件帧
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
id: 1743313300000-0
|
|
|
|
|
event: INBOX_MESSAGE_CREATED
|
|
|
|
|
data: {"event_id":"6f0d...","occurred_at":"2026-03-30T07:00:00Z","user_id":"...","message_id":"...","op":"created","version":1743313300000,"data":{"message":{...}}}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 事件类型
|
|
|
|
|
|
|
|
|
|
- `INBOX_MESSAGE_CREATED`
|
|
|
|
|
- `INBOX_MESSAGE_READ_CHANGED`
|
|
|
|
|
- `INBOX_MESSAGE_STATUS_CHANGED`
|
|
|
|
|
- `INBOX_SNAPSHOT_REQUIRED`
|
|
|
|
|
|
|
|
|
|
### Event Envelope
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"event_id": "uuid",
|
|
|
|
|
"occurred_at": "datetime",
|
|
|
|
|
"user_id": "uuid",
|
|
|
|
|
"message_id": "uuid",
|
|
|
|
|
"op": "created | read_changed | status_changed | snapshot_required",
|
|
|
|
|
"version": 1743313300000,
|
|
|
|
|
"data": {}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Delta 约定
|
|
|
|
|
|
|
|
|
|
- `created`:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"message": {
|
|
|
|
|
"id": "uuid",
|
|
|
|
|
"recipient_id": "uuid",
|
|
|
|
|
"sender_id": "uuid | null",
|
|
|
|
|
"message_type": "InboxMessageType",
|
|
|
|
|
"schedule_item_id": "uuid | null",
|
|
|
|
|
"friendship_id": "uuid | null",
|
|
|
|
|
"content": {},
|
|
|
|
|
"is_read": false,
|
|
|
|
|
"status": "pending",
|
|
|
|
|
"created_at": "datetime"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- `read_changed`:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"is_read": true
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- `status_changed`:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"status": "accepted"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- `snapshot_required`:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 幂等与补偿策略
|
|
|
|
|
|
|
|
|
|
- 客户端按 `message_id + version` 做幂等合并;旧版本事件必须丢弃。
|
|
|
|
|
- 若检测到版本跳跃或本地状态不可信,客户端应回退到 `GET /api/v1/inbox/messages` 全量快照。
|
|
|
|
|
- `id` 字段可用于 `Last-Event-ID` 断点续流。
|
|
|
|
|
|
|
|
|
|
### 前端渲染约束(强制)
|
|
|
|
|
|
|
|
|
|
- 对 `message_type=calendar`,前端必须按 `content.type` 严格分发:`invite | updated | deleted`。
|
|
|
|
|
- 若 `content` 缺少协议必填字段(`schema_version/item/actor/summary`,以及 `updated` 的 `changes`),前端必须进入协议异常展示路径。
|
|
|
|
|
- 禁止将协议异常消息兜底渲染为“默认日历邀请”或其他正常业务消息。
|