审核提交成功后,连接断了。页面该显示什么?如果错误处理里直接切成“通过”,推送系统就成了绕过审核的入口。连接状态和审核状态必须分开保存。
ticket 与事件游标各管一件事
提交接口返回 ticket,表示这次审核任务;流事件带事件 ID,表示这个任务的有序变化。服务端先持久化状态与事件,再推送。重连从持久日志补发,而不是从内存里猜哪些事件客户端可能没看到。
WHATWG 的 SSE 规范说明了事件 ID、Last-Event-ID 和重新连接处理。10 月 22 日是当前页面的更新日期,不是 SSE 的首发日期。原生 EventSource 发起 GET,不能给它塞一个 POST 请求体。POST 流需要自行处理分帧与恢复,API 密钥也不能写进浏览器 URL。
下面采用教学合同:每个 ticket 的 seq 从 1 连续增长。标准 SSE 的 id 原本是字符串,并不保证连续或可做数值比较;只有服务端明确承诺该序列,我们才能用缺号判断是否需要拉取快照。
const terminal=new Set(['APPROVED','REJECTED','CANCELED']); const edges={ PENDING:new Set(['REVIEW','APPROVED','REJECTED','CANCELED']), REVIEW:new Set(['APPROVED','REJECTED','CANCELED']) }; function applyReview(s,e){ if(!e||e.ticket!==s.ticket||!Number.isSafeInteger(e.seq)||e.seq<1) return {...s,error:'invalid-event'}; if(e.seq<=s.last)return s; // 已持久处理的重复事件 if(e.seq!==s.last+1)return {...s,needsSnapshot:true}; if(terminal.has(s.status))return {...s,error:'terminal-conflict'}; if(!edges[s.status]?.has(e.status))return {...s,error:'invalid-transition'}; return {ticket:s.ticket,last:e.seq,status:e.status,needsSnapshot:false}; } function connectionLost(s){return {...s,connection:'reconnecting'};} const initial={ticket:'demo-ticket',last:0,status:'PENDING',needsSnapshot:false};重连不等于重新提交
图中 41、42 是说明游标的局部示意:事件从服务端向客户端下行,续传请求从客户端向流服务上行。REVIEW 是当前待审状态,不是业务终态。客户端应保留最后成功应用的游标,服务端必须校验 ticket 所属用户,再按游标读日志。只因为请求里带了 Last-Event-ID 就信任它,会把续传接口变成跨任务读取入口。
发现序列缺口时,页面显示“结果同步中”,从有版本号的状态快照恢复。不能跳过缺失事件后把游标推进到最新值,也不能在断线时重新发起审核。若旧日志已过保留期,服务端应明确要求快照恢复,而不是沉默返回空流。
连接层也需要收尾
服务端定期发注释心跳;它维持连接,不改变审核状态。反向代理是否缓冲、空闲超时多长、浏览器是否实际收到心跳,都要在测试环境检查。组件卸载或收到终态后调用 close,避免同一 ticket 留下多条连接。
终态冲突不在客户端自行“择优”:APPROVED 后又来 REJECTED,应查询权威快照并记录矛盾。若业务支持终态后的撤销,需要新的版本和明确的状态迁移,不能私下复用本例。
本轮验证了重复、缺号、未知事件、断线保持待审和终态冲突。没有启动 SSE 服务器或代理,不声称恰好一次投递。去重只能限制重复应用;服务端任务执行仍需自己的幂等和事务边界。
复现记录
本轮在 Node.js v26.3.0 中运行随文核心逻辑的构造样例,进程退出码为 0。测试输出:
PASS: disconnected pending, deduplication, sequence gap, terminal conflict, ticket boundary将上方完整代码保存为 example.mjs,以 Node.js 26 执行 node example.mjs 可加载核心与教学输入;函数默认不访问网络、模型或硬件。浏览器适配入口需要明确传入对应对象,本轮未执行这些入口。上面的通过项来自独立构造样例,不能替代集成测试。
资料与时效
- HTML Living Standard: Server-sent events:首次发布日期未核实;更新 2026-10-22。
- 语音合成(LAS):首次发布日期未核实;更新 2026-10-16。
本稿对应原预排时段;实际发布前须人工复核来源更正、SDK/API 版本及可用性,不据此预言 10 月 24–31 日的新事件。