4.4 KiB
外围工单主动同步
工单的创建、编辑、现场反馈和删除改为由来源事件触发同步。浏览器继续使用已有工作台 SSE,不需要新增连接或定时工单轮询。
外围工单写入
→ 同一 PostgreSQL 事务保存源记录、历史版本和变更事件
→ COMMIT 后 NOTIFY
→ Server 工作台 SSE 唤醒外围同步器
→ 同步器通过授权 API 更新任务投影
→ task/action/notification 事件推送给浏览器
数据库范围
本次仅启用 tjwater_v2。没有新增表、列或迁移;复用 workbench.domain_event_log 的 workbench.work_order.changed topic。源事件的聚合类型为 peripheral_work_order,元数据只包含 source=peripheral_sample、工单 ID 和 created|updated|deleted,不包含工单正文。事务失败时,源记录、历史、事件及通知一起回滚;同一请求的幂等重放不再产生事件。
外围服务只写源记录及源变更事实,不直接改业务任务。任务投影、调查创建和通知仍通过 Server 状态机和现有授权 API 处理。其他数据库不写入该源事件;扩展前遵循 数据库变更门禁。事件表缺失或无写权限时,tjwater_v2 的工单写入返回存储错误,不会假装成功。
同步器
从 Server 仓库运行已有命令:
conda run -n server python scripts/sync_peripheral_work_orders.py
conda run -n server python scripts/sync_peripheral_work_orders.py --once
继续复用 OPERATIONAL_WORKER_* OAuth 工作身份及 OPERATIONAL_ACTION_PERIPHERAL_URL,不需要新账号或密钥。启动和游标过期时先读取事件游标,再进行全量对账;扫描期间的事件从该游标继续补读。相同源版本仍由原幂等键去重。源事件按工单 ID 唤醒同步器;动作回执事件只补查该任务关联的工单,避免源创建早于回执保存时漏同步。任务投影事件不再触发全量扫描。
正常状态收到源事件即处理对应 ID:GET /api/v1/operational-tasks?peripheral_work_order_id=<uuid> 只返回同项目的来源任务和动作回执关联任务;外围接口只读取该工单,不再分页遍历全部工单和全部调查详情。重复 ID 合并;目标工单的明确删除只撤回该工单投影,不影响其他任务。
内存待处理 ID 最多 200 个,超过上限合并为一次全量对账,不持续扩张队列。这是突发积压保护,不增加公共配置项。
默认每 300 秒仍进行一次全量对账,处理漏通知、断线、旧批量脚本直接写库及重启。持续收到事件不会推迟全量对账期限。可用 --reconcile-seconds 调整这一兜底周期(最小 1 秒),不再复用工况 worker 的 30 秒租约检查周期。同步失败按 5、10、20、40、60 秒退避重试;事件连接断开每 5 秒重试,保留最后游标。扫描开始前取出待处理 ID,扫描期间新到达的 ID 留给下一轮;失败的 ID 会放回队列,重试不依赖新消息。
浏览器收到源事件时不会提前刷新业务快照,而是等待任务投影事件后读取对应任务。工况手动刷新仍只读 Server 权威快照。工单事件不等于 Header 通知;只有需要处理的业务待办或告警才进入通知列表。
游标顺序、共享 Hub 查询与兼容边界见 事件链路收敛。新增查询参数已从 Server OpenAPI 同步生成 Agent 类型,不另建手写契约。
验证与启用
conda run -n server python -m pytest tests/unit/test_peripheral_push_sync.py tests/unit/test_peripheral_work_order_events.py tests/unit/test_peripheral_task_sync.py -q
PERIPHERAL_SAMPLE_LIVE_TEST=1 conda run -n server python -m pytest tests/integration/test_peripheral_work_order_events.py -q
数据库测试强制限定 tjwater_v2,使用随机项目键和回滚事务,不保留测试工单或事件。覆盖创建、修改、反馈、删除、幂等重放及回滚无事件。HTTP 隔离测试覆盖源事件即时投影、游标重置、断线续传、失败重试和监听取消。
上线时更新外围服务、Server 同步器及前端,再按既有发布流程重启对应进程。提交代码不会自动重启现有服务;旧同步器进程仍按旧的 30 秒周期运行。回退应用代码不涉及数据结构回滚,已有事件仍可正常补读;回退到旧同步器后恢复周期对账。