Skip to content

接续 #127:把设备侧动作通道接入运行时并打通通知流调用链 #179

Description

@JunLang-7

背景

#127 / PR #170 已实现并测试 ImActionChannelEspActionStreamTransport、时间窗口、幂等与重连语义,但这些组件尚未进入生产运行时:当前没有生产 NotificationIntent 调用方,voicelife_runtime 未装配 IM 组件,esp32s3-devim 仍为 disabled

本 Issue 负责把已有设备侧动作通道接入真实运行时,不重新设计 Gateway 或跨入口动作状态模型。

目标

形成并在真机上验证以下生产调用链:

TimingTask 已提交的到点通知事件
  → NotificationIntent 映射与 SubmitNotification
  → NotificationSubmission.actionStream
  → ExtractActionWindow
  → ImActionChannel::Run
  → TimingTask 动作执行
  → ReminderActionResult HTTPS 回传

前置条件

范围

1. Runtime 生产装配

  • voicelife_runtime 的生产组合根中装配 IM 运行时依赖,而不是在测试或示例入口旁路启动;
  • 注入真实 EspActionStreamTransportEspHttpTransportEspClock、设备凭据 Provider 与 TimingTask-backed Action Executor;
  • Runtime 明确拥有 IM 工作任务的启动、停止和资源生命周期,重复启动不得创建重复任务或重复订阅;
  • main/Runtime 不引用 fake、mock 或测试 fixture;
  • im=disabled 仍是可用回退路径,禁用后本地日程与语音提醒不受影响。

2. 通知事件来源

  • TimingTask 已成功提交的到点提醒事件 作为生产 NotificationIntent 唯一触发源,不使用启动时合成事件、mock scenario 或轮询数据库拼装事实;
  • 建立从本地提醒事件到 NotificationIntent 的 Mapper/Adapter,保留 eventIdcorrelationIddeviceIdreminderTriggerId、提醒强度和发生时间;
  • 只有本地事实提交成功后才允许通知上报;网络失败不得回滚已提交的 ReminderTrigger;
  • 同一事件重放使用稳定 eventId,不得造成重复 Gateway Delivery;
  • 弱提醒受理结果不得产生 actionStream,Runtime 也不得建立 SSE;
  • 强提醒仅在受理响应包含有效 actionStream 时调用 ExtractActionWindowRun

3. 设备凭据

  • 实现生产 ImCredentialProvider,提供与设备身份一致的 deviceId 和设备 Token;
  • Token 从设备安全存储或受控 provisioning 注入路径读取;仓库、固件二进制默认值、Profile、日志和 fixture 中不得出现真实 Token;
  • Profile 只允许配置非 Secret 元数据(例如 Adapter 选择、Gateway origin 或凭据引用),公开占位值不得被当作有效生产凭据;
  • 凭据缺失、格式非法、设备身份不匹配或读取失败时 fail closed:不建立 SSE、不发送未认证请求,并输出不含凭据值的稳定诊断;
  • HTTPS 与 SSE 使用同一设备身份;Gateway 返回 401/403 时不得无限紧密重试。

本 Issue 必须落地至少一条可用于真机验收的安全注入路径;完整的设备注册、批量烧录、轮换后台不在本期范围。

4. 网络与 TLS

  • Runtime 在网络就绪、DNS 可用且系统时间满足证书校验条件后启动 HTTPS/SSE;网络丢失时释放连接并等待恢复;
  • Gateway 使用固定 https:// origin,通知提交、SSE 和结果回传不得降级为明文 HTTP;
  • 使用受信 CA/证书链进行服务端身份校验,主机名校验开启;禁止 skip verify、接受任意证书或把临时自签名例外带入生产 Profile;
  • 连接、读取和请求具有有限超时;重连使用有上限的退避并受 Action 有效窗口约束;
  • SSE 断线携带 Last-Event-ID 恢复,但 Last-Event-ID 不替代 ReminderActionResult
  • 日志可通过 correlationId 追踪通知提交、SSE 命令、执行结果和重连,且不记录 Authorization、Action token 或完整敏感 URL。

