state ∈ 有效 / **被拒(需重扫)** / 未知;🔴 -14 是**被拒不是过期**,且**未知原因绝不 写成「过期」**(面板文案与错误码都如实)。三态判定为纯函数 + 三态各有用例。 ⚠️ 未做(如实登记): 三态(每账号逐条三态)本片未做。
- known=false ⇒ accountOnline 键**缺席**(未知 ≠ 离线),与既有 connected 语义并列不合并 - 面板:账号在线/账号离线/账号状态未知 三态 + 中性灰 tone + 「这不等于离线」悬停说明 - 测试:gateway 跨层锁「键缺席」(用真 qq_obv11.Adapter)+ 面板 61/40 断言 - 变异:known 当离线 ⇒ 两条测试同时红(已复跑确认) - Docs/api-client.md:§7.2 那行由未实现改已实现(留表,注明下版移入 §7.1)
缺口①:「连接」是按平台连、且恒打第一条账号(channelActionRequest 只有 name)⇒ 主人有两条 qq-obv11 时点到的是 mode=server 的历史条目,报 server mode requires listenAddr,而正确的 client 条目轮不到。缺口②:空 id 条目既删不掉(DELETE 要求 id 非空)也改不了(UpsertChannel 空 id = 新建),越改越多。 后端: - POST /admin/channels/open|close 请求体新增可选 accountId(缺省逐字兼容,老客户端零影响); 按该账号驱动;未知 id ⇒ 404 且零连接(不静默回落第一条);已配置未注册 ⇒ 409 - DELETE /admin/channels/{platform}/config 新增显式 empty=1(id 与 empty=1 互斥 ⇒ 400; 无可删 ⇒ 404);语义严格 —— 只删空 id 条目,绝不把「id 为空」默认解释成删空 id - 响应新增:open/close 的 resolved(实际驱动的适配器名)与 accountId;delete 的 removed/empty; GET /admin/channels 的 accounts[].connected(可选 bool,缺省 = 未知) - 审计动作名不变(channel.open/close/delete),日志行新增 accountId 字段 前端:每条账号一行「连接/断开」(带 accountId);按钮旁明确「会连哪一条」;空 id 行显著标注 + 删除入口;channelActionBody 携带 accountId;新增 action 测试 33 checks + 空 id 行渲染 31 checks。 测试:后端 14 个用例(真实 HTTP 路由 + JWT + 真注册表 + spy 适配器,断言「哪一条被驱动、 另一条一次都没被碰」)。 变异 6 条(accountId 被忽略 / id 为空当删空 id / 找不到静默回落 / 前端丢 accountId / 删空 id 退化成 ?id= / 空 id 行按普通行渲染)。