## MODIFIED Requirements ### Requirement: 审计时间线 系统 SHALL 支持按事件、操作者、资源、请求、关联标识和资金维度查询已记录的审计事实。新建的 `tb_integration_log` 在已取得稳定审计事件时 SHALL 写入其内部 ID 作为 `audit_event_id`;没有上游审计事件时 `audit_event_id` SHALL 为空,系统 MUST NOT 为补齐关联而另写审计事件,并且该日志的非空 `integration_id` SHALL 出现在对应事件列表和事件详情的 `investigation_refs.integration_refs` 中;关联生成失败时系统 MUST 保留 Integration Log 并记录可排查告警,且 MUST NOT 按名称、时间或摘要推断关联。历史 Integration Log 不在本要求的回填范围内。审计事件的构造、校验或持久化失败 MUST 记录可关联的结构化错误日志和二次失败记录,且 MUST NOT 改变已通过业务校验的业务操作结果或接口响应。尚无完成物理清理记录时,审计调查接口 MUST 继续查询在线审计事实,且返回的留存边界不得将数据标记为已归档。 #### Scenario: 审计时间线 - **GIVEN** 审计事实已存在 - **WHEN** 使用对应维度查询 - **THEN** 返回匹配的事实与稳定动作编码,不用访问日志替代 #### Scenario: 事件返回已关联外部交互引用 - **GIVEN** 一个在线审计事件有 `tb_integration_log.audit_event_id` 指向其内部 ID 的外部交互记录 - **WHEN** 查询全局审计事件列表或该事件详情 - **THEN** 该事件的 `investigation_refs.integration_refs` 返回该记录的 `integration_id` #### Scenario: 事件没有关联外部交互引用 - **GIVEN** 一个在线审计事件没有稳定关联的外部交互记录 - **WHEN** 查询全局审计事件列表或该事件详情 - **THEN** `investigation_refs.integration_refs` 返回空数组 #### Scenario: 新外部交互日志具有稳定审计关联 - **GIVEN** 系统即将记录一次新的外部调用、入站回调或未发送裁决,且该调用由一个已写入的审计事件发起 - **WHEN** 写入对应 Integration Log - **THEN** 该记录保存非空 `audit_event_id`,且其目标 Audit Event 已存在 #### Scenario: 没有上游审计事件的外部交互日志 - **GIVEN** 一次系统任务发起的外部调用没有上游审计事件 - **WHEN** 写入对应 Integration Log - **THEN** 该记录的 `audit_event_id` 为空 - **AND** 审计事件表不新增 `integration.attempt_started` 事件 #### Scenario: 缺少审计关联时保留外部交互日志 - **GIVEN** 一次新的外部交互日志没有稳定审计事件关联 - **WHEN** 系统尝试写入该 Integration Log - **THEN** 系统持久化该 Integration Log、记录可排查告警,并返回空 `integration_refs` #### Scenario: 审计写入失败不阻断业务 - **GIVEN** 一个业务操作已通过自身输入、权限和状态校验 - **WHEN** 该操作的审计事件构造、校验或持久化失败 - **THEN** 系统提交或返回该业务操作原本的结果,并以请求关联标识、动作编码和资源标识记录审计失败 #### Scenario: 尚无物理清理记录时查询在线审计 - **GIVEN** 审计或外部交互日志尚无完成物理清理的归档运行记录 - **WHEN** 调用任一依赖留存边界的审计调查接口 - **THEN** 系统返回当前在线数据或空列表 - **AND** 响应中的 `archived_before` 为空 - **AND** 系统不得因空留存边界返回服务端错误 ### Requirement: 轮询审计事实保留边界 系统 SHALL 仅为人工触发的轮询操作,以及由轮询结果导致的资金或套餐生命周期变化(如套餐到期、激活、退款失效)持久化统一审计事件及资源快照。系统任务发起的轮询同步本身(流量同步、网络状态同步、流量扣减、停复机原因更新)MUST NOT 创建 `tb_audit_event` 或 `tb_audit_event_resource`,无论成功、失败或结果未知;其可查证据由采集记录、采集失败记录、停复机记录、日流量记录与应用日志提供。审计调查接口仅返回实际已保留的历史事实。 #### Scenario: 自动轮询成功但无业务变化 - **GIVEN** 一次自动轮询成功且没有改变任何业务状态、有效流量或风险结论 - **WHEN** 系统结束该轮询处理 - **THEN** 审计事件和审计资源表不新增该次轮询记录 #### Scenario: 自动轮询同步流量并扣减 - **GIVEN** 一次自动轮询读到有效增量并完成扣减 - **WHEN** 系统提交该变化 - **THEN** 审计事件表不新增流量同步或扣减事件 - **AND** 采集记录保存该次读数、扣减判定、增量与原始响应 #### Scenario: 自动轮询调用失败 - **WHEN** 一次自动轮询查流量超时或返回无效响应 - **THEN** 审计事件表不新增记录 - **AND** 采集失败记录保存该次失败的错误类别、上游返回与原始数据 #### Scenario: 轮询导致套餐到期 - **GIVEN** 轮询或定时任务判定一个套餐已到期 - **WHEN** 系统把套餐置为已过期 - **THEN** 系统保留套餐到期的审计事实与资源快照 #### Scenario: 人工触发刷新 - **WHEN** 后台账号手动刷新一张卡 - **THEN** 系统为该操作写审计事件 ## ADDED Requirements ### Requirement: 审计动作落库策略 系统 SHALL 在统一审计注册表中为每个动作声明落库策略,取值为:总是写(`always`)、只写成功(`on_success`)、只写结论(`outcome`)、只写应用日志(`log_only`)、不写(`none`)。 - 操作者为后台账号、个人客户、OpenAPI 或外部系统时,系统 MUST 按"总是写"处理,不受动作策略限制。 - 操作者为系统任务或定时任务、且动作未声明策略时,系统 MUST 按"只写应用日志"处理。 - 审计覆盖扫描 MUST 在存在可由系统任务或定时任务发起、但未声明落库策略的动作时失败。 - 系统 SHALL 提供按动作编码覆盖落库策略的运行时配置;覆盖只作用于系统任务与定时任务操作者。 - 是否写入审计 MUST NOT 改变业务操作的结果或接口响应。 #### Scenario: 人工操作始终写审计 - **GIVEN** 动作 `order.create` 对系统任务声明为只写应用日志 - **WHEN** 后台账号创建订单 - **THEN** 系统写入审计事件与资源快照 #### Scenario: 新增机器动作未声明策略 - **WHEN** 系统任务写一个未声明落库策略的动作 - **THEN** 审计事件表不新增记录,应用日志新增一行该动作的结构化日志 #### Scenario: 覆盖扫描发现未声明策略 - **GIVEN** 一个可由系统任务发起的动作没有声明落库策略 - **WHEN** 运行审计覆盖扫描 - **THEN** 扫描失败并列出该动作编码 #### Scenario: 运行时覆盖 - **GIVEN** 运行时配置把 `package_usage.expire` 覆盖为只写应用日志 - **WHEN** 定时任务使一个套餐到期 - **THEN** 审计事件表不新增记录,应用日志新增一行 - **AND** 删除该覆盖后,下一次到期重新写审计事件 ### Requirement: 系统过程动作写应用日志 落库策略为"只写应用日志",或"只写成功"时的失败结果,系统 SHALL 在所在服务的应用日志中写一行结构化日志,并 MUST NOT 写入审计事件表与资源表。 - 日志行 MUST 带固定 logger 名 `audit`。 - 日志行 MUST 包含:动作编码、操作者类型与标识、来源、结果、资源类型与资源键、关联标识、请求标识、错误码、摘要。 - 日志行 MUST NOT 包含变更前后快照、主体数据、密钥、令牌或加密报文。 - 成功写 Info 级别,失败写 Warn 级别。 - 应用日志写入失败 MUST NOT 重试,也 MUST NOT 影响业务结果。 #### Scenario: 网络状态同步 - **WHEN** 系统任务同步一张卡的网络状态 - **THEN** 应用日志新增一行 `logger=audit`、动作编码为 `iot_card.worker_network_sync` 的记录 - **AND** 该行不含变更前后快照 #### Scenario: 不新增审计表数据 - **WHEN** 系统任务写任一只写应用日志的动作 - **THEN** `tb_audit_event` 与 `tb_audit_event_resource` 均无新增 ### Requirement: 自动重试动作只写结论 落库策略为"只写结论"的动作,系统 SHALL 只在最终结论产生时写一条审计事件。最终结论指最终成功,或重试用尽转人工处理。 - 中间失败 MUST NOT 写审计事件,只写应用日志;日志行 MUST 带尝试序号与对应尝试记录标识。 - 结论事件的元数据 MUST 包含总尝试次数、首次失败时间与最后错误码。 - 同一业务对象的同一结论 MUST 只有一条审计事件。 - 每一次尝试的细节 MUST 由对应业务尝试记录保存,并可分页查询。 #### Scenario: 重试后成功 - **GIVEN** 一张退款单的资产后处理前两次失败、第三次成功 - **WHEN** 第三次处理完成 - **THEN** 审计事件表只有一条结论为成功的事件,元数据中总尝试次数为 3 - **AND** 尝试记录中有三条记录 #### Scenario: 重试用尽转人工 - **GIVEN** 一张退款单的资产后处理连续失败达到上限 - **WHEN** 系统将其标记为需人工处理 - **THEN** 审计事件表写入一条结论为失败的事件,包含最后错误码 #### Scenario: 结论重复写入 - **WHEN** 同一退款单的成功结论被重复提交 - **THEN** 审计事件表仍只有一条该结论事件