Files
story/docs/integration-diagnostics.md

211 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 集成诊断工作台开发说明
> 对应接口契约: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`列为必需依赖。