feat: 审计链路
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Failing after 59s

This commit is contained in:
luo
2026-08-07 18:33:37 +08:00
parent fa3d7088f9
commit b8b2854aa6
54 changed files with 5448 additions and 12 deletions

View File

@@ -0,0 +1,53 @@
## Context
本次变更横跨平台、代理和企业三类主体以及资产、订单、退款、钱包、通知等多个业务页面。OpenAPI 将关联关系明确建模为 `investigation_refs``linkage` 和稳定资源引用,并区分平台内部 ID 与代理/企业可见的业务标识。前端必须保持这些边界,不能为了补齐跳转而从名称、时间或摘要推断关联关系。
仓库目前没有已归档的基线 specs但存在通知中心和旧资产操作日志的活跃变更。本提案建立独立的审计链路能力通过明确的依赖点与它们集成不复制或覆盖其已有要求。
## Goals / Non-Goals
- Goals: 按 OpenAPI 实现 16 个只读接口的类型、查询、页面和跨视角跳转契约。
- Goals: 为平台、代理和企业提供符合各自权限与数据投影的业务审计入口。
- Goals: 统一分页、RFC3339 时间范围、枚举展示、在线保留窗口和错误处理。
- Non-Goals: 不修改后端接口、数据库、日志采集或保留策略。
- Non-Goals: 不提供恢复、重试、修改、删除、处置、封禁、任意全文搜索或导出。
- Non-Goals: 不替换现有资产操作日志组件,也不修改 C/H5 端。
## Decisions
- Decision: 新增独立审计 API 模块和共享类型按平台审计、主体活动、资金、风险、Integration、链路时间线分组暴露查询函数统一保留服务端响应包装和 nullable 字段。
- Decision: 平台业务入口使用响应中的内部稳定 ID。卡、设备、店铺、企业、订单和退款分别使用对应 `resource_type``response.data.id`;钱包通过资金时间线的 `wallet_id` 进入。
- Decision: 代理与企业入口使用响应中的稳定业务标识。卡使用 `iccid`,设备使用 `virtual_no`;代理的分配、换货、店铺和企业使用接口文档指定的业务编号。缺少稳定标识时隐藏入口。
- Decision: 前端只转发后端返回的调查引用。事件的 `event_id``actor_ref``resource_refs``request_id``correlation_id``integration_refs`,以及 Integration 的 `linkage`/`resource` 是跨视角导航的唯一数据来源。
- Decision: `request_id` 可以来自调查引用、Integration、或用户从 Access Log 明确粘贴;`correlation_id``integration_id` 只能来自接口返回的稳定字段。前端不使用 UUID 或其他方式生成这些 ID。
- Decision: 对不存在、为空或 `fidelity=false` 的引用隐藏对应跳转,禁止按名称、时间、摘要或相邻记录猜测缺失关系。
- Decision: 资源搜索严格使用 `resource_type + keyword` 精确查询,并把选中项的 `resource_type/resource_id` 原样传给资源时间线。`historical=true` 只作为历史快照命中提示。
- Decision: 列表和时间线统一消费 `page``page_size``total` 并保留 `retention` 语义。时间筛选发送含时区的 RFC3339 值,`created_to` 按接口定义为不包含该时刻;页面不展示全局在线窗口提示,也不提供归档查询假入口。
- Decision: 所有枚举筛选提交稳定 code并优先展示后端 `*_name``action`、Integration `operation` 等开放编码必须直接来自响应,不能从中文文案反推。
- Decision: 资金金额按分展示转换,权威性只认 `amount_authority.authoritative=true` 指向的业务字段;前端不得合并冲突金额或自行计算余额。
- Decision: 代理与企业活动共用展示外壳但使用独立 API。企业页面只展示主体安全投影不引入平台操作者、风险、内部原因或 before/after 字段。
- Decision: 通知目标仅在 `available=true``target_type=integration_log``target_key` 非空时跳转,并将 `target_key` 原样作为 Integration 详情的 `integration_id`。其他情况保留通知正文并提示目标不可用。
- Decision: 权限以最终菜单/按钮权限编码和服务端鉴权共同控制。平台、代理、企业不展示不属于其主体的审计入口,`401/403` 继续走共享认证与无权处理。
## Risks / Trade-offs
- 最终菜单与按钮权限编码尚未体现在 OpenAPI 中;实施前必须确认,否则只能依赖服务端 `403`,无法做到准确的入口隐藏。
- 审计事件和 Integration 模型字段较多;通过共享详情/时间线组件和严格类型避免各业务页重复实现,但不抽象为可以执行任意端点的动态页面。
- 在线保留窗口外的数据不可由这些接口查询;空状态使用“当前查询无记录”的中性表达,避免将空数据误报为无历史事件。
- 活跃通知中心变更也会修改通知导航;实施时需在其最新状态上追加 `integration_log` 规则,避免覆盖既有白名单映射。
- 旧资产操作日志与新审计中心含义相邻但数据源不同;两个入口并存并清晰命名,避免误将旧日志能力删除。
## Migration Plan
1. 确认平台、代理、企业的菜单和按钮权限编码,以及审计中心路由归属。
2. 建立 OpenAPI 对齐的类型、API 模块和共享只读展示组件。
3. 按平台审计、资金、风险、Integration、主体活动顺序接入页面。
4. 在业务页面与通知导航中增加稳定引用入口。
5. 完成角色、数据隔离、空引用、保留窗口、错误和跨视角跳转验收。
6. 若上线异常,隐藏新增路由与入口;现有业务接口和旧操作日志不受影响。
## Open Questions
- 平台审计中心、风险中心、Integration、代理资源活动和企业资源活动的最终菜单及按钮权限编码分别是什么
- 审计中心是作为一级菜单,还是归入现有系统管理/运维菜单?
- OpenAPI 未包含 `GET /api/admin/notifications/{id}/target` 的完整响应 schema是否继续以活跃通知中心变更定义的 `available/target_type/target_id/target_key` 为最终契约?