5. 动作执行与 Profile

  • 实现生产 ImActionExecutor 到 TimingTask Application Port 的映射;
  • 执行前校验 deviceIdreminderTriggerId、命令有效期和动作参数;
  • operationId 在重放、重连和设备任务重启场景下保持幂等;
  • 执行成功或失败后通过 HTTPS 回传 ReminderActionResult,未回传成功的结果可安全重试;
  • 启用 esp32s3-dev Profile 的 IM Adapter,并为 Gateway origin、凭据引用和 TLS 信任配置提供可检查的 Profile 校验;
  • 不把微信、Koishi 或 Action UI 类型引入设备组件。

非目标

TDD 计划

RED:

  • 生产 Runtime 组合测试因 IM 组件零引用而失败;
  • TimingTask 到点事件无法触发 SubmitNotification
  • 缺失/错误设备凭据仍尝试联网;
  • TLS 证书或主机名校验失败时连接仍被接受;
  • 弱提醒错误建立 SSE;
  • SSE 重连后相同 operationId 被执行两次。

GREEN:

  • 完成 Runtime、通知源、凭据、网络/TLS 与 TimingTask Executor 装配;
  • 强提醒受理后在有效窗口内建立临时 SSE;
  • 命令幂等执行并可靠回传结果;
  • 网络恢复后按窗口与退避策略续传,过期后停止。

REFACTOR:

  • 组合根只负责装配,契约映射、网络、凭据与业务执行边界分别由 Port/Adapter 承担;
  • 公共 fixture 继续被 C++ 主机测试与 TypeScript Gateway 测试复用;
  • 删除为验收临时增加的硬编码 endpoint、凭据和测试旁路。

验收

自动验证

  • 存在测试证明生产 voicelife_runtime 实际引用并启动 IM Adapter,而非只验证组件可编译;
  • 主机测试覆盖通知事件映射、弱/强提醒分支、凭据 fail-closed、TLS 校验、超时退避、SSE 重连、operationId 幂等与 Result 重试;
  • python3 scripts/firmware.py build esp32s3-dev 通过;
  • ./scripts/run_checks.sh 通过;
  • C++/TypeScript 共享契约 fixture 通过。

真机与独立 Gateway 验收

Given:

  • ESP32-S3 使用启用 IM 的有效设备 Profile;
  • 设备凭据通过本 Issue 定义的安全路径注入,并已在 Gateway 注册;
  • Wi-Fi、DNS、系统时间与 TLS 信任链正常;
  • [IM] Gateway 服务进程装配与可部署性 #152 Gateway 通过固定 HTTPS 地址运行,公众号测试账号已绑定。

When:

  1. 真机 TimingTask 产生并提交一个强提醒到点事件;
  2. Runtime 提交 NotificationIntent,Gateway 完成一次真实公众号投递;
  3. 用户在 H5 选择“推迟 10 分钟”;
  4. SSE 在动作有效期内主动断开一次,然后恢复;
  5. 设备执行命令并回传结果。

Then:

  • 通知来源可追溯到真实 TimingTask 事件,不是 mock/手工 HTTP 代发;
  • 微信只收到一次有效提醒;
  • 设备只在强提醒窗口内建立 SSE;
  • 重连后命令可重放,但相同 operationId 本地只执行一次;
  • 本地 ReminderTrigger 进入 snoozed,并产生正确 nextTriggerAt
  • Gateway 收到 ReminderActionResult 后关闭当前动作流;
  • 断网恢复或设备任务重启不回滚本地事实,也不产生第二次动作;
  • 脱敏日志可用同一 correlationId 串联 TimingTask 事件、通知提交、Delivery、Action、SSE 与 Result;
  • 验收记录包含固件 commit、Gateway commit、Profile 名称、Gateway 环境、证书校验方式和脱敏结果截图/日志。

风险与回退

  • TLS、网络或凭据异常时 IM Adapter fail closed,本地提醒继续运行;
  • Profile 可把 im 切回 disabled
  • Gateway 故障只影响远端投递与动作闭环,不得修改或回滚设备本地事实;
  • 未确认命令在窗口结束后自然到期,不通过人工改库恢复。

Refs #95, #127, #152, #170, #214

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions