Files
story/docs/integration-diagnostics.md
T

211 lines
24 KiB
Markdown
Raw Permalink Normal View History

2026-08-26 06:41:07 +08:00
# 集成诊断工作台开发说明
> 对应接口契约:OpenAPI 0.55.0
> 适用角色:`ops_viewer`、`ops_operator`、`ops_approver`、`ops_admin`
> 适用阶段:模拟联调、真机联调、试点运行和生产故障处置
## 1. 建设目标与安全边界
集成诊断工作台用于把设备、通话、RTC房间、供应商任务和接口请求关联到同一条脱敏诊断记录,减少联调时在多个数据库表和日志系统之间人工拼接信息的成本。查询始终使用已经验证的OIDC身份和明确的`X-Tenant-Id`,服务端先解析运营账号与客户组织关系,再固定租户条件执行查询。
工作台不返回字幕正文、录音地址、RTC短期令牌、设备签名密钥、供应商API Key、Outbox正文或配置密钥内容。`ops_viewer`和`ops_approver`只能查询;`ops_operator`可执行配置刷新、安全停止、状态对账、死信重试和诊断包导出;设备吊销仅允许`ops_admin`执行。所有修改动作必须填写原因、提供幂等键,并记录操作人、请求关联编号、资源和结果。
字幕正文和原录音不属于诊断查询返回。需要排查内容问题时,必须转入[运营敏感数据临时访问](ops-sensitive-access.md)流程,按单次通话申请最小能力、由另一人审批并逐次审计。
## 2. 功能设计
### 2.0 外部依赖预检
API和Worker按配置周期检查OIDC JWKS、方舟HTTPS入口及火山RTC HTTPS入口。OIDC检查要求响应成功、JSON可解析且至少包含一把签名密钥;方舟和RTC只执行DNS、TCP及证书校验TLS握手,不发送模型或语音业务请求,不消耗额度。V35把每个进程和依赖的最近结果写入平台级状态台账,字段包括最近检查、最近成功/失败、耗时、探针周期、连续失败和有限错误码。台账不保存实际端点、证书正文、原始网络错误或任何凭据。
联调页面把超过三倍探针周期未更新的记录显示为“结果过期”,即使其最后状态为成功也不能作为当前可用证据。进程启动时删除本进程已停用依赖的旧记录,避免从方舟或RTC切回本地实现后继续展示历史探针。探针失败不会让健康API或Worker退出就绪状态,但会进入Prometheus和告警规则,便于区分“平台自身不可用”和“外部联调条件未满足”。
### 2.1 精确检索
| 检索字段 | 输入 | 主要关联路径 | 返回信息 |
|---|---|---|---|
| `device_id` | 设备公开UUID | `device → conversation → rtc_task` | 设备状态、在线状态、固件/配置版本及相关通话 |
| `conversation_id` | 通话公开UUID | `conversation → device/rtc_task/provider_event` | 通话状态、预留期限、房间及供应商任务状态 |
| `room_id` | RTC房间编号 | `rtc_task → conversation → device` | 对应通话、设备和供应商任务 |
| `provider_task_ref` | 供应商任务编号 | `rtc_task → conversation → device` | 供应商、任务状态、启动/停止尝试次数 |
| `request_id` | 接口请求关联编号 | `audit_event → resource → conversation/device` | 该请求涉及的脱敏资源状态 |
查询只支持完整值匹配,不提供字幕、姓名、手机号等个人数据的模糊检索。单次最多返回50条,设备检索可返回该设备的多条历史通话,其余唯一业务编号通常只返回一条。
### 2.2 状态展示
每条记录包含以下诊断字段:
- 设备公开编号、注册/吊销状态、Redis在线状态、固件版本、期望/已应用配置版本和最近归一化错误码;
- 通话公开编号、通话状态、房间资料有效期;
- 短期入房凭证签发时间和过期时间,不返回凭证正文、摘要或可重建凭证的签名材料;
- RTC房间编号、供应商代号、稳定任务编号、任务状态、启动和停止尝试次数;
- 已接收供应商通知数量、最近通知类型与时间;
- 最近一次相关请求编号。
存在通话时,接口同时从`conversation.created_at/ended_at`和`conversation_state_history`的首次状态到达时间计算五类耗时:创建至`ROOM_ISSUED`、房间资料签发至设备确认`STARTING_AGENT`、设备确认至供应商`ACTIVE`、`STOPPING`至最终结束,以及创建至最终结束。尚未到达的阶段返回空值,时间倒置的数据不生成负数;页面以毫秒、秒或分秒展示,不使用浏览器请求时间代替业务阶段耗时。
### 2.3 授权修复动作
| 动作 | 允许状态 | 处理结果 |
|---|---|---|
| 配置刷新 | 设备属于选定客户组织 | 以当前期望版本创建幂等`apply_config`设备命令 |
| 状态对账 | `STARTING_AGENT`或`STOPPING` | 分别创建启动/停止对账Outbox任务;稳定状态返回`queued=false` |
| 安全停止 | `PREPARING/ROOM_ISSUED` | 不调用供应商,直接收敛为`EXPIRED`并释放房间 |
| 安全停止 | `STARTING_AGENT/ACTIVE` | 收敛为`STOPPING`并可靠投递供应商停止任务 |
| 安全停止 | `STOPPING`或最终状态 | 幂等返回当前状态,不重复创建副作用 |
| 设备吊销 | 设备属于选定客户组织 | 写入吊销时间并立即阻断后续设备签名请求;若存在未结束通话,则在同一事务内过期或可靠停止 |
| 死信重试 | 状态为`dead`且事件类型已注册 | 重置为`pending`,保留并递增人工重试次数、操作人和时间;未知事件类型拒绝重试 |
| 诊断包导出 | 五类精确检索条件有效 | 生成`ops-diagnostic-minimal-v1`脱敏JSON快照,不写正文副本 |
| 发布候选验收快照 | 候选版本、SHA-256构建摘要、环境、范围和必需依赖明确 | 在单个事务内冻结迁移、依赖、问题、任务和告警门禁,生成`ops-acceptance-minimal-v1`归档JSON |
| 试点运行日报 | 业务日期、IANA时区和归档原因有效 | 聚合六类运行证据并生成`ops-pilot-daily-minimal-v1`脱敏JSON,不在服务端保存正文 |
| 试点运行周报 | 周一业务日期、IANA时区和归档原因有效 | 聚合内容审核、记忆、隐私、固件和配置差异,并明确要求外部投诉证据 |
配置刷新、状态对账、安全停止和设备吊销均从具体诊断结果卡片打开页面内“受控联调操作”确认区,不再通过浏览器弹窗采集原因。确认区固定展示目标设备;状态对账和安全停止还固定展示目标通话,切换客户组织或重新检索会清除旧操作草稿。操作原因必填、最长500字符,空理由不能提交。安全停止与设备吊销还必须勾选对应影响确认,其中设备吊销明确提示后续签名请求将被拒绝,安全停止明确限定为当前通话。取消只清空页面草稿,不调用修改接口。角色、租户、状态、幂等和审计仍由服务端重新校验,前端确认不能替代服务端门禁。
诊断包导出和死信重试同样使用页面内表单。导出表单在打开时冻结检索字段和精确编号,展示中文字段名称、完整目标值、脱敏边界和审计提示;用户随后修改检索框不会改变已经打开的导出目标,重新执行查询则清除旧导出草稿。死信重试表单冻结任务公开编号、注册事件类型、聚合类型、自动/人工尝试次数和最近稳定错误码,并要求填写最长500字符的原因,再勾选“根因或依赖问题已经处理”的影响确认。刷新任务列表、切换客户组织或取消会清除旧任务草稿。页面不展示Outbox正文、内部聚合编号或锁信息。
### 2.4 死信作业摘要
死信列表最多返回100条,只展示作业公开编号、已注册事件类型、聚合类型、自动尝试次数、人工重试次数、稳定错误码和时间。接口不返回Outbox正文、内部聚合编号或锁持有者。人工重试会重新启用一轮自动重试预算,但不清除人工重试历史;若根因未修复,任务仍会按显式重试分类再次进入退避或死信。
### 2.5 联调问题闭环
问题清单按客户组织保存需求P0282规定的可追踪字段:问题现象、严重级别、设备编号、通话编号、发生时间、请求关联编号、责任方、根因、解决结果、修复版本、回归证据和回归结论。设备、通话和请求编号均为诊断引用;记录不得粘贴字幕正文、录音地址、短期凭证、长期密钥或带签名下载地址。设备和通话编号同时填写时,服务端验证二者属于同一关联链,避免建立错误关联。
问题状态为`OPEN → IN_PROGRESS → RESOLVED → VERIFIED → CLOSED`,严重级别为`LOW/MEDIUM/HIGH/CRITICAL`。重新处理时可以回到`IN_PROGRESS`;进入`RESOLVED`及后续状态必须填写解决结果、根因和修复版本,进入`VERIFIED`或`CLOSED`还必须填写回归结论和脱敏回归证据。V36汇总当前客户组织全部问题,而不是只统计页面最近100条;任何未关闭的HIGH或CRITICAL问题均计入高严重度发布阻断。该布尔结果只证明此项门禁,不替代供应商、真机、安全、恢复和合规门禁。创建请求使用幂等键,后续维护使用强ETag,防止两名联调人员静默覆盖彼此结论。只读运营角色可以查看,`ops_operator`和`ops_admin`可以登记和维护,每次写入均记录独立审计。
问题维护不再使用连续浏览器弹窗,而是从具体问题卡片打开统一的页面内流转编辑区。编辑区固定问题公开编号、当前状态、当前责任方、现象和ETag版本,再根据动作只显示当前必需字段:调整级别要求选择不同值;接手或重新打开要求最长120字符的责任方;登记解决同时要求根因、最长128字符的修复版本及解决结果;登记回归同时要求脱敏回归证据和结论;关闭要求显式确认已经复核根因、修复版本、解决结果和回归证据。正文类字段最长4000字符。切换筛选、刷新清单、切换客户组织或取消会清除草稿;并发更新仍由服务端`If-Match`校验,冲突时保留页面输入和错误提示供人工复核。
背景资料准备失败是无需供应商对账的本地终态。精确查询该通话时,诊断记录返回`conversation_status=FAILED`和`conversation_failure_code=context_preparation_failed`,RTC状态、房间和供应商任务均为空;工作台与设备HTTP 422响应使用同一个失败码,联调人员可直接登记问题,不得把空RTC任务误判为任务丢失。
### 2.6 发布候选验收快照
验收快照用于阶段8每次真实联调、试点扩量或生产发布前的技术状态冻结。操作人必须填写候选版本、`sha256:`构建摘要、`LAB/STAGING/PRODUCTION`环境、验收类型、覆盖范围、必需外部依赖和最长255字符的归档原因。服务端在一个数据库事务内读取当前迁移版本及脏状态、当前客户组织的联调问题全量汇总、租户及平台级Outbox积压、未解决HIGH/CRITICAL告警,以及所选外部依赖的最新探针状态。
六项门禁分别为候选构建可识别、数据库迁移清洁、必需依赖当前可用、无未关闭高严重度联调问题、无待处理/处理中/死信可靠任务、无未解决高严重度告警。探针记录超过三倍探针周期即视为过期;缺少记录不能按成功处理。实验室环境允许运行版本为`dev`,便于本地准备;预发布和生产环境要求运行版本等于候选版本,同时提交号和构建时间不能为`unknown`。`overall_gate_satisfied`是六项技术条件的合取结果,不包含真机音频体验、真实供应商协议、灰度观察、容量、安全、合规或人工审批,快照正文固定携带该免责声明。
OpenAPI 0.51.0在告警计数之外加入最多100条阻断告警定位项,只包含告警公开编号、规则码、级别、处置状态、关联资源类型/公开编号、首次/最近命中时间和资源核对结果。`PRESENT`表示关联资源仍存在,`MISSING`表示快照时已找不到关联公开资源,`AGGREGATE`表示队列类聚合告警,`UNKNOWN`表示该资源类型没有自动存在性判断。该字段不返回告警标题、摘要、接收组或原始错误;`MISSING`只能作为运营复核证据,不能自动关闭告警。总数超过100时`blocking_items_truncated=true`,必须转到告警工作台查看完整队列。
快照不建立服务端历史表,返回后由浏览器下载归档。页面同时保留本次响应,直接展示六项门禁和阻断告警定位,切换客户组织时立即清除,避免把上一组织的结果误作当前状态。幂等记录保证网络重试返回同一编号与生成时间;审计只保存候选标识、环境、范围、所选依赖、结果和脱敏策略。输出不包含探针地址、凭据、原始错误、告警正文、Outbox正文、字幕或录音。
### 2.7 试点运行日报
OpenAPI 0.55.0要求日报绑定候选版本与不可变构建摘要,再按指定IANA时区的自然日生成设备、通话、供应商、上传、告警和费用快照。自然日活动和生成时刻积压采用不同字段,通话始终返回8个状态,费用按币种分列且不做汇率换算。日报只保存幂等和审计元数据,不保存服务端正文;详细口径、接口和数据来源见[试点运行日报开发说明](pilot-daily-operations.md)。
### 2.8 试点运行周报
OpenAPI 0.55.0要求周报绑定候选版本与不可变构建摘要,再按周一开始的七个业务自然日生成内容审核、正式记忆、隐私请求、固件分布和配置差异快照。人工修订率和故事确认率同时返回样本数及可计算标识;没有分母时不能解释为0%。平台没有投诉权威表,因此周报固定保留外部投诉证据缺口。详细设计见[试点运行周报开发说明](pilot-weekly-review.md)。
## 3. 接口设计
所有接口要求`Authorization: Bearer ...`和`X-Tenant-Id`。修改接口还要求`Idempotency-Key`,请求体为`{"reason":"..."}`。
| 方法与路径 | 最低角色 | 说明 |
|---|---|---|
| `GET /api/v1/ops/diagnostics/search?field={field}&value={value}` | `ops_viewer` | 按五类字段精确查询脱敏状态 |
| `GET /api/v1/ops/diagnostics/external-dependencies` | `ops_viewer` | 查询API/Worker外部依赖预检台账 |
| `POST /api/v1/ops/diagnostics/conversations/{id}/stop` | `ops_operator` | 按当前状态执行本地收敛或可靠停止 |
| `POST /api/v1/ops/diagnostics/conversations/{id}/reconcile` | `ops_operator` | 对启动/停止中间态创建幂等对账任务 |
| `POST /api/v1/ops/diagnostics/devices/{id}/config-refresh` | `ops_operator` | 创建当前配置重新应用命令 |
| `GET /api/v1/ops/diagnostics/jobs` | `ops_viewer` | 查询当前客户组织的脱敏死信摘要 |
| `POST /api/v1/ops/diagnostics/jobs/{id}/retry` | `ops_operator` | 对已注册类型的死信作业执行受控重试 |
| `POST /api/v1/ops/diagnostics/bundles` | `ops_operator` | 按精确检索条件生成脱敏JSON诊断包 |
| `POST /api/v1/ops/diagnostics/acceptance-snapshots` | `ops_operator` | 幂等生成并下载发布候选技术验收快照 |
| `POST /api/v1/ops/diagnostics/pilot-daily-snapshots` | `ops_operator` | 按业务自然日幂等生成并下载脱敏试点运行日报 |
| `POST /api/v1/ops/diagnostics/pilot-weekly-snapshots` | `ops_operator` | 按周一开始的业务自然周幂等生成并下载脱敏试点运行周报 |
| `POST /api/v1/ops/diagnostics/devices/{id}/revoke` | `ops_admin` | 吊销设备,并联动收敛该设备未结束通话 |
| `GET /api/v1/ops/integration-issues` | `ops_viewer` | 查询最近100条问题及全量高严重度发布阻断汇总,可按状态筛选 |
| `POST /api/v1/ops/integration-issues` | `ops_operator` | 幂等登记问题现象、严重级别、关联编号、发生时间和责任方 |
| `PATCH /api/v1/ops/integration-issues/{id}` | `ops_operator` | 使用If-Match维护严重级别、责任方、根因、修复版本和回归证据 |
成功响应返回动作、最新状态、是否排队和相关命令编号。跨客户组织、只读角色或不存在资源不会泄露其他租户信息;响应统一带`Cache-Control: no-store`。稳定错误包括`OPS_ACCESS_DENIED`、`DIAGNOSTIC_NOT_FOUND`、`DIAGNOSTIC_STATE_CONFLICT`和`IDEMPOTENCY_KEY_CONFLICT`。
## 4. 业务流程设计
### 4.1 联调定位流程
```mermaid
sequenceDiagram
participant O as 联调/运营人员
participant API as 诊断API
participant DB as PostgreSQL
participant R as Redis
O->>API: OIDC令牌 + 客户组织 + 精确编号
API->>DB: 校验运营账号与租户关系
API->>DB: 查询设备/通话/RTC任务/通知/审计
API->>R: 查询设备在线状态
API-->>O: 返回脱敏统一诊断记录
O->>API: 选择修复动作并填写原因、幂等键
API->>DB: 锁定资源并检查当前状态
API->>DB: 写状态/命令/Outbox与审计
API-->>O: 返回最新状态、是否排队和请求编号
```
### 4.2 安全停止流程
系统先锁定目标通话并重新读取权威状态。尚未启动供应商任务时,只执行本地过期收敛;供应商任务可能存在时,事务内把通话置为`STOPPING`并写可靠停止任务;已在停止或最终状态时直接幂等返回。这样可避免运营页面重复点击产生第二个供应商任务。
### 4.3 状态对账流程
只有启动中和停止中状态需要向后台恢复链路排队。任务使用通话内部编号和操作幂等键形成去重边界,Worker继续遵守固定供应商任务编号、未知启动先查询、停止不受启动熔断影响等规则。稳定状态不伪造对账任务。
### 4.4 设备吊销流程
系统先锁定设备,再锁定该设备唯一未结束通话。设备状态、`revoked_at`和版本号在事务内更新;`PREPARING/ROOM_ISSUED`通话直接进入`EXPIRED`且RTC任务进入`COMPLETED`,`STARTING_AGENT/ACTIVE`通话进入`STOPPING`并写入可靠停止任务,已经`STOPPING`则不重复投递。状态历史、吊销审计和停止意图与设备吊销一起提交,避免出现“身份已失效但云端任务继续运行”的分裂状态。
### 4.5 死信恢复与诊断包流程
运营人员先根据稳定错误码修复依赖,再选择死信任务、填写原因并提交。服务端锁定任务,确认租户、`dead`状态和已注册类型后重置待执行字段并记录人工重试元数据。诊断包则复用同一套精确查询和租户边界,把当前脱敏状态序列化为JSON;导出编号、条件、条数和脱敏策略进入审计,但服务端不建立新的内容副本。
### 4.6 联调问题处理流程
联调人员先通过精确诊断检索确认设备、通话和请求关联编号,可从诊断结果一键带入三个编号,再补充问题现象、严重级别和责任方。责任方接手后把状态置为处理中,完成修复后填写根因、修复版本和解决结果并置为待回归;回归人员按可复现步骤验证,登记请求关联号、诊断包编号、重复次数等脱敏证据和结论,最后关闭问题。若回归失败或问题复现,记录回到处理中,原解决结果、回归证据和审计历史继续保留。HIGH或CRITICAL问题在关闭前持续阻断对应发布门禁。服务端通过版本条件更新保证并发修改可见,不以最后写入者静默覆盖前一人的结论。
### 4.7 发布候选技术预检流程
运营人员先冻结候选版本和构建摘要,再按本次联调或发布范围选择真实必需依赖。API完成角色和客户组织校验后,在幂等事务中依次读取迁移、问题、探针、可靠任务和告警状态,计算六项独立门禁及总结果,同时写入最小审计。浏览器下载JSON后交由阶段8证据库归档;任一门禁失败时以`gates`中的稳定代码定位处理项,修复后使用新的幂等键重新生成,不修改旧快照。技术门禁全部通过后仍需继续执行真实设备、供应商、灰度和人工审批清单。
## 5. 数据库设计
诊断模块不建立正文副本或独立影子库,复用以下权威表:
| 表 | 用途 |
|---|---|
| `device`、`device_twin`、`device_presence_state` | 设备身份、吊销时间、固件、期望/已应用配置和在线状态 |
| `conversation`、`conversation_state_history` | 通话权威状态和状态变更依据 |
| `rtc_task`、`provider_event` | RTC房间、短期凭证签发/过期时间、供应商稳定任务编号、尝试次数及通知摘要;V28只新增凭证时间元数据 |
| `device_command` | 配置刷新命令及设备回执 |
| `outbox_event` | 启动/停止对账和可靠停止任务;V26增加公开编号、人工重试次数、时间和操作人 |
| `audit_event` | 操作人、原因、请求编号、资源和结果 |
| `idempotency_record` | 修改动作的重放、并发和正文冲突保护 |
| `integration_issue` | V30联调问题权威记录;V36增加严重级别、根因、修复版本、脱敏回归证据和高严重度汇总索引 |
| `external_dependency_probe_state` | V35平台级探针当前状态、周期、时间、有限错误码和连续失败次数;不保存端点或凭据 |
| `schema_migrations`、`alert_incident` | 验收快照读取迁移脏状态和未解决HIGH/CRITICAL告警;不复制告警正文 |
| `upload_batch`、`media_segment`、`conversation_usage_event`、`conversation_cost_line` | 试点日报读取上传活动/积压与按币种费用,不建立日报副本 |
查询所有连接条件同时包含或继承客户组织边界;HTTP只暴露公开UUID。运营修改在同一数据库事务内完成状态、可靠任务和审计写入,任一步失败则整体回滚。验收快照和试点日/周报复用权威表及`idempotency_record/audit_event`,OpenAPI 0.50.0—0.55.0不新增数据库迁移或快照持久化表。
## 6. 验证
```powershell
pwsh -NoProfile -File .\scripts\smoke-diagnostics.ps1
pwsh -NoProfile -File .\scripts\smoke-acceptance-snapshot.ps1
powershell -ExecutionPolicy Bypass -File .\scripts\smoke-pilot-daily-snapshot.ps1
powershell -ExecutionPolicy Bypass -File .\scripts\smoke-pilot-weekly-snapshot.ps1
```
烟测使用真实本地OIDC令牌和设备HMAC签名,验证五类查询、跨租户403、稳定状态对账、配置刷新、安全停止、死信重试、脱敏诊断包、设备吊销、请求编号回查和精确数据清理。联调问题采用定向数据库与HTTP验证,覆盖创建重放、租户隔离、严重级别阻断、根因/修复版本/回归证据门禁、关闭后汇总恢复和旧ETag冲突。服务集成测试另覆盖活动通话随设备吊销进入`STOPPING`并只投递一个停止任务。生产验收仍需使用真实设备、真实供应商账号和实际值班角色重复执行。
日常页面可用性采用一条轻量真实OIDC冒烟:`test/e2e/visual-ops-sensitive-access-request.mjs`创建临时通话诊断记录、已注册类型的临时死信任务及覆盖五种状态的临时问题记录,依次打开诊断包导出、配置刷新、状态对账、安全停止、设备吊销、死信重试、六种问题流转和敏感访问申请表单,验证目标绑定、字段门禁、ETag展示、危险操作显式确认、取消关闭以及零修改/导出请求。检查要求零横向溢出、零控制台错误和零失败请求,并在`finally`精确清理夹具。该检查不实际下载诊断包、修改问题、重试任务、停止通话或吊销种子设备,服务端副作用仍由上述HTTP与集成测试证明。
外部预检日常验证只运行V35迁移、`internal/externalprobe`定向用例和真实OIDC纵向查询。页面检查使用:
```powershell
$env:PLAYWRIGHT_REQUIRE_FROM='C:\Users\19926\.cache\codex-runtimes\codex-primary-runtime\dependencies\node\node_modules'
$env:CHROME_PATH='C:\Program Files\Google\Chrome\Application\chrome.exe'
node .\scripts\visual-diagnostics-ui.cjs
```
该检查确认本地Keycloak的`api/oidc_jwks`为当前可用、服务归属和检查时间可见、接口无端点或原始错误字段;同时使用真实登录从页面下载实验室验收快照和试点运行日/周报,核对门禁、固定分布、统计守恒、外部投诉证据缺口与归档JSON后精确清理审计/幂等夹具,并保存`test/e2e/screenshots/operations/ops-diagnostics-workspace.png`。本地未配置方舟、火山RTC或托管内容安全时不生成对应成功记录;真实联调或生产快照必须把实际使用的方舟、RTC和`content_safety_tls`列为必需依赖。