19 KiB
IM 会议笔记前后端逐图对接协议
版本:v1.0
日期:2026-09-05
需求依据:IM 会议笔记产品原型需求文档
公共协议依据:IM 前后端逐图对接协议
说明:本文档的图片标题和顺序与会议笔记产品原型完全一致。后续合并时建议作为第 9 章的9.14 会议笔记,原主文档9.14 接口示例顺延为9.15 接口示例。
9.14 会议笔记
会议笔记属于企业工作台模块,复用会议主数据和统一文件能力。服务端负责判断会议是否结束、当前用户是否与会议有关、是否为会议记录员,以及文件查看和操作权限;客户端只根据响应中的状态与 permissions 展示页面,不自行推导权限。
本模块遵循主文档第 0 章的公共约定:产品 HTTP 使用 X-MeetingIM-APIKey、X-IM-Schema: 1、X-Request-ID 和 Authorization: Bearer <token>;写操作额外使用 Idempotency-Key;成功和失败响应均使用统一包裹结构。
会议笔记不产生移动端通知、不写入会议通知未读数,也不增加工作台底部 Tab 红点。客户端进入会议笔记列表或详情页时主动请求最新数据;从上传、预览或下载页面返回后,只刷新当前会议笔记页面。
9.14.1 会议笔记-工作台入口.png
状态:工作台接口需改造。
GET /v1/im/workbench的modules增加code="meeting_notes"、route="/meeting-notes"。服务端按租户功能开关和当前用户权限决定是否返回该入口。- 会议笔记模块固定返回
unread_count=0,不参与GET /v1/im/bootstrap的entry_badges.workbench_total_unread汇总。 - 当前工作台红点只对应“会议通知”模块的未读通知。日报、月报、打卡、部门框架、举报信箱和会议笔记均不产生工作台通知数。
- 客户端点击入口后请求
GET /v1/im/meeting-notes。切换企业后必须清空原租户的会议笔记缓存并重新加载。
9.14.2 会议笔记-列表.png
状态:新增。
- 列表:
GET /v1/im/meeting-notes?query=&cursor=&limit=20。接口只返回当前用户作为发起人、会议记录员或参会人的已结束会议,不返回未开始、进行中或已取消会议。 query支持匹配会议主题、发起人、会议记录员、参会人和地点;搜索范围仍受当前用户会议可见权限限制。- 列表按
end_at倒序排列,分页使用不透明cursor。客户端不得用本地时间过滤会议状态或自行扩大搜索范围。 - 已结束但尚未上传笔记的会议仍返回,使用
note_status="empty"、file_count=0;客户端展示会议基础信息,进入详情后显示“暂无笔记”。 permissions.can_view=true决定是否允许进入详情;permissions.can_upload只对当前会议记录员为true。普通参会人可以进入详情,但不显示上传按钮。state="ended"是服务端按会议状态及服务端当前时间计算或持久化的有效状态。服务端必须以end_at和取消状态为准,不能信任客户端传入的“已结束”条件。
9.14.3 会议笔记-发布页面.png
状态:现有会议创建接口需改造。
- 页面字段顺序为发起人、地点、会议记录员、参会人、开始时间、时长、会议主题。会议记录员默认显示当前会议发起人。
- 创建仍使用
POST /v1/im/meetings,请求新增recorder_user_id。客户端未传或传空字符串时,服务端默认使用当前登录用户;客户端不能通过organizer_id伪造发起人。 - 服务端校验会议记录员属于当前租户且账号有效。首版允许会议记录员是发起人或参会人;若选择了非参会人,服务端自动加入参会人列表并在响应中返回最终名单。
- 服务端保存的会议必须有
recorder_user_id;创建请求可省略该字段以使用发起人默认值。attendee_user_ids、start_at、end_at和title为必填业务数据,location可为空,且end_at必须晚于start_at。 - 创建成功后的会议通知仍统一归属“会议通知”模块;会议笔记模块不额外生成通知或未读数。
PATCH /v1/im/meetings/{meeting_id}支持在会议结束前修改recorder_user_id,并使用version做乐观锁。会议结束后返回MEETING_CLOSED。
9.14.4 会议笔记-详情页.png
状态:新增。
- 详情:
GET /v1/im/meetings/{meeting_id}/notes,一次返回会议基础信息、发起人、会议记录员、参会人、文件列表、笔记状态和当前用户权限。 - 会议基础信息与会议通知页面共用
im_meetings和im_meeting_participants数据,不复制会议标题、时间和成员快照作为另一份可编辑主数据。 - 当前用户为会议记录员且会议已结束时,
permissions.can_upload=true,客户端展示“上传文件”;其他用户不展示该按钮。 - 上传文件先调用
POST /v0/file/u/,Multipart 携带purpose=meeting_note和meeting_id;上传成功取得file_id后,再调用POST /v1/im/meetings/{meeting_id}/notes/files建立会议笔记文件记录。 - 文件登记成功后才进入详情列表。上传成功但登记失败的临时文件不得展示,并由服务端按临时文件 TTL 回收。
- 当前会议记录员可替换或删除自己登记的文件;所有写操作均需
Idempotency-Key,服务端记录操作审计。
9.14.5 会议笔记-详情页(普通成员).png
状态:新增权限响应。
- 普通参会人调用与 9.14.4 相同的详情接口。响应中
permissions.can_view=true、can_upload=false、can_replace=false、can_delete=false。 - 没有已上传文件时返回
note_status="empty"和空数组files=[],客户端展示“暂无笔记”,不把空状态当作请求失败。 - 有文件时普通参会人只展示文件名、大小、类型、上传人、上传时间和预览入口,不展示上传、替换、删除按钮。
- 服务端必须在文件登记、替换、删除、预览和下载接口逐次校验会议关系及角色。隐藏客户端按钮不能代替服务端鉴权。
- 与会议无关、已离开可见范围或跨租户的用户统一返回
RESOURCE_NOT_FOUND或FORBIDDEN,不得通过meeting_id、note_file_id或file_id探测资源。
9.14.6 会议笔记-详情页-在线预览.png
状态:基础文件读取已实现;会议笔记 ACL 和预览转换新增。
- 文件项返回
preview.status=ready|processing|unsupported|failed、preview.mode=native|converted|download_only和可选preview_file_id,客户端根据状态选择直接预览、轮询转换状态或提示暂不支持。 - 原生可预览文件使用受认证的
GET /v0/file/s/{file_id};转换预览使用GET /v0/file/s/{preview_file_id}。两个接口都必须校验当前用户仍有会议笔记查看权限。 - 服务端不得返回长期有效的公共下载 URL。客户端缓存键至少包含
tenant_code + user_id + file_id + etag,退出登录或切换企业时清理受控缓存。 preview.status="processing"时客户端可在当前详情页低频刷新,建议 2 秒后开始、最长 30 秒;离开页面立即停止轮询。
9.14.7 会议笔记-详情页-文件下载中.png
状态:复用统一下载任务;会议笔记 ACL 新增。
- 下载使用
GET /v0/file/s/{file_id},Header 传 API Key 与X-MeetingIM-Auth: token <base64_token>,并支持Range和If-Range断点续传。 - 下载任务保存
file_id,etag,total_bytes,downloaded_bytes,temp_path,state。Token 更新后使用新 Token 续传,禁止把 Token 写入任务文件或日志。 - 服务端在每次下载及续传时重新校验会议可见权限。返回
FILE_FORBIDDEN时客户端停止重试;返回FILE_UNAVAILABLE时标记文件失效并刷新详情。 - 下载进度属于客户端本地状态,不更新会议笔记业务状态、不产生工作台通知,也不写入未读数。
9.14.8 会议笔记-文件预览.png
状态:客户端预览页;服务端复用 9.14.6 文件读取协议。
- 预览页展示服务端返回的安全文件名和实际内容;标题不得直接信任未经清理的上传文件名。
- 图片、PDF、文本等受支持类型在应用内预览;Office 等类型优先使用受控转换文件,无法转换时按
preview.mode="download_only"提示下载后打开。 - 文件被删除、审核拒绝、转换失败或权限撤销时,服务端返回稳定
reason,客户端关闭预览并刷新详情,不能继续显示旧的授权链接。 - 从预览页返回详情后调用
GET /v1/im/meetings/{meeting_id}/notes获取最新文件状态。
9.14.9 接口示例
以下示例用于会议笔记 HTTP 联调快速查阅。成功响应统一包裹为 {"code":0,"data":示例,"request_id":"...","server_time":"..."},下表只展示 data。
9.14.9.1 工作台、会议记录员与会议笔记
| 接口 | 输入示例 | 成功 data 示例 |
|---|---|---|
GET /v1/im/workbench 需改造 |
Query only | {"layout_version":2,"modules":[{"code":"meeting_notifications","name":"会议通知","route":"/meetings","unread_count":3,"enabled":true,"permission":"enabled"},{"code":"meeting_notes","name":"会议笔记","route":"/meeting-notes","unread_count":0,"enabled":true,"permission":"enabled"}]} |
POST /v1/im/meetings 需改造 |
Header Idempotency-Key;Body {"title":"项目周会","start_at":"2026-09-05T10:00:00+08:00","end_at":"2026-09-05T10:30:00+08:00","location":"会议室 A","recorder_user_id":"usr_me","attendee_user_ids":["usr_2"],"reminder_minutes":[10]} |
{"meeting_id":"mtg_1","title":"项目周会","organizer":{"user_id":"usr_me","display_name":"张三"},"recorder":{"user_id":"usr_me","display_name":"张三"},"start_at":"2026-09-05T02:00:00Z","end_at":"2026-09-05T02:30:00Z","state":"scheduled","participants":[{"user_id":"usr_me","response":"accepted"},{"user_id":"usr_2","response":"pending"}],"permissions":{"can_update":true,"can_cancel":true},"version":1} |
PATCH /v1/im/meetings/{meeting_id} 需改造 |
Header Idempotency-Key;Body {"recorder_user_id":"usr_2","version":1} |
{"meeting_id":"mtg_1","recorder":{"user_id":"usr_2","display_name":"李四"},"version":2,"updated_at":"2026-09-05T01:30:00Z"} |
GET /v1/im/meeting-notes?query=&cursor=&limit=20 新增 |
Query only | {"items":[{"meeting_id":"mtg_1","title":"项目周会","organizer":{"user_id":"usr_me","display_name":"张三"},"recorder":{"user_id":"usr_me","display_name":"张三"},"start_at":"2026-09-05T02:00:00Z","end_at":"2026-09-05T02:30:00Z","location":"会议室 A","state":"ended","participant_count":2,"participant_names":["张三","李四"],"note_status":"empty","file_count":0,"permissions":{"can_view":true,"can_upload":true}}],"next_cursor":"","has_more":false} |
GET /v1/im/meetings/{meeting_id}/notes 新增 |
Path meeting ID | {"meeting":{"meeting_id":"mtg_1","title":"项目周会","organizer":{"user_id":"usr_me","display_name":"张三"},"recorder":{"user_id":"usr_me","display_name":"张三"},"start_at":"2026-09-05T02:00:00Z","end_at":"2026-09-05T02:30:00Z","location":"会议室 A","state":"ended","participants":[{"user_id":"usr_me","display_name":"张三"},{"user_id":"usr_2","display_name":"李四"}]},"note_status":"available","files":[{"note_file_id":"mnote_1","file_id":"file_1","name":"会议纪要.pdf","size":204800,"mime":"application/pdf","uploader":{"user_id":"usr_me","display_name":"张三"},"created_at":"2026-09-05T03:00:00Z","version":1,"preview":{"status":"ready","mode":"native","preview_file_id":"file_1"},"permissions":{"can_preview":true,"can_download":true,"can_replace":true,"can_delete":true}}],"permissions":{"can_view":true,"can_upload":true},"version":1} |
POST /v0/file/u/ |
Multipart 文件;字段 purpose=meeting_note、meeting_id=mtg_1;Header X-MeetingIM-Auth |
{"ctrl":{"code":200,"params":{"file_id":"file_1","url":"/v0/file/s/file_1","name":"会议纪要.pdf","size":204800,"mime":"application/pdf"}}} |
POST /v1/im/meetings/{meeting_id}/notes/files 新增 |
Header Idempotency-Key;Body {"file_id":"file_1"} |
{"note_file_id":"mnote_1","meeting_id":"mtg_1","file_id":"file_1","name":"会议纪要.pdf","size":204800,"mime":"application/pdf","created_at":"2026-09-05T03:00:00Z","version":1,"preview":{"status":"processing","mode":"converted"}} |
PUT /v1/im/meetings/{meeting_id}/notes/files/{note_file_id} 新增 |
Header Idempotency-Key;Body {"file_id":"file_2","version":1} |
{"note_file_id":"mnote_1","meeting_id":"mtg_1","file_id":"file_2","name":"会议纪要-v2.pdf","version":2,"updated_at":"2026-09-05T03:10:00Z"} |
DELETE /v1/im/meetings/{meeting_id}/notes/files/{note_file_id} 新增 |
Header Idempotency-Key;Query version=2 |
{"note_file_id":"mnote_1","deleted":true,"deleted_at":"2026-09-05T03:20:00Z"} |
GET /v0/file/s/{file_id} |
Header X-MeetingIM-Auth;可带 Range: bytes=0- |
返回文件字节流及 Content-Type、Content-Length、ETag、Accept-Ranges;失败返回 FILE_FORBIDDEN 或 FILE_UNAVAILABLE |
9.14.10 字段与状态约定
| 字段 | 取值 | 前端处理 |
|---|---|---|
meeting.state |
scheduled、in_progress、ended、canceled |
会议笔记列表只展示 ended |
note_status |
empty、available |
empty 展示“暂无笔记”,available 展示文件列表 |
preview.status |
ready、processing、unsupported、failed |
分别进入预览、短时刷新、仅下载提示、失败提示 |
preview.mode |
native、converted、download_only |
使用原文件、转换文件或仅提供下载 |
permissions.can_view |
Boolean | 是否可以查看当前会议笔记 |
permissions.can_upload |
Boolean | 是否显示并允许使用上传按钮,仅会议记录员为 true |
permissions.can_replace |
Boolean | 是否允许替换当前笔记文件 |
permissions.can_delete |
Boolean | 是否允许删除当前笔记文件 |
permissions.can_preview |
Boolean | 是否允许预览当前文件 |
permissions.can_download |
Boolean | 是否允许下载当前文件;首版普通参会人若产品要求“只能预览”,该值返回 false |
客户端必须以服务端返回的 permissions 为准。特别是普通参会人“只能预览”的要求,由文件项 can_preview=true,can_download=false 表达;即使预览底层使用文件读取接口,客户端也不得提供导出、系统分享或保存到相册入口。
9.14.11 错误码与客户端处理
reason |
典型 HTTP 状态 | 场景 | 客户端处理 |
|---|---|---|---|
MEETING_NOT_ENDED |
409 | 未结束会议上传或登记笔记 | 提示“会议结束后可上传笔记”,刷新会议详情 |
MEETING_CLOSED |
409 | 会议结束后修改记录员或会议数据 | 切换为只读并刷新详情 |
NOT_MEETING_RECORDER |
403 | 非记录员上传、替换或删除文件 | 隐藏操作入口并刷新权限 |
MEETING_NOTE_VERSION_CONFLICT |
409 | 替换或删除时版本过期 | 拉取最新详情,不自动覆盖 |
MEETING_NOTE_FILE_INVALID |
400 | 文件未上传完成、用途不符或不属于当前租户 | 提示重新上传 |
FILE_PREVIEW_PROCESSING |
409 | 转换预览尚未完成 | 保持当前页并按退避策略刷新 |
FILE_PREVIEW_UNSUPPORTED |
422 | 文件类型不支持在线预览 | 根据 can_download 决定是否提供下载 |
FILE_FORBIDDEN |
403 | 当前用户无文件访问权限 | 关闭预览或停止下载并刷新详情 |
FILE_UNAVAILABLE |
404/410 | 文件被删除、失效或审核拒绝 | 标记不可用并刷新文件列表 |
RESOURCE_NOT_FOUND |
404 | 会议或笔记不可见、跨租户访问 | 返回列表,不提示资源是否真实存在 |
9.14.12 前后端联调与验收要点
- 发起会议时不传
recorder_user_id,响应中的记录员必须默认为当前发起人;指定其他记录员时,响应返回最终记录员和参会人名单。 - 会议笔记列表只包含与当前用户有关的已结束会议;已结束但无文件的会议仍展示,详情为空状态。
- 记录员进入详情显示上传按钮,普通参会人进入同一会议不显示上传按钮;直接调用写接口仍必须被服务端拒绝。
- 普通参会人只允许在线预览时,响应为
can_preview=true,can_download=false,客户端不展示下载、分享或导出入口。 - 文件上传成功但会议笔记登记失败时,详情列表不出现该文件;重复登记同一幂等键返回首次结果。
- 文件替换和删除必须校验
version;旧版本请求返回冲突,不能覆盖新文件。 - 进入列表、进入详情以及从上传/预览/下载页面返回时均主动刷新;断开 WebSocket 不影响会议笔记数据获取。
- 会议笔记新增、替换、删除均不产生 Push、会议通知未读或工作台红点;工作台红点只随会议通知模块变化。
- 跨租户、无关用户和已撤销权限用户无法通过会议 ID 或文件 ID 查看、预览、下载会议笔记。
- 切换企业或退出登录后,会议笔记列表、详情、预览文件和下载临时数据按租户及账号隔离清理。
合并建议
- 将本文
9.14 会议笔记整段插入现有《IM 前后端逐图对接协议》第 9 章的9.13 总经理信箱-文案提示.png之后。 - 将现有
9.14 接口示例及其子章节顺延为9.15,并把本文9.14.9.1的接口行合并到新的第 9 章接口示例表中。 - 同步扩展会议数据模型:
im_meetings增加会议记录员字段,并新增会议笔记文件关联表;具体表结构在系统设计和数据库变更文档中统一维护。 - 主文档第 9 章现有会议创建示例需要补充
recorder_user_id,工作台示例需要增加meeting_notes模块并明确其unread_count=0。