## ADDED Requirements ### Requirement: 代理和企业必须使用独立的安全资源活动接口 系统 SHALL 为代理和企业提供独立于平台内部审计中心的只读资源活动接口:代理使用 `GET /api/admin/agent/resource-activities/{resource_type}/{identifier}`,企业使用 `GET /api/admin/enterprise/resource-activities/{resource_type}/{identifier}`。两者 MUST 使用 `page/page_size` 和专用安全投影 DTO;响应资源摘要 MUST 包含 `resource_type/resource_id/resource_key/display_name`,活动项 MUST 仅包含外部动作编码/中文名、`subject_summary`、Registry 白名单约束的 `subject_data`、结果、发生时间及允许公开的关联资源摘要。接口响应 SHALL 使用统一的 `{code, msg, data, timestamp}` 格式,且不得通过响应字段、路径命名或错误差异暴露平台内部审计实现。 #### Scenario: 代理查询有权管理的资源活动 - **WHEN** 代理账号查询自己店铺或下级店铺范围内的卡、设备或其他已支持资源 - **THEN** 系统返回该资源允许代理感知的分页活动时间线 - **AND** 默认每页 20 条且每页最多 100 条 #### Scenario: 企业查询已授权资源活动 - **WHEN** 企业账号查询当前企业仍然有效授权的卡或设备 - **THEN** 系统返回该资源允许企业感知的分页活动时间线 #### Scenario: 外部主体无法访问平台审计中心 - **WHEN** 代理或企业账号直接调用平台内部审计接口 - **THEN** 系统统一返回无权限错误 - **AND** 不返回任何平台审计数据 ### Requirement: 资源归属必须在后端按真实业务关系校验 系统 MUST 在查询活动事件之前验证目标资源属于当前调用方的数据范围,不得仅依赖前端页面可见性,也不得把缺失的店铺范围解释为不受限制。 #### Scenario: 资源标识来自受权业务入口 - **WHEN** 代理或企业从当前卡、设备等业务列表或详情页打开“活动记录” - **THEN** 前端使用该页面已有的 `resource_type/identifier` 请求资源活动接口 - **AND** `identifier` 使用 Resource Registry 为该类型声明的业务稳定标识,不由前端猜测内部资源 ID #### Scenario: 代理从卡资产详情打开活动记录 - **WHEN** 代理调用 `GET /api/admin/assets/resolve/{identifier}` 并得到 `asset_type=card` 与非空 `iccid` - **THEN** 页面调用 `GET /api/admin/agent/resource-activities/iot_card/{iccid}` - **AND** 店铺范围由认证上下文提供,不把响应中的 `shop_id` 作为授权凭证传入 #### Scenario: 代理从设备资产详情打开活动记录 - **WHEN** 代理调用资产详情并得到 `asset_type=device` 与非空 `virtual_no` - **THEN** 页面调用 `GET /api/admin/agent/resource-activities/device/{virtual_no}` #### Scenario: 企业从受权资产列表打开活动记录 - **WHEN** 企业从 `GET /api/admin/enterprises/{id}/cards` 的 `items[].iccid` 或 `GET /api/admin/enterprises/{id}/devices` 的 `items[].virtual_no` 打开活动记录 - **THEN** 页面分别调用 `GET /api/admin/enterprise/resource-activities/iot_card/{iccid}` 或 `GET /api/admin/enterprise/resource-activities/device/{virtual_no}` - **AND** 后端忽略路由来源中的企业 ID 作为授权证明,始终以认证上下文企业 ID 复核当前有效授权 #### Scenario: 企业不能从平台资产详情进入 - **WHEN** 当前统一资产 resolve 接口禁止企业账号调用 - **THEN** 企业前端不展示依赖该接口的活动入口,也不尝试使用平台资源时间线替代 #### Scenario: 身份范围来自认证上下文 - **WHEN** 代理或企业请求资源活动接口 - **THEN** Handler 从认证上下文取得当前账号、代理店铺范围或企业 ID - **AND** 不接受 query、path 或 body 传入 shop ID、enterprise ID 或其他主体范围 #### Scenario: 代理资源范围按店铺层级校验 - **WHEN** 代理账号查询资源活动 - **THEN** 系统验证资源当前归属店铺位于该代理自己或下级店铺范围内 #### Scenario: 企业卡归属按有效授权校验 - **WHEN** 企业账号查询某张卡的资源活动 - **THEN** 系统验证企业卡授权记录的 `enterprise_id` 等于当前企业且授权未撤销 #### Scenario: 企业设备归属按有效授权校验 - **WHEN** 企业账号查询某台设备的资源活动 - **THEN** 系统验证企业设备授权记录的 `enterprise_id` 等于当前企业且授权未撤销 #### Scenario: 无权资源不泄露存在性 - **WHEN** 代理或企业查询不存在、不支持或不属于自身范围的资源 - **THEN** 系统返回统一的“无权限操作该资源或资源不存在”错误 - **AND** 响应不得表明资源是否真实存在、是否曾被授权或是否存在内部审计事件 #### Scenario: 活动入口缺少业务标识 - **WHEN** 卡没有 ICCID、设备没有 VirtualNo 或响应没有 Registry 声明的稳定 identifier - **THEN** 页面隐藏活动入口,不使用内部 ID、名称或其他字段猜测 identifier ### Requirement: 外部活动投影必须按事件可见级别过滤 系统 SHALL 根据 Audit Event 写入时确定的 `internal_only`、`subject_result`、`subject_detail` 可见级别生成外部活动投影,不得在查询时把完整内部事件直接序列化后临时删除部分字段。 #### Scenario: 内部事件完全隐藏 - **WHEN** 资源关联事件的可见级别为 `internal_only` - **THEN** 代理和企业的活动时间线不返回该事件 - **AND** 不以占位记录或数量差异提示该事件存在 #### Scenario: 平台操作仅显示安全结论 - **WHEN** 平台内部操作影响代理或企业资源且可见级别为 `subject_result` - **THEN** 活动时间线只返回稳定动作名称、安全业务结论、结果和发生时间 - **AND** 不表明该结论是否由平台人工操作、系统任务或内部补偿产生 #### Scenario: 主体自身操作显示允许的业务详情 - **WHEN** 代理或企业自身操作产生 `subject_detail` 事件 - **THEN** 活动时间线返回写入时按 Registry 白名单生成的 `subject_data`、允许公开的关联资源标识和处理结果 - **AND** 仍不返回平台内部字段 #### Scenario: 查询不得从内部字段临时生成主体详情 - **WHEN** 事件没有持久化 `subject_data` - **THEN** 资源活动 Query 只返回已有安全结论,不读取内部 before/after 后删字段生成详情 ### Requirement: 安全资源活动不得泄露内部调查数据 系统 MUST 从代理和企业活动响应中排除平台内部操作者、内部原因、内部备注、风险判断、内部前后数据、Audit Event ID、Integration Log 请求响应及系统安全凭据。 #### Scenario: 平台退款处理对外只展示结果 - **WHEN** 平台完成一笔影响代理资金的退款处理 - **THEN** 代理活动时间线可显示“退款资金已处理”等安全结论及最终结果 - **AND** 不返回平台操作人、人工处理方式、内部备注、风控原因或外部支付响应 #### Scenario: 外部系统交互不直接展示 - **WHEN** 某资源经历外部请求、重试、回调或查询确认 - **THEN** 代理和企业活动时间线只展示允许感知的最终业务结论 - **AND** 不返回第三方错误码、请求摘要、响应摘要、调用次数或 Integration Log 标识 ### Requirement: 多资源事件必须进入每个有权资源的活动时间线 系统 SHALL 根据 Audit Event Resource 关联从不同资源入口投影同一业务操作,并 MUST 对每个入口分别执行资源归属和可见性校验。 #### Scenario: 设备换卡在相关资源时间线出现 - **WHEN** 一次换卡事件关联设备、旧卡和新卡且这些资源均属于当前主体范围 - **THEN** 系统在设备、旧卡和新卡的活动时间线中展示相应的安全业务结论 #### Scenario: 部分关联资源不属于当前主体 - **WHEN** 一个事件同时关联当前主体资源和其他主体或平台资源 - **THEN** 系统只返回当前主体有权查看资源对应的安全投影 - **AND** 不返回其他关联资源的内部标识、数量或详情 ### Requirement: 主体资源活动只展示在线留存窗口 代理和企业资源活动 DTO MUST 返回 `retention{online_from, archived_before, timezone}`,并 MUST 只查询 PostgreSQL 在线 Audit Event。月初清理后的历史不从对象存储返回;显式时间范围早于或跨越在线边界时 MUST 返回稳定的已归档错误,同时仍不得泄露平台内部事件或归档对象信息。 #### Scenario: 代理查看已归档月份的资源活动 - **WHEN** 代理对自身资源提交的显式时间范围早于 `online_from` - **THEN** 系统返回统一的已归档提示和当前在线边界 - **AND** 不返回冷归档对象 Key、平台事件数量或任何内部调查字段