## ADDED Requirements ### Requirement: 平台提供只读外部集成调查中心 系统 MUST 为已认证的平台账号提供只读 Integration Log 调查能力,平台账号不应用店铺或企业数据范围过滤,第一阶段不设置细粒度权限码;代理、企业和个人客户 MUST NOT 访问该调查能力。所有成功响应 MUST 使用统一 `{code, msg, data, timestamp}` 格式。 #### Scenario: 平台账号访问调查中心 - **WHEN** 已认证的平台账号查询 Integration Log 总览、列表或详情 - **THEN** 系统返回平台范围内符合查询条件的完整业务摘要,且不应用店铺或企业数据范围过滤 #### Scenario: 非平台主体尝试访问 - **WHEN** 代理、企业或个人客户直接请求平台 Integration Log 调查接口 - **THEN** 系统统一拒绝访问,且不泄露目标记录是否存在 ### Requirement: 外部交互异常总览 系统 MUST 提供受时间范围约束的 Integration Log 总览,至少返回交互总数、结果分布、提供方分布、入站与出站分布、异常数量、结果未知数量、陈旧待处理数量、本地状态变化数量、平均耗时、P95 耗时和按时间分桶的趋势。总览 MUST 区分实际成功、结果未知、明确失败、处理中和未发送终态,不得把 `completed`、`ignored`、`merged`、`rate_limited` 或 `cancelled` 计为外部请求成功。 #### Scenario: 查询指定时间范围总览 - **WHEN** 平台账号提交合法的开始时间、结束时间和时间粒度 - **THEN** 系统在该时间范围内返回固定结构的汇总、分类计数和趋势数据,并为每个稳定结果编码返回中文名称及派生类别 #### Scenario: 区分未发送终态与成功 - **WHEN** 时间范围内同时存在 `success`、`completed`、`merged` 和 `rate_limited` 记录 - **THEN** 系统仅把 `success` 计入实际成功,并把其余记录计入对应的未发送或提前完成分类 ### Requirement: Integration Log 组合筛选列表 系统 MUST 提供按创建时间和主键稳定倒序的分页列表,支持组合筛选 `integration_id`、provider、direction、operation、原始 result 或派生结果类别、external_id、resource_type 与 resource_id、resource_type 与 resource_key、trigger_source、trigger_scene、trigger_series、state_changed、http_status、provider_code、request_id、correlation_id 和创建时间范围。筛选条件 MUST 使用精确匹配或受控枚举,不得提供任意 SQL、任意 JSONPath 或任意 JSONB 字段搜索。 #### Scenario: 组合查询外部失败记录 - **WHEN** 平台账号同时指定 provider、operation、失败派生类别、资源和时间范围 - **THEN** 系统仅返回同时满足全部条件的记录,并为列表项返回稳定编码、中文名称、主要资源、结果、耗时、状态变化、请求标识、关联标识和发生时间 #### Scenario: 查询结果为空 - **WHEN** 合法筛选条件没有命中任何记录 - **THEN** 系统返回成功的空分页结果,而不是资源不存在错误 #### Scenario: 拒绝无界或非法筛选 - **WHEN** 查询时间范围、页码、每页数量或枚举值超过接口约束 - **THEN** 系统返回统一参数错误,且不执行无界全表查询 ### Requirement: 使用稳定 integration_id 查询结构化详情 系统 MUST 使用业务稳定的 `integration_id` 而非数据库自增 ID 定位单条详情,并按 identity、resource、trigger、result、content、linkage、timestamps 和 attempts 分区返回结构化 DTO。详情 MUST 包含可用的请求摘要、响应摘要、metadata、正文安全摘要、HTTP 状态、渠道结果码、可读安全结果摘要、耗时、本地状态变化、人工核对说明、request_id、correlation_id 和关联 Audit Event ID。 #### Scenario: 查询单条外部交互详情 - **WHEN** 平台账号使用存在的 `integration_id` 查询详情 - **THEN** 系统返回该记录的结构化详情及 provider、direction、operation、result 的稳定编码和中文名称,不要求前端解析数据库 Model 或任意 JSON 才能识别基础语义 #### Scenario: 稳定标识不存在 - **WHEN** 平台账号使用不存在的 `integration_id` 查询详情 - **THEN** 系统返回统一资源不存在错误,且不回退使用数据库自增 ID 猜测记录 ### Requirement: Integration Log 查询入参必须有明确来源 总览的时间范围、粒度及聚合筛选和列表的组合筛选 SHALL 来自调查人员输入、通知目标或其他调查视角携带的稳定值;provider、direction、operation、result 等枚举选项 MUST 来自后端稳定常量。详情 `integration_id` MUST 来自 Integration 列表、通知目标或事件/request/correlation 时间线节点,也 MAY 由调查人员粘贴稳定 Integration ID;系统 MUST NOT 使用数据库自增 ID、相似资源或相近时间猜测目标记录。 #### Scenario: 从列表打开详情 - **WHEN** 调查人员选择 Integration 列表中的一条记录 - **THEN** 前端使用列表返回的稳定 `integration_id` 请求详情 - **AND** 后端不要求前端提供数据库主键或重复提供资源条件 #### Scenario: 从其他调查视角跳转 - **WHEN** 事件、request 或 correlation 时间线节点关联一条外部交互 - **THEN** 节点返回可跳转的稳定 `integration_id`,前端据此打开详情 #### Scenario: 查询身份来自认证上下文 - **WHEN** 调用方请求 Integration Log 总览、列表或详情 - **THEN** Handler 从认证上下文判断其是否为平台身份 - **AND** 不接受 query、path 或 body 传入平台、店铺或企业范围 #### Scenario: 从通知目标打开 Integration 详情 - **WHEN** `GET /api/admin/notifications/{id}/target` 返回 `available=true`、`target_type=integration_log` 和非空 `target_key` - **THEN** 前端将 `target_key` 原样作为 `integration_id` 调用 Integration 详情 - **AND** 不直接解析通知列表的 `ref_type/ref_id/ref_key` 或按资源与时间重新搜索 #### Scenario: 通知目标不可用 - **WHEN** 通知目标解析返回 `available=false` 或缺少 `target_key` - **THEN** 前端只展示通知正文,不显示 Integration 详情跳转 ### Requirement: 尝试序列只使用显式技术序列 系统 MUST 仅在记录具有相同非空 `trigger_series` 时将其组织为同一技术尝试序列,并按 attempt、发生时间和主键稳定排序。可重试或分阶段外呼的新接入 MUST 写入稳定 `trigger_series` 和单调递增的 attempt;缺少 `trigger_series` 的历史记录 MUST 作为单次交互展示,系统不得仅因 provider、operation、资源或时间接近而猜测其属于同一重试序列。 #### Scenario: 展示显式尝试序列 - **WHEN** 用户查询的记录带有 `trigger_series`,且存在同序列的多个尝试 - **THEN** 详情按 attempt 和时间返回完整尝试序列,并逐条保留是否发送、结果、耗时和状态变化 #### Scenario: 历史记录缺少序列标识 - **WHEN** 用户查询的历史记录没有 `trigger_series` - **THEN** 系统只返回当前单次交互,不把相同资源或相同 operation 的相邻记录拼成重试序列 ### Requirement: 业务关联链路与技术重试保持不同语义 系统 MUST 将 `correlation_id` 解释为跨请求、异步任务、外部交互和业务事实的业务链路标识,将 `trigger_series + attempt` 解释为一次外部操作的技术尝试序列。按 correlation_id 查询时 MUST 返回相关外部交互节点,但 MUST NOT 将这些节点统一标记为重试;跨 Audit Event、Outbox、Asynq 和 Domain Ledger 的完整时间线由统一审计调查 Query 组合,Integration Log Query 只提供外部交互节点。 #### Scenario: 同一业务链路包含不同外部操作 - **WHEN** 同一 correlation_id 下存在预下单、回调和查单等不同 operation - **THEN** 系统按业务发生时间展示相关交互并保留各自 operation,不把回调或查单描述为预下单重试 #### Scenario: 同时存在业务链路和尝试序列 - **WHEN** 一条业务链路中的某个 operation 具有多个显式 trigger_series 尝试 - **THEN** 系统既保留 correlation_id 的业务链路关系,也在该 operation 内单独展示技术尝试序列 ### Requirement: 调查中心不承担恢复和导出 Integration Log 调查接口 MUST 只有读取能力。第一阶段 MUST NOT 提供重试、补偿、结果确认、外部单号绑定、人工恢复、状态修改、记录删除或导出接口。`recovery_strategy` 仅作为人工核对说明展示,不得被解释为可执行命令。 #### Scenario: 查看结果未知记录 - **WHEN** 平台账号查看 result 为 `unknown` 的详情 - **THEN** 系统展示已记录的人工核对说明,但响应中不包含恢复动作地址、可执行命令或写操作按钮契约 #### Scenario: 尝试调用恢复或导出能力 - **WHEN** 调用方尝试通过审计中心执行 Integration Log 恢复、修改、删除或导出 - **THEN** 系统不存在对应业务路由或统一拒绝请求,且原 Integration Log 不发生变化 ### Requirement: 安全凭据不得进入或离开 Integration Log 平台可以查看 Integration Log 中已经安全筛选的完整业务摘要,但密码、操作密码、验证码、Access Token、Refresh Token、Secret、私钥、回调 Token、EncodingAESKey、Authorization、Cookie、签名 URL、支付密钥、完整加密回调正文和其他系统安全凭据 MUST 在写入前删除,查询接口 MUST NOT 返回这些内容。原始第三方错误正文 MUST NOT 直接进入 provider_message;调用方必须提供有界、可读且不含凭据的业务结果摘要。 #### Scenario: 请求摘要包含业务字段和安全凭据 - **WHEN** 外部调用摘要同时包含普通业务字段和系统安全凭据 - **THEN** 系统保留普通业务字段并在持久化前删除安全凭据,详情接口只返回持久化后的安全摘要 #### Scenario: 入站回调包含完整正文 - **WHEN** 系统接收运营商、支付或企业微信回调 - **THEN** Integration Log 只保存受控业务摘要、正文大小和安全 hash,不通过调查接口返回完整原始或解密正文 ### Requirement: 历史 Integration Log 缺口必须显式兼容 系统 MUST 保留并查询现有 Integration Log,不在线伪造回填缺失的 trigger_series、correlation_id、资源 ID 或可读 provider_message。查询 DTO MUST 对无法可靠解析的历史字段返回明确的数据完整性标识;对使用现有确定性 ICCID hash 保存 resource_key 的运营商历史记录,资源查询 MUST 支持按相同受控算法定位,但 MUST NOT 向调用方暴露 hash 作为业务标识。已保存为不可逆摘要的 provider_message MUST 原样标识为历史摘要,不得生成虚假的可读原文。历史 `request_summary/response_summary/metadata` MUST 在响应前按字段白名单和统一凭据删除规则再次清理,MUST NOT 直接透传旧 JSON。 #### Scenario: 查询只有 ICCID hash 的历史运营商记录 - **WHEN** 平台账号使用合法 ICCID 查询资源外部交互轨迹,且历史记录只保存确定性 ICCID hash - **THEN** 系统使用受控兼容规则命中该记录,并把资源解析状态标记为历史兼容,不把 hash 返回为 ICCID #### Scenario: 历史链路字段缺失 - **WHEN** 历史记录缺少 correlation_id、trigger_series 或可靠资源 ID - **THEN** 系统仍返回现有事实并标明对应关联能力受限,不猜测链路、重试次数或资源关系 #### Scenario: 历史 JSON 含有未覆盖的安全凭据 - **WHEN** 历史 Integration Log 摘要含有 Token、Secret、Authorization、Cookie、签名 URL 或其他禁止字段 - **THEN** Query 在返回前删除禁止字段并保留其余完整业务摘要 - **AND** 不直接序列化历史 JSON 到响应 ### Requirement: Integration Log 查询必须分页并命中受控索引 列表 MUST 默认每页 20 条、最大 100 条,并要求受控时间范围;总览 MUST 限定时间范围和固定聚合维度。实现 MUST 使用创建时间、结果、provider、external_id、资源、trigger_series、request_id、correlation_id 和 audit_event_id 的受控索引完成主要查询,不得逐条补查产生 N+1,不得为第一阶段的摘要展示增加任意 JSONB GIN 搜索。 #### Scenario: 查询高频轮询历史 - **WHEN** 平台账号查询包含大量 Gateway 高频记录的合法时间窗口 - **THEN** 系统使用分页和匹配的时间或筛选索引返回结果,单页查询不加载窗口外全部记录,也不逐条查询关联基础信息 #### Scenario: 深分页或超大时间窗口 - **WHEN** 调用方请求超过允许范围的页码、每页数量或时间窗口 - **THEN** 系统返回统一参数错误,引导调用方缩小时间窗口,而不是执行高成本扫描 ### Requirement: Integration 调查只覆盖在线留存窗口 Integration overview、列表、详情和关联时间线 MUST 仅查询 PostgreSQL 在线数据,列表和总览 DTO MUST 返回 `retention{online_from, archived_before, timezone}`。月初清理后的上月记录只保留在对象存储,第一阶段 MUST NOT 提供归档下载、对象存储扫描、冷热联合查询或恢复能力。显式时间范围早于或跨越 `online_from` 时 MUST 返回稳定的已归档错误,不能返回成功空结果或部分结果。 #### Scenario: 查询已归档的外部交互月份 - **WHEN** 平台账号查询的 Integration 时间范围全部早于 `online_from` - **THEN** 系统返回数据已归档和当前在线窗口 - **AND** 不执行 PostgreSQL 无效扫描或对象存储查询 #### Scenario: 当前月份外部交互调查 - **WHEN** 查询范围完全位于在线窗口 - **THEN** 系统返回符合条件的 Integration Log 与 retention 元数据 - **AND** 详情和尝试序列仍遵循现有稳定 ID 与显式 `trigger_series` 契约