View File

@@ -0,0 +1,38 @@
# Change: 新增审计链路前端接入能力
## Why
八月迭代新增了审计事件、请求与业务链路、资金调查、风险调查、外部集成交互以及代理/企业资源活动共 16 个只读接口。当前前端尚无统一的审计调查页面、接口类型和业务入口,无法可靠消费后端返回的稳定调查引用,也容易错误地由名称、时间或页面数据自行拼接链路标识。
## What Changes
- 新增平台审计事件列表、详情、操作者时间线、资源精确搜索和资源时间线能力。
- 新增请求链路与业务关联链路时间线,并只使用接口返回或用户从 Access Log 粘贴的稳定 `request_id``correlation_id`
- 新增资金调查时间线、风险总览与明细、Integration 总览/列表/详情页面。
- 新增代理和企业资源活动入口,并按主体支持范围使用稳定业务标识和后端安全投影。
- 在卡、设备、店铺、企业、订单、退款和钱包相关列表/详情页增加角色匹配的「审计记录」入口。
- 接入通知目标到 Integration 详情的受控跳转:仅接受可用的 `integration_log` 目标。
- 统一使用后端返回的名称、状态、保留窗口语义和调查引用;禁止前端猜测或生成 `request_id``correlation_id``integration_id`
- 所有新增能力均为只读调查能力,不增加恢复、修改、删除、处置、封禁或导出操作。
## Impact
- Affected specs: `audit-chain-frontend-integration`
- Affected code:
- `src/api/modules/` 下新增审计接口模块
- `src/types/api/` 下新增审计接口类型
- `src/views/` 下审计中心、风险中心、Integration 和时间线页面
- 卡、设备、店铺、企业、订单、退款、钱包相关列表/详情页
- 路由、菜单、权限控制及通知目标导航
- API contracts:
- `GET /api/admin/audit/*`
- `GET /api/admin/agent/resource-activities/{resource_type}/{identifier}`
- `GET /api/admin/enterprise/resource-activities/{resource_type}/{identifier}`
- `GET /api/admin/notifications/{id}/target`
- Dependencies:
- 与活跃变更 `update-admin-notification-center-api` 的受控通知目标协议保持一致
- 与活跃变更 `update-admin-asset-device-signal-and-audit-logs` 的旧资产操作日志展示并存;本变更不替换旧日志接口
- Source of truth:
- `docs/产品迭代8月份/审计链路接口变更整理文档.md`
- `docs/产品迭代8月份/默认模块.openapi.json`
- Breaking changes: 无;现有业务接口路径保持不变

