Fetching metadata from the HF Docker repository... 15 days ago
-
聊天室:修复置顶 422,未读角标改挂到用户行
xiaolongmr
置顶报 422 的根因:
- 前端把 body 做了 JSON.stringify 再交给 httpRequest,而 httpRequest 是直接把它
当 axios 的 data 传下去的 —— **传字符串时 axios 不会自动补
Content-Type: application/json**,FastAPI 就把 body 当非 JSON,
报「Field required」→ 422。
- 修法:改成传**原始对象** `body: { pinned }`,与文件里其他 POST 调用一致。
(curl 复现:不带 Content-Type 的字符串 body → 422,带上 → 200。)
未读角标的位置与语义:
- ⚠️ 不再把「会话数」红色角标挂在「私聊」分组标题上 —— 那个数字分不清是谁发来的。
改成挂在**每个用户那一行**(对方昵称右侧),显示「这个人有几条我没看」。
- ⚠️ 原来 direct_conversations() 的 total 是「所有非我发的消息总数」,
没有已读概念 → 角标只增不减(读完也不消)。现在按已读位置过滤。
- 已读位置**不落库**:存 localStorage(`chatgpt2api_chat_room_direct_read`,
peer_id → 已读到的最大 id),接口用 `mark=peer:last_id,...` 把整张表带过去,
服务端只数 `id > last_read` 的对方消息。这样不必新建「每用户每会话已读表」。
- ⚠️ 私聊的已读位置**与公共大厅分开存**:两边消息 id 不同源,共用一个 key 会互相顶掉。
- 打开会话 / 正在看该会话时收到新消息 → 立刻写已读位置并广播事件,
收件箱监听该事件即时重拉,角标当场归零,不必等下一个轮询周期。
- `_parse_marks` 把前端输入一律当不可信:格式不对整段丢弃,宁多显示未读也不能 500。
另外顺手改:收件箱摘要里自己发的消息加「我:」前缀(否则看起来像对方说的),
已撤回的显示「[消息已撤回]」而不是空白。
验证:四套聊天室测试共 64 项全过(服务层 24 + HTTP 11 + 用户侧端到端 15 + 未读 14)。
tsc / eslint 全绿。已清… -
聊天室:管理员置顶公告 + 修复私聊收不到/回不了
xiaolongmr
置顶(仅管理员,服务端强制):
- 消息表新增 pinned_ts / pinned_by 两列,_row_to_message 下发 pinned / pinned_at。
- set_pinned 只放行 admin;已删除的消息不能置顶;撤回时一并清掉置顶。
- 新增 GET /api/chat-room/pinned 与 POST /api/chat-room/messages/{id}/pin。
- 置顶条**独立于翻页列表**:置顶消息可能早就不在当前页,仍要常驻顶部。
- 前端在 header 与消息区之间加置顶横幅,多条默认只露第一条(可展开),
点击跳转到原消息(不在当前页时给提示,不静默无反应)。
修复「管理员私聊用户,用户看不到消息,也无法回复」——两个独立成因:
1) 权限判定用错:_assert_can_direct(只允许 admin)被误用到读历史/回复路径,
导致收件人侧一律 403。拆出 _assert_can_access_conversation(会话双方可访问),
读历史与「回复已有会话」走它;只有「发起全新会话」才要求 admin。
⚠️ 判定新会话不能只看「me 在参与者里」——新会话的键就是 dm:me|peer,恒成立,
必须用 _has_history() 查是否有实际往来,否则普通用户能私聊任何人。
2) 收件人没有入口:原未读统计只看公共大厅,收件人根本不知道有人私聊自己。
新增 direct_conversations() + GET /api/chat-room/direct-conversations,
前端侧栏加「私聊」区块(头像/昵称/摘要/在线点 + 计数徽章)。
验证:服务层 24 项、HTTP 接口 11 项、用户侧真实鉴权端到端 15 项,共 50 项全过。
后端全量测试 948 passed,失败 26 项均为既有环境漂移(chrome 指纹版本、导航迁移),
与本次改动无交集(chat_room 相关 0 项失败)。 -
聊天室:头像回退微信头像、复用画图页输入框、撤回删除、布局收口
xiaolongmr
头像
- 根因是聊天室与 presence 只读 identity["avatar_url"],而微信登录用户的头像
存在 wechat_avatar 里,于是恒为空、界面只显示首字占位(签到页能显示是因为
它两个字段都读)。新增 presence_service.resolve_avatar_url 统一口径:
先 avatar_url,再回退 wechat_avatar,与顶栏 / 画图页 / 批量页 / 签到页一致。
- 在线列表、消息落库、summary.me 三处都改走这个函数。
输入框
- 底部输入框改用画图页同一套 @/components/prompt-composer 部件
(Surface / Textarea / Toolbar / SubmitButton),外形与画图页完全一致。
- @ 唤出面板通过 suggestions 传入,键盘拦截走 onKeyDownCapture
(返回 true 表示面板已消费该按键),粘贴图片仍走 onPaste。
撤回与删除
- 软删除:新增 deleted_ts / deleted_by 两列,不做物理删。消息 id 是前端翻页
游标,物理删会留下空洞、导致增量轮询的序列错乱。
- 已删消息的正文与图片在服务端就抹空(不能只靠前端不渲染,否则直接读接口
仍能拿到内容)。前端显示「你/某人撤回了一条消息」或「管理员删除了一条消息」。
- 权限在服务端强制:作者本人 2 分钟内可撤回(RECALL_WINDOW_SECS=120),
管理员可删任意消息。前端按钮只按同一规则决定是否渲染,不作为权限依据。
- 新增 DELETE /api/chat-room/messages/{id}。
- mergeMessages 支持就地更新:撤回后 id 不变而内容变了,若只做「去重后追加」,
轮询拉回的已删状态会被旧数据挡住,界面永远不会更新。
布局
- 输入框区去掉「所有登录用户可见,请勿发送隐私信息」。
- 左上角侧栏头部由「在线用户 + 计数 + 搜索框」改为单个「公告聊天室」标题。
- section 高度由 min-h 改为固定 h:AppShell 内层本身是 min-h-screen 加
pt-[5.25rem],section 再叠 min-h 会与之相… -
新增聊天室页面:登录用户群聊
xiaolongmr
- 后端:services/chat_room_service.py(SQLite 只追加存储,经镜像 runtime_file 持久化)+ api/chat_room.py(消息增删查 / 汇总接口,均需登录)
- 前端:/chat-room 页面,3s 轮询增量拉取、乐观发送、回车发送、500 字上限、自动滚底、日期分隔与角色徽章
- 导航:三套角色导航均插入「聊天室」(对话之后),含一次性迁移