View File

@@ -0,0 +1,207 @@
## ADDED Requirements
### Requirement: Platform Audit Event Investigation
The admin frontend SHALL provide platform audit event list and detail views using `GET /api/admin/audit/events` and `GET /api/admin/audit/events/{event_id}`. It SHALL preserve the documented event, actor, scope, resource, result, risk, request and investigation-reference fields, use stable codes for filters, and prefer backend display names.
#### Scenario: Filter and inspect audit events
- **WHEN** an authorized platform user filters audit events by documented time, action, category, actor, source, result, risk, scope, resource or linkage parameters
- **THEN** the frontend MUST send the documented parameter names and stable code values
- **AND** it MUST render the paginated `items`, `total`, `page`, `page_size` and `retention` response
#### Scenario: Open an event detail
- **WHEN** the user opens an event returned by the list or another investigation reference
- **THEN** the frontend MUST pass its `event_id` unchanged to the detail endpoint
- **AND** it MUST display backend snapshots and names without replacing historical values with current account or resource names
### Requirement: Stable Investigation Reference Navigation
The frontend SHALL navigate between audit views only through stable identifiers returned by the APIs. `investigation_refs.actor_ref`, `resource_refs`, `request_id`, `correlation_id`, `integration_refs`, Integration `linkage`, and stable Integration `resource` references SHALL be the authoritative navigation sources.
#### Scenario: Navigate through a returned reference
- **WHEN** an audit or Integration response contains a non-empty supported investigation reference
- **THEN** the frontend MUST pass the returned identifier and type unchanged to the corresponding actor, resource, request, correlation or Integration view
#### Scenario: A reference is unavailable or unreliable
- **WHEN** the required reference is empty, absent, unsupported, or its linkage `fidelity` indicates that a relationship is unavailable
- **THEN** the frontend MUST hide or disable the related navigation action
- **AND** it MUST NOT infer a relationship from names, timestamps, summaries, adjacent rows or resource text
#### Scenario: Link identifiers are not generated by the frontend
- **WHEN** the frontend needs a `correlation_id` or `integration_id`
- **THEN** it MUST use a value returned by an API
- **AND** it MUST NOT generate, concatenate or guess the identifier
### Requirement: Actor, Resource, and Link Timelines
The admin frontend SHALL provide actor-event, resource-event, request-link and correlation-link timelines through the documented endpoints. It SHALL support an explicitly pasted Access Log `request_id`, while all other navigation values SHALL come from returned stable references.
#### Scenario: Inspect actor behavior
- **WHEN** a user follows `actor_ref.kind/id` or opens an account by its stable ID
- **THEN** the frontend MUST call `/api/admin/audit/actors/{kind}/{id}/events`
- **AND** action and resource filters MUST use stable values returned by audit data
#### Scenario: Select an exact resource result
- **WHEN** a user searches with a supported `resource_type` and exact `keyword` and selects a result
- **THEN** the frontend MUST pass `items[].resource_type/resource_id` unchanged to the resource timeline
- **AND** it MUST identify `historical=true` as a historical-snapshot match
#### Scenario: Query a request copied from Access Log
- **WHEN** a platform user explicitly pastes a request ID from Access Log
- **THEN** the frontend MAY query `/api/admin/audit/requests/{request_id}/timeline` with that exact value
- **AND** it MUST NOT claim that the endpoint scans Access Log or archived object storage
### Requirement: Retention, Pagination, and Time Semantics
All audit list and timeline views SHALL use the documented pagination and `retention` contract. Time filters SHALL be RFC3339 timestamps with timezone information, and `created_to` SHALL be treated as an exclusive upper bound.
#### Scenario: Respect the online retention window without a global prompt
- **WHEN** a response contains `retention.online_from`, `archived_before` and `timezone`
- **THEN** the frontend MUST preserve the retention semantics in its data contract without displaying a global online-window banner
- **AND** it MUST NOT interpret an empty online result as proof that no historical event exists
#### Scenario: Page through a timeline
- **WHEN** the user changes page or page size
- **THEN** the frontend MUST use `page` and `page_size`, respect the maximum page size of 100, and preserve the active filters
### Requirement: Finance Investigation Timeline
The admin frontend SHALL query `GET /api/admin/audit/finance/timeline` with any documented stable finance condition, including shop, wallet, order, payment, refund, recharge, approval, third-party trade, actor or correlation identifiers. Monetary values SHALL remain integer fen in application data, and authority SHALL follow `amount_authority`.
#### Scenario: Open finance history from a business record
- **WHEN** a user opens finance history from an order, refund, recharge, wallet or shop with a stable backend ID
- **THEN** the frontend MUST send the matching documented query parameter
- **AND** it MUST allow the server to complete related facts instead of assembling a local timeline
#### Scenario: Display an authoritative amount
- **WHEN** a finance node returns an amount and `amount_authority.authoritative=true`
- **THEN** the frontend MUST treat the referenced table and field as authoritative
- **AND** it MUST only convert integer fen for presentation and MUST NOT reconcile conflicting facts locally
### Requirement: Risk Investigation
The admin frontend SHALL provide risk overview and event-detail views using `/api/admin/audit/risks/overview` and `/api/admin/audit/risks/events`. Filters SHALL use codes returned in overview collections, and the selected time range SHALL not exceed 31 days.
#### Scenario: Drill down from a risk summary
- **WHEN** a user selects a risk, result, action, source or signal represented by the overview
- **THEN** the frontend MUST use the returned stable code for the supported event-list filter
- **AND** backend `name` values MUST be used only for display
#### Scenario: Select a range longer than 31 days
- **WHEN** the user attempts to query more than 31 days
- **THEN** the frontend MUST prevent submission and explain the maximum range
### Requirement: External Integration Investigation
The admin frontend SHALL provide Integration overview, list and detail views through the documented endpoints. It SHALL preserve provider, direction, operation, raw result, derived result category, duration, state-change, trigger, resource, linkage, attempt, content-summary, fidelity and retention fields.
#### Scenario: Apply overview filters to the list
- **WHEN** a user selects an overview provider, direction or result
- **THEN** the frontend MUST map `providers[].code` to `provider`, `directions[].code` to `direction`, `results[].code` to `result`, and `results[].category` to `result_category`
- **AND** it MUST use `name` only as display text
#### Scenario: Display Integration trend categories
- **WHEN** the overview returns trend points
- **THEN** the frontend MUST preserve the five categories `processing`, `succeeded`, `indeterminate`, `failed` and `not_sent`
- **AND** it MUST use only the documented `hour` or `day` bucket
#### Scenario: Inspect an Integration detail
- **WHEN** a user opens an `integration_id` returned by a list, event reference or notification target
- **THEN** the frontend MUST query the detail with that value unchanged
- **AND** it MUST present the capability as read-only without recovery, modification, deletion or export actions
### Requirement: Role-Specific Resource Activity
The frontend SHALL use the agent and enterprise resource-activity endpoints according to the authenticated subject. Agent activity SHALL support `iot_card`, `device`, `asset_allocation_record`, `exchange_order`, `shop` and `enterprise`; enterprise activity SHALL support only authorized `iot_card` and `device` resources.
#### Scenario: Open an agent resource activity
- **WHEN** an agent opens activity for a supported resource
- **THEN** the frontend MUST use ICCID for a card, VirtualNo for a device, or the documented stable business number for another supported resource
- **AND** it MUST NOT send or infer the agent identity or shop scope as query data
#### Scenario: Open enterprise asset activity
- **WHEN** an enterprise user opens an authorized card or device activity
- **THEN** the frontend MUST use ICCID or VirtualNo with the enterprise endpoint
- **AND** it MUST render only the subject-safe projection without platform actor, risk, internal reason or before/after fields
#### Scenario: A stable subject identifier is missing
- **WHEN** a business response does not contain the required ICCID, VirtualNo or stable business number
- **THEN** the frontend MUST hide the activity entry
- **AND** it MUST NOT substitute a display name or internal identifier intended for another subject
### Requirement: Business Audit Entries
The frontend SHALL add role-appropriate audit entries to card, device, shop, enterprise, order, refund and wallet list/detail contexts without changing existing business API URLs. Platform entries SHALL use backend internal IDs; agent and enterprise entries SHALL use their documented stable business identifiers.
#### Scenario: Open platform asset or business history
- **WHEN** a platform user opens audit history from a card, device, shop, enterprise, order or refund record
- **THEN** the frontend MUST use the corresponding `resource_type` and backend `response.data.id` with the resource timeline
#### Scenario: Open wallet or transaction history
- **WHEN** a user opens audit history for a wallet, order or refund with a stable ID
- **THEN** the frontend MUST offer the applicable finance timeline query
- **AND** the resource timeline MAY also be offered only when a stable resource reference is available
#### Scenario: Preserve existing operation logs
- **WHEN** new audit entries are introduced on asset pages
- **THEN** existing asset operation-log features MUST remain available
- **AND** the UI MUST distinguish the existing operation logs from the new cross-system audit investigation
### Requirement: Controlled Notification Integration Target
The frontend SHALL resolve a notification through `GET /api/admin/notifications/{id}/target` before opening an Integration detail. It SHALL open the detail only when the target is available, has `target_type=integration_log`, and contains a non-empty `target_key`.
#### Scenario: Open an Integration notification
- **WHEN** target resolution returns `available=true`, `target_type=integration_log`, and a non-empty `target_key`
- **THEN** the frontend MUST pass `target_key` unchanged as the Integration `integration_id`
- **AND** it MUST open the controlled internal Integration detail route
#### Scenario: Notification target cannot be used
- **WHEN** the target is unavailable, has another type, lacks `target_key`, or maps to no registered route
- **THEN** the frontend MUST not navigate or execute an arbitrary URL
- **AND** it MUST preserve the notification content and show a safe unavailable-target message
### Requirement: Audit Authorization and Read-Only Boundary
The frontend SHALL enforce the final platform, agent and enterprise route and action permissions, while preserving backend authentication, authorization and data-isolation enforcement. All capabilities in this change SHALL remain read-only.
#### Scenario: A subject lacks audit permission
- **WHEN** the current subject lacks the configured permission for an audit page or business entry
- **THEN** the frontend MUST hide or disable that page or entry and MUST NOT call the endpoint as a fallback
#### Scenario: An audit request is rejected
- **WHEN** an endpoint returns `400`, `401`, `403` or `500`
- **THEN** the frontend MUST use shared validation, authentication, authorization and server-error handling
- **AND** it MUST preserve the current investigation state when retrying would be unsafe or misleading
#### Scenario: A user inspects an audit record
- **WHEN** any audit, risk, finance, Integration or subject-activity view is displayed
- **THEN** the UI MUST NOT provide mutation, recovery, deletion, risk-disposition, blocking or export actions

View File

@@ -0,0 +1,44 @@
## 1. Contract and Shared Infrastructure
- [x] 1.1 根据 `默认模块.openapi.json` 建立审计事件、调查引用、资源、保留窗口、链路节点、资金节点、风险和 Integration 的 TypeScript 类型。
- [x] 1.2 新增平台审计、主体资源活动、资金、风险、Integration 和链路时间线 API 方法,保持文档参数名、枚举和 nullable 语义。
- [x] 1.3 建立共享的分页、RFC3339 时间范围、保留窗口、枚举名称和 API 错误展示能力。
- [x] 1.4 建立只读事件详情、资源摘要、调查引用和时间线节点组件,不提供写操作或导出能力。
## 2. Platform Audit Center
- [x] 2.1 实现审计事件列表及文档定义的时间、动作、类别、操作者、来源、结果、风险、范围、资源和链路筛选。
- [x] 2.2 实现事件详情,展示事件、操作者、资源快照、结果、风险和元数据,并按非空 `investigation_refs` 提供跳转。
- [x] 2.3 实现操作者行为时间线,使用 `actor_ref.kind/id` 和稳定 code 筛选。
- [x] 2.4 实现资源精确搜索及资源时间线,原样使用选中项的 `resource_type/resource_id` 并提示历史快照命中。
- [x] 2.5 实现请求和业务关联时间线,支持从返回引用进入,以及明确粘贴 Access Log `request_id` 的查询入口。
## 3. Specialized Investigations
- [x] 3.1 实现资金调查时间线和全部文档筛选条件,以分为数据单位并展示金额权威来源。
- [x] 3.2 实现风险总览和风险事件明细,按总览返回的稳定 code 回填筛选,并限制最长 31 天时间范围。
- [x] 3.3 实现 Integration 总览、列表和详情,覆盖 provider、direction、result、result_category、趋势、尝试、内容摘要、保真度和关联字段。
- [x] 3.4 实现各调查视角之间基于 `investigation_refs``linkage` 和稳定资源引用的受控跳转。
## 4. Subject Activity and Business Entries
- [x] 4.1 实现代理资源活动页面,支持卡、设备、资产分配记录、换货单、店铺和企业的稳定业务标识。
- [x] 4.2 实现企业资源活动页面,仅支持当前授权卡和设备并只渲染主体安全投影。
- [x] 4.3 在卡、设备、店铺、企业、订单、退款和钱包列表/详情增加角色匹配的「审计记录」入口。
- [x] 4.4 缺少内部 ID、ICCID、VirtualNo 或业务编号时隐藏入口,不使用页面文本或其他字段推断。
- [x] 4.5 保留现有资产操作日志入口,并通过命名与说明区分旧操作日志和新审计调查能力。
## 5. Notification, Routes, and Permissions
- [x] 5.1 增加审计中心、风险中心、Integration 和共享时间线的路由与菜单配置。
- [x] 5.2 接入最终确认的平台、代理、企业菜单/按钮权限编码,隐藏越权页面和业务入口。
- [x] 5.3 在通知目标导航中仅对可用 `integration_log` 目标开放 Integration 详情,并原样使用 `target_key`
- [x] 5.4 对未知、不可用或缺失目标保留安全提示,不执行任意 URL 或推测跳转。
## 6. Verification
- [ ] 6.1 为 API 参数序列化、枚举、nullable 字段、分页和 RFC3339 时间范围增加单元测试。
- [ ] 6.2 为稳定引用跳转、空引用隐藏、`fidelity=false`、通知 Integration 目标和禁止生成链路 ID 增加测试。
- [ ] 6.3 验证平台、代理、企业角色可见性、服务端数据隔离、企业安全投影以及 `401/403/400/500` 处理。
- [ ] 6.4 验证保留窗口、空数据、最长 31 天风险查询、资金金额单位和 Integration 五类趋势状态。
- [ ] 6.5 完成 16 个接口的联调回归,并确认现有业务 URL、通知中心和旧资产操作日志未被破坏。