From 33485b137f8d28a653c1d001119143381933f92a Mon Sep 17 00:00:00 2001 From: luo Date: Tue, 11 Aug 2026 16:47:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=AE=A1=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proposal.md | 18 + .../audit-chain-frontend-integration/spec.md | 26 + .../tasks.md | 16 + .../enrich-actor-timeline-detail/proposal.md | 16 + .../audit-chain-frontend-integration/spec.md | 29 + .../enrich-actor-timeline-detail/tasks.md | 10 + .../proposal.md | 17 + .../audit-chain-frontend-integration/spec.md | 22 + .../tasks.md | 10 + .../proposal.md | 17 + .../audit-chain-frontend-integration/spec.md | 23 + .../enrich-finance-timeline-detail/tasks.md | 10 + .../proposal.md | 17 + .../audit-chain-frontend-integration/spec.md | 22 + .../enrich-request-timeline-detail/tasks.md | 10 + .../proposal.md | 17 + .../audit-chain-frontend-integration/spec.md | 22 + .../enrich-resource-timeline-detail/tasks.md | 10 + src/api/modules/audit.ts | 33 +- .../business/audit/AuditEventTable.vue | 19 +- .../audit/AuditInvestigationDrawer.vue | 777 ++++++++++++++++-- .../audit/AuditInvestigationLinks.vue | 26 +- src/config/constants/audit.ts | 33 + src/router/guards/beforeEach.ts | 9 + src/store/modules/user.ts | 2 +- src/utils/business/auditAccess.ts | 14 + .../account-management/account/index.vue | 9 +- .../enterprise-customer/index.vue | 9 +- .../components/BasicInfoCard.vue | 46 +- .../components/CurrentPackageCard.vue | 2 +- .../components/WalletTransactionCard.vue | 15 +- .../asset-management/device-list/index.vue | 4 +- .../exchange-management/detail.vue | 8 +- .../exchange-management/index.vue | 8 +- .../iot-card-management/index.vue | 4 +- .../record-management/asset-assign/detail.vue | 8 +- .../record-management/asset-assign/index.vue | 8 +- src/views/audit/events/detail.vue | 9 +- src/views/audit/events/index.vue | 22 +- src/views/audit/integrations/detail.vue | 33 +- src/views/audit/integrations/index.vue | 24 +- src/views/audit/risks/index.vue | 4 + src/views/finance/agent-recharge/detail.vue | 8 +- src/views/finance/agent-recharge/index.vue | 8 +- src/views/finance/refund/detail.vue | 4 +- src/views/finance/refund/index.vue | 4 +- .../order-management/order-list/detail.vue | 2 +- .../order-management/order-list/index.vue | 2 +- src/views/shop-management/list/index.vue | 9 +- 49 files changed, 1263 insertions(+), 212 deletions(-) create mode 100644 openspec/changes/enforce-audit-subject-api-boundaries/proposal.md create mode 100644 openspec/changes/enforce-audit-subject-api-boundaries/specs/audit-chain-frontend-integration/spec.md create mode 100644 openspec/changes/enforce-audit-subject-api-boundaries/tasks.md create mode 100644 openspec/changes/enrich-actor-timeline-detail/proposal.md create mode 100644 openspec/changes/enrich-actor-timeline-detail/specs/audit-chain-frontend-integration/spec.md create mode 100644 openspec/changes/enrich-actor-timeline-detail/tasks.md create mode 100644 openspec/changes/enrich-correlation-timeline-detail/proposal.md create mode 100644 openspec/changes/enrich-correlation-timeline-detail/specs/audit-chain-frontend-integration/spec.md create mode 100644 openspec/changes/enrich-correlation-timeline-detail/tasks.md create mode 100644 openspec/changes/enrich-finance-timeline-detail/proposal.md create mode 100644 openspec/changes/enrich-finance-timeline-detail/specs/audit-chain-frontend-integration/spec.md create mode 100644 openspec/changes/enrich-finance-timeline-detail/tasks.md create mode 100644 openspec/changes/enrich-request-timeline-detail/proposal.md create mode 100644 openspec/changes/enrich-request-timeline-detail/specs/audit-chain-frontend-integration/spec.md create mode 100644 openspec/changes/enrich-request-timeline-detail/tasks.md create mode 100644 openspec/changes/enrich-resource-timeline-detail/proposal.md create mode 100644 openspec/changes/enrich-resource-timeline-detail/specs/audit-chain-frontend-integration/spec.md create mode 100644 openspec/changes/enrich-resource-timeline-detail/tasks.md create mode 100644 src/utils/business/auditAccess.ts diff --git a/openspec/changes/enforce-audit-subject-api-boundaries/proposal.md b/openspec/changes/enforce-audit-subject-api-boundaries/proposal.md new file mode 100644 index 0000000..8395fbe --- /dev/null +++ b/openspec/changes/enforce-audit-subject-api-boundaries/proposal.md @@ -0,0 +1,18 @@ +# Change: 收紧审计接口的主体访问边界 + +## Why + +平台 `/api/admin/audit/*` 接口仅允许超级管理员和平台账号访问。当前部分业务入口未传递当前账号类型,可能让代理或企业用户构造平台资源时间线目标;平台审计页面和调查抽屉也缺少统一的前端请求拦截。 + +## What Changes + +- 平台审计中心、平台资源时间线、资金链路、请求链路、风险和 Integration 等 `/api/admin/audit/*` 调用只允许超级管理员或平台账号发起。 +- 代理账号仅通过代理主体活动接口查看其支持资源;企业账号仅通过企业主体活动接口查看卡和设备资源。 +- 业务入口基于当前登录账号类型选择对应权限与调查目标,账号、店铺等不受主体活动支持的资源不向代理或企业显示平台审计入口。 +- 在共享调查加载层增加防御性校验,越权目标不发起网络请求并提示不可用。 +- 保持审计能力严格只读:所有相关前端 API 保持 GET,不新增修改、删除、导出、恢复、重试、补偿或风险处置操作。 + +## Impact + +- Affected specs: `audit-chain-frontend-integration` +- Affected code: `src/components/business/audit/`, `src/utils/business/auditNavigation.ts`, audit routes/views, and business pages with audit-entry buttons including account, shop, asset, order, refund and wallet views. diff --git a/openspec/changes/enforce-audit-subject-api-boundaries/specs/audit-chain-frontend-integration/spec.md b/openspec/changes/enforce-audit-subject-api-boundaries/specs/audit-chain-frontend-integration/spec.md new file mode 100644 index 0000000..20e15ea --- /dev/null +++ b/openspec/changes/enforce-audit-subject-api-boundaries/specs/audit-chain-frontend-integration/spec.md @@ -0,0 +1,26 @@ +## ADDED Requirements + +### Requirement: 审计接口按当前主体隔离 + +前端 SHALL 仅允许超级管理员和平台账号调用 `/api/admin/audit/*` 平台审计接口。代理账号 MUST 仅调用其支持资源的代理主体活动接口;企业账号 MUST 仅调用卡和设备资源的企业主体活动接口。共享调查加载层 MUST 在请求前拒绝不匹配当前主体的调查目标。 + +#### Scenario: 代理或企业用户访问业务审计入口 + +- **WHEN** 代理或企业用户打开业务列表、详情或资源信息 +- **THEN** 前端仅显示当前主体支持的活动入口 +- **AND** 前端不请求任何 `/api/admin/audit/*` 接口 + +#### Scenario: 平台用户访问审计调查 + +- **WHEN** 超级管理员或平台账号打开平台审计、资源时间线、资金链路、请求链路、风险或 Integration 调查 +- **THEN** 前端使用对应的 `/api/admin/audit/*` GET 接口 + +### Requirement: 审计调查仅提供只读能力 + +审计调查前端 SHALL 仅使用 GET 请求读取平台审计或主体活动数据,且 MUST NOT 提供修改、删除、导出、恢复、重试、补偿或风险处置操作。 + +#### Scenario: 用户查看审计或主体活动记录 + +- **WHEN** 用户查询任一审计或主体活动视图 +- **THEN** 页面只展示只读数据和受控跳转 +- **AND** 页面不展示或调用任何写入、导出、恢复、重试、补偿或风险处置操作 diff --git a/openspec/changes/enforce-audit-subject-api-boundaries/tasks.md b/openspec/changes/enforce-audit-subject-api-boundaries/tasks.md new file mode 100644 index 0000000..7cdc1fc --- /dev/null +++ b/openspec/changes/enforce-audit-subject-api-boundaries/tasks.md @@ -0,0 +1,16 @@ +## 1. Shared Access Rules + +- [x] 1.1 Add shared current-user access checks for platform audit and subject activity APIs. +- [x] 1.2 Guard the shared investigation loader so it does not request a platform audit API for agent or enterprise users. + +## 2. Entry Points and Pages + +- [x] 2.1 Update business audit entry points to pass current user type and use only supported subject activity targets for agents and enterprises. +- [x] 2.2 Hide or block platform audit routes and actions for agent and enterprise users. +- [x] 2.3 Keep finance, risk, Integration and link-timeline entries platform-only. + +## 3. Read-only Verification + +- [x] 3.1 Verify all audit and subject-activity methods used by the UI are GET-only. +- [x] 3.2 Verify platform, agent and enterprise users cannot trigger the wrong API family. +- [x] 3.3 Run lint and type checks for changed files. diff --git a/openspec/changes/enrich-actor-timeline-detail/proposal.md b/openspec/changes/enrich-actor-timeline-detail/proposal.md new file mode 100644 index 0000000..f9846ae --- /dev/null +++ b/openspec/changes/enrich-actor-timeline-detail/proposal.md @@ -0,0 +1,16 @@ +# Change: 补全操作者行为时间线事件信息 + +## Why + +操作者行为时间线接口已返回完整审计事件字段,但当前调查抽屉仅展示动作、结果、摘要、操作者名称和来源,无法支持对操作者所属组织、业务范围、请求上下文、批次结果、失败原因和关联资源的有效追溯。 + +## What Changes + +- 在操作者行为时间线的每个事件节点展示接口返回的分类、风险、操作者身份与组织快照、业务范围、请求摘要、批次统计和错误摘要。 +- 展示关联资源的名称、类型、关系、业务角色和主体摘要;将资源快照与前后变更数据放入按需展开的只读区域。 +- 对缺失或空字段使用明确的空态,不从名称、时间或相邻事件推断数据;保留分页、排序与只读边界。 + +## Impact + +- Affected specs: `audit-chain-frontend-integration` +- Affected code: `src/components/business/audit/AuditInvestigationDrawer.vue` diff --git a/openspec/changes/enrich-actor-timeline-detail/specs/audit-chain-frontend-integration/spec.md b/openspec/changes/enrich-actor-timeline-detail/specs/audit-chain-frontend-integration/spec.md new file mode 100644 index 0000000..948e0a8 --- /dev/null +++ b/openspec/changes/enrich-actor-timeline-detail/specs/audit-chain-frontend-integration/spec.md @@ -0,0 +1,29 @@ +## ADDED Requirements + +### Requirement: 操作者行为时间线完整事件展示 + +前端 SHALL 在操作者行为时间线中展示接口返回的事件分类、动作、结果、风险、摘要、操作者身份及所属组织快照、业务范围、批次统计、错误摘要和关联资源;分类、来源、操作者和业务范围 MUST 在同一行展示。请求摘要与扩展元数据 MUST 仅向超级管理员展示。资源身份快照、主体安全数据及变更前后数据 MUST 以按需展开的只读区域展示。 + +#### Scenario: 查看包含完整审计字段的操作者事件 + +- **WHEN** `/api/admin/audit/actors/{kind}/{id}/events` 返回包含操作者、范围、请求、批次、错误和资源字段的事件 +- **THEN** 前端在对应时间线节点中展示这些字段的业务可读值 +- **AND** 将资源快照与变更数据放入可折叠的只读区域 + +#### Scenario: 非超级管理员查看事件 + +- **WHEN** 非超级管理员打开操作者行为时间线 +- **THEN** 前端不展示请求上下文和扩展元数据 +- **AND** 其余允许展示的事件字段保持只读 + +#### Scenario: 字段缺失或资源为空 + +- **WHEN** 事件中可选字段为空或没有关联资源 +- **THEN** 前端显示明确空态或隐藏对应区域 +- **AND** 不从名称、时间或其他事件推断或补造数据 + +#### Scenario: 保持只读与分页语义 + +- **WHEN** 用户查看或翻页操作者行为时间线 +- **THEN** 前端继续使用文档规定的分页与排序结果 +- **AND** 不提供修改、删除、导出、恢复、重试、补偿或风险处置操作 diff --git a/openspec/changes/enrich-actor-timeline-detail/tasks.md b/openspec/changes/enrich-actor-timeline-detail/tasks.md new file mode 100644 index 0000000..ef5ffe0 --- /dev/null +++ b/openspec/changes/enrich-actor-timeline-detail/tasks.md @@ -0,0 +1,10 @@ +## 1. Implementation + +- [x] 1.1 为操作者行为时间线事件节点补充接口已返回的事件、操作者、范围、请求、批次和错误字段。 +- [x] 1.2 以可折叠只读区域展示关联资源与快照/变更数据,并处理空值和无资源场景。 +- [x] 1.3 保持资源、请求和关联标识只读展示,不新增跳转或写操作。 + +## 2. Verification + +- [x] 2.1 验证接口返回字段均按文档语义显示且不改变分页和排序。 +- [x] 2.2 运行格式化、类型检查和 ESLint。 diff --git a/openspec/changes/enrich-correlation-timeline-detail/proposal.md b/openspec/changes/enrich-correlation-timeline-detail/proposal.md new file mode 100644 index 0000000..e6917f1 --- /dev/null +++ b/openspec/changes/enrich-correlation-timeline-detail/proposal.md @@ -0,0 +1,17 @@ +# Change: 补全业务关联链路节点展示 + +## Why + +业务关联链路弹窗当前每个节点仅显示标题、结果、摘要和事实来源,无法清晰查看节点编码、关联范围和资源引用。操作者行为时间线已采用结构化、按需展开的只读展示,业务关联链路需要使用相同的视觉层级,但不能假设接口返回了操作者事件专有字段。 + +## What Changes + +- 基于 `AuditLinkTimelineNode` 的实际返回字段,使用与操作者行为时间线一致的卡片、标签和基础信息单行布局展示业务关联节点。 +- 仅展示节点实际返回的事实来源和稳定编码;关联链路、父审计事件、请求和资源内部 ID 不在界面显示,数据完整性信息仅超级管理员可见。 +- 当节点返回资源引用时,以折叠的只读区域展示资源名称、类型和业务 Key;不推断资源快照、操作者、风险或变更数据。 +- 保持业务关联链路全量查询、排序、只读和无额外跳转的现有语义。 + +## Impact + +- Affected specs: `audit-chain-frontend-integration` +- Affected code: `src/components/business/audit/AuditInvestigationDrawer.vue` diff --git a/openspec/changes/enrich-correlation-timeline-detail/specs/audit-chain-frontend-integration/spec.md b/openspec/changes/enrich-correlation-timeline-detail/specs/audit-chain-frontend-integration/spec.md new file mode 100644 index 0000000..cba1380 --- /dev/null +++ b/openspec/changes/enrich-correlation-timeline-detail/specs/audit-chain-frontend-integration/spec.md @@ -0,0 +1,22 @@ +## ADDED Requirements + +### Requirement: 业务关联链路结构化节点展示 + +前端 SHALL 在业务关联链路中以与操作者行为时间线一致的层级展示每个 `AuditLinkTimelineNode`:节点标题、结果、事实来源和稳定编码 MUST 使用接口实际返回值;基础信息 MUST 在同一行展示。关联链路、父审计事件、请求和资源内部 ID MUST 不在界面显示;数据完整性信息 MUST 仅向超级管理员展示。资源引用 MUST 按需在折叠的只读区域展示。 + +#### Scenario: 查看包含资源引用的关联链路节点 + +- **WHEN** 业务关联链路返回含资源引用和关联标识的节点 +- **THEN** 前端展示节点基础信息并提供只读、可折叠的资源引用区域 +- **AND** 不推断操作者、分类、风险、资源快照或变更数据 + +#### Scenario: 非超级管理员查看关联链路 + +- **WHEN** 非超级管理员打开业务关联链路弹窗 +- **THEN** 前端不展示请求标识和数据完整性信息 +- **AND** 继续展示其余允许的节点字段 + +#### Scenario: 保持只读链路语义 + +- **WHEN** 用户查看业务关联链路 +- **THEN** 前端不提供修改、删除、导出、恢复、重试、补偿或风险处置操作 diff --git a/openspec/changes/enrich-correlation-timeline-detail/tasks.md b/openspec/changes/enrich-correlation-timeline-detail/tasks.md new file mode 100644 index 0000000..ff25ee3 --- /dev/null +++ b/openspec/changes/enrich-correlation-timeline-detail/tasks.md @@ -0,0 +1,10 @@ +## 1. Implementation + +- [x] 1.1 为业务关联链路节点增加与操作者时间线一致的基础信息单行布局。 +- [x] 1.2 隐藏关联链路中的所有 ID,并按超级管理员边界展示数据完整性信息。 +- [x] 1.3 对接口返回的资源引用增加折叠的只读展示,并处理空字段。 + +## 2. Verification + +- [x] 2.1 验证只使用 `AuditLinkTimelineNode` 已返回字段,不补造操作者事件字段。 +- [x] 2.2 运行格式化、ESLint、类型检查和 OpenSpec 严格校验。 diff --git a/openspec/changes/enrich-finance-timeline-detail/proposal.md b/openspec/changes/enrich-finance-timeline-detail/proposal.md new file mode 100644 index 0000000..6554002 --- /dev/null +++ b/openspec/changes/enrich-finance-timeline-detail/proposal.md @@ -0,0 +1,17 @@ +# Change: 补全财务审计时间线节点展示 + +## Why + +资金调查时间线接口已返回金额、前后余额、金额权威来源、钱包引用和安全结构化事实,但当前弹窗只展示标题、结果、金额、编码和来源,无法支持资金事实核对。 + +## What Changes + +- 按“查询资金调查时间线”中的 `AuditFinanceTimelineNode` 响应结构,将财务审计节点改为与既有时间线一致的结构化卡片与折叠信息区。 +- 显示金额、变更前后余额、币种、事实来源、稳定编码和钱包类型;金额均按分转换为展示金额。 +- 金额权威来源与安全结构化事实使用按需展开的只读区域;不显示内部节点、店铺或钱包 ID,不新增跳转或写操作。 +- 保留现有稳定条件查询、分页、排序和权威金额字段语义。 + +## Impact + +- Affected specs: `audit-chain-frontend-integration` +- Affected code: `src/components/business/audit/AuditInvestigationDrawer.vue` diff --git a/openspec/changes/enrich-finance-timeline-detail/specs/audit-chain-frontend-integration/spec.md b/openspec/changes/enrich-finance-timeline-detail/specs/audit-chain-frontend-integration/spec.md new file mode 100644 index 0000000..1147f92 --- /dev/null +++ b/openspec/changes/enrich-finance-timeline-detail/specs/audit-chain-frontend-integration/spec.md @@ -0,0 +1,23 @@ +## ADDED Requirements + +### Requirement: 财务审计时间线完整资金事实展示 + +前端 SHALL 在财务审计时间线中按 `AuditFinanceTimelineNode` 展示标题、结果、金额、变更前后余额、币种、事实来源、稳定编码和钱包类型。金额与余额 MUST 按分转换为展示金额。金额权威来源和安全结构化事实 MUST 使用按需展开的只读区域;内部节点、店铺和钱包 ID MUST 不在界面显示。 + +#### Scenario: 查看带金额权威信息的资金节点 + +- **WHEN** 资金节点返回金额、余额、钱包和 `amount_authority` +- **THEN** 前端展示格式化后的金额与余额,并在折叠区域展示权威来源说明 +- **AND** 不将非权威字段当作权威金额 + +#### Scenario: 查看安全结构化事实 + +- **WHEN** 资金节点返回非空 `facts` +- **THEN** 前端提供可折叠的只读 JSON 展示 +- **AND** 不显示内部 ID 或提供写操作 + +#### Scenario: 金额字段为空 + +- **WHEN** 资金节点不承载金额或余额 +- **THEN** 前端隐藏相应金额项或显示中性空态 +- **AND** 保持分页和排序结果不变 diff --git a/openspec/changes/enrich-finance-timeline-detail/tasks.md b/openspec/changes/enrich-finance-timeline-detail/tasks.md new file mode 100644 index 0000000..613f694 --- /dev/null +++ b/openspec/changes/enrich-finance-timeline-detail/tasks.md @@ -0,0 +1,10 @@ +## 1. Implementation + +- [x] 1.1 按 `AuditFinanceTimelineNode` 补充金额、余额、币种、来源、编码和钱包类型展示。 +- [x] 1.2 增加金额权威来源和安全结构化事实的折叠只读区域。 +- [x] 1.3 隐藏内部 ID,处理无金额、无钱包和空结构化字段场景。 + +## 2. Verification + +- [x] 2.1 验证金额单位按分转换,并保持 `amount_authority.authoritative` 语义。 +- [x] 2.2 运行格式化、ESLint、类型检查和 OpenSpec 严格校验。 diff --git a/openspec/changes/enrich-request-timeline-detail/proposal.md b/openspec/changes/enrich-request-timeline-detail/proposal.md new file mode 100644 index 0000000..b2e8f9e --- /dev/null +++ b/openspec/changes/enrich-request-timeline-detail/proposal.md @@ -0,0 +1,17 @@ +# Change: 补全请求链路节点展示 + +## Why + +请求链路弹窗当前仅呈现标题、结果、摘要和事实来源,未充分利用接口返回的稳定编码、资源引用和关联完整性信息,且与已调整的操作者、业务关联链路节点样式不一致。 + +## What Changes + +- 基于 `查询请求关联时间线_简化版.md` 的 `AuditLinkTimelineNode`,将请求链路节点改为与既有时间线一致的结构化卡片与单行基础信息布局。 +- 节点仅展示标题、结果、事实来源和稳定编码;不显示请求、业务关联、父审计事件、节点或资源内部 ID。 +- 资源引用以折叠只读区域展示名称、类型和业务 Key;关联完整性信息仅超级管理员可见。 +- 保持接口全量排序和只读语义,不新增跳转或写操作。 + +## Impact + +- Affected specs: `audit-chain-frontend-integration` +- Affected code: `src/components/business/audit/AuditInvestigationDrawer.vue` diff --git a/openspec/changes/enrich-request-timeline-detail/specs/audit-chain-frontend-integration/spec.md b/openspec/changes/enrich-request-timeline-detail/specs/audit-chain-frontend-integration/spec.md new file mode 100644 index 0000000..437ae48 --- /dev/null +++ b/openspec/changes/enrich-request-timeline-detail/specs/audit-chain-frontend-integration/spec.md @@ -0,0 +1,22 @@ +## ADDED Requirements + +### Requirement: 请求链路结构化节点展示 + +前端 SHALL 在请求链路中以与操作者和业务关联链路一致的层级展示每个 `AuditLinkTimelineNode`:节点标题、结果、事实来源和稳定编码 MUST 使用接口实际返回值,并在同一行展示基础信息。请求、业务关联、父审计事件、节点和资源内部 ID MUST 不在界面显示。资源引用 MUST 以按需展开的只读区域展示;数据完整性信息 MUST 仅向超级管理员展示。 + +#### Scenario: 查看含资源引用的请求链路节点 + +- **WHEN** 请求链路返回含资源引用和关联完整性信息的节点 +- **THEN** 前端展示节点基础信息和可折叠的资源引用区域 +- **AND** 不展示接口返回的任何 ID + +#### Scenario: 非超级管理员查看请求链路 + +- **WHEN** 非超级管理员打开请求链路弹窗 +- **THEN** 前端不展示数据完整性信息 +- **AND** 继续展示其余允许的节点字段 + +#### Scenario: 保持请求链路只读语义 + +- **WHEN** 用户查看请求链路 +- **THEN** 前端不提供修改、删除、导出、恢复、重试、补偿或风险处置操作 diff --git a/openspec/changes/enrich-request-timeline-detail/tasks.md b/openspec/changes/enrich-request-timeline-detail/tasks.md new file mode 100644 index 0000000..8cfcb0d --- /dev/null +++ b/openspec/changes/enrich-request-timeline-detail/tasks.md @@ -0,0 +1,10 @@ +## 1. Implementation + +- [x] 1.1 为请求链路节点增加与既有时间线一致的基础信息布局。 +- [x] 1.2 隐藏所有 ID,仅向超级管理员展示数据完整性信息。 +- [x] 1.3 对请求链路返回的资源引用增加折叠只读展示并处理空字段。 + +## 2. Verification + +- [x] 2.1 验证仅使用简化版文档已定义的 `AuditLinkTimelineNode` 字段。 +- [x] 2.2 运行格式化、ESLint、类型检查和 OpenSpec 严格校验。 diff --git a/openspec/changes/enrich-resource-timeline-detail/proposal.md b/openspec/changes/enrich-resource-timeline-detail/proposal.md new file mode 100644 index 0000000..71e94a6 --- /dev/null +++ b/openspec/changes/enrich-resource-timeline-detail/proposal.md @@ -0,0 +1,17 @@ +# Change: 补全通用资源时间线事件展示 + +## Why + +通用资源时间线接口返回完整 `AuditEventPage` / `AuditEventView`,但当前弹窗仅显示动作、结果、摘要、操作者和来源,未使用接口返回的分类、风险、操作者组织、业务范围、批次、错误、资源快照与变更数据。 + +## What Changes + +- 依据“查询通用资源时间线”响应结构,在资源审计时间线中复用操作者时间线的完整事件节点布局。 +- 展示分类、来源、操作者、业务范围、风险、批次、错误及关联资源;基础信息同一行,资源 JSON 按需折叠。 +- 请求上下文与扩展元数据仅超级管理员可见;不新增请求筛选、跳转或任何写操作。 +- 保持接口现有分页、排序和“缺少 resource_id 隐藏入口”的调用边界。 + +## Impact + +- Affected specs: `audit-chain-frontend-integration` +- Affected code: `src/components/business/audit/AuditInvestigationDrawer.vue` diff --git a/openspec/changes/enrich-resource-timeline-detail/specs/audit-chain-frontend-integration/spec.md b/openspec/changes/enrich-resource-timeline-detail/specs/audit-chain-frontend-integration/spec.md new file mode 100644 index 0000000..36ffa8b --- /dev/null +++ b/openspec/changes/enrich-resource-timeline-detail/specs/audit-chain-frontend-integration/spec.md @@ -0,0 +1,22 @@ +## ADDED Requirements + +### Requirement: 通用资源时间线完整事件展示 + +前端 SHALL 按通用资源时间线返回的 `AuditEventPage` 展示完整 `AuditEventView` 节点:分类、来源、操作者、业务范围 MUST 在同一行展示;风险、批次统计、错误信息及关联资源 MUST 使用接口返回值。资源身份快照、主体安全数据及变更前后数据 MUST 以按需展开的只读区域展示。请求上下文与扩展元数据 MUST 仅向超级管理员展示。 + +#### Scenario: 查看包含资源快照的资源时间线事件 + +- **WHEN** 资源时间线返回包含批次、错误和关联资源快照的事件 +- **THEN** 前端在对应节点展示完整事件字段和折叠的资源数据 +- **AND** 不推断缺失字段或增加写操作 + +#### Scenario: 非超级管理员查看资源时间线 + +- **WHEN** 非超级管理员打开资源审计时间线 +- **THEN** 前端不展示请求上下文和扩展元数据 +- **AND** 继续展示其余允许的事件字段 + +#### Scenario: 保持资源时间线分页语义 + +- **WHEN** 用户翻页资源审计时间线 +- **THEN** 前端继续使用接口返回的页码、每页数量和稳定排序结果 diff --git a/openspec/changes/enrich-resource-timeline-detail/tasks.md b/openspec/changes/enrich-resource-timeline-detail/tasks.md new file mode 100644 index 0000000..35363e6 --- /dev/null +++ b/openspec/changes/enrich-resource-timeline-detail/tasks.md @@ -0,0 +1,10 @@ +## 1. Implementation + +- [x] 1.1 将资源时间线接入完整 `AuditEventView` 节点展示。 +- [x] 1.2 复用分类、来源、操作者、业务范围单行布局以及批次、错误和资源快照折叠区。 +- [x] 1.3 确保请求上下文和扩展元数据仅超级管理员可见。 + +## 2. Verification + +- [x] 2.1 验证接口分页、排序、空值和资源 ID 入口边界未改变。 +- [x] 2.2 运行格式化、ESLint、类型检查和 OpenSpec 严格校验。 diff --git a/src/api/modules/audit.ts b/src/api/modules/audit.ts index f2db60a..353bead 100644 --- a/src/api/modules/audit.ts +++ b/src/api/modules/audit.ts @@ -1,4 +1,6 @@ import { BaseService } from '../BaseService' +import { useUserStore } from '@/store/modules/user' +import { isPlatformAuditAccount } from '@/utils/business/auditAccess' import type { AuditActorEventQuery, AuditActorKind, @@ -26,12 +28,28 @@ import type { } from '@/types/api' export class AuditService extends BaseService { + private static ensurePlatformAuditAccess() { + const userStore = useUserStore() + if (!isPlatformAuditAccount(userStore.info.user_type, userStore.isSuperAdmin)) { + throw new Error('当前账号无权访问平台审计接口') + } + } + + private static ensureSubjectActivityAccess(subject: 'agent' | 'enterprise') { + const userType = Number(useUserStore().info.user_type) + if ((subject === 'agent' && userType !== 3) || (subject === 'enterprise' && userType !== 4)) { + throw new Error('当前账号无权访问主体活动接口') + } + } + static getEvents(params?: AuditEventQuery): Promise> { + this.ensurePlatformAuditAccess() return this.get('/api/admin/audit/events', params) } static getEventDetail(eventId: string): Promise> { - return this.get(`/api/admin/audit/events/${encodeURIComponent(eventId)}`) + this.ensurePlatformAuditAccess() + return this.get(`/api/admin/audit/events/${eventId}`) } static getActorEvents( @@ -39,6 +57,7 @@ export class AuditService extends BaseService { id: string, params?: AuditActorEventQuery ): Promise> { + this.ensurePlatformAuditAccess() return this.get( `/api/admin/audit/actors/${encodeURIComponent(kind)}/${encodeURIComponent(id)}/events`, params @@ -48,6 +67,7 @@ export class AuditService extends BaseService { static searchResources( params: AuditResourceSearchQuery ): Promise> { + this.ensurePlatformAuditAccess() return this.get('/api/admin/audit/resources/search', params) } @@ -56,6 +76,7 @@ export class AuditService extends BaseService { resourceId: string, params?: AuditResourceTimelineQuery ): Promise> { + this.ensurePlatformAuditAccess() return this.get( `/api/admin/audit/resources/${encodeURIComponent(resourceType)}/${encodeURIComponent(resourceId)}/timeline`, params @@ -63,40 +84,48 @@ export class AuditService extends BaseService { } static getRequestTimeline(requestId: string): Promise> { + this.ensurePlatformAuditAccess() return this.get(`/api/admin/audit/requests/${encodeURIComponent(requestId)}/timeline`) } static getCorrelationTimeline(correlationId: string): Promise> { + this.ensurePlatformAuditAccess() return this.get(`/api/admin/audit/correlations/${encodeURIComponent(correlationId)}/timeline`) } static getFinanceTimeline( params: AuditFinanceQuery ): Promise> { + this.ensurePlatformAuditAccess() return this.get('/api/admin/audit/finance/timeline', params) } static getRiskOverview(params?: AuditRiskQuery): Promise> { + this.ensurePlatformAuditAccess() return this.get('/api/admin/audit/risks/overview', params) } static getRiskEvents(params?: AuditRiskEventQuery): Promise> { + this.ensurePlatformAuditAccess() return this.get('/api/admin/audit/risks/events', params) } static getIntegrationOverview( params?: IntegrationQuery & { bucket?: 'hour' | 'day' } ): Promise> { + this.ensurePlatformAuditAccess() return this.get('/api/admin/audit/integrations/overview', params) } static getIntegrations(params?: IntegrationQuery): Promise> { + this.ensurePlatformAuditAccess() return this.get('/api/admin/audit/integrations', params) } static getIntegrationDetail( integrationId: string ): Promise> { + this.ensurePlatformAuditAccess() return this.get(`/api/admin/audit/integrations/${encodeURIComponent(integrationId)}`) } @@ -105,6 +134,7 @@ export class AuditService extends BaseService { identifier: string, params?: AuditSubjectActivityQuery ): Promise> { + this.ensureSubjectActivityAccess('agent') return this.get( `/api/admin/agent/resource-activities/${encodeURIComponent(resourceType)}/${encodeURIComponent(identifier)}`, params @@ -116,6 +146,7 @@ export class AuditService extends BaseService { identifier: string, params?: AuditSubjectActivityQuery ): Promise> { + this.ensureSubjectActivityAccess('enterprise') return this.get( `/api/admin/enterprise/resource-activities/${encodeURIComponent(resourceType)}/${encodeURIComponent(identifier)}`, params diff --git a/src/components/business/audit/AuditEventTable.vue b/src/components/business/audit/AuditEventTable.vue index 0c24e46..f6d13ab 100644 --- a/src/components/business/audit/AuditEventTable.vue +++ b/src/components/business/audit/AuditEventTable.vue @@ -12,7 +12,12 @@ - + @@ -30,11 +35,6 @@ }} - - - @@ -44,7 +44,12 @@ import { formatDateTime } from '@/utils/business/format' import { auditResultMeta, auditRiskMeta } from '@/utils/business/audit' - defineProps<{ items: AuditEventView[]; loading?: boolean }>() + withDefaults( + defineProps<{ items: AuditEventView[]; loading?: boolean; detailEnabled?: boolean }>(), + { + detailEnabled: true + } + ) defineEmits<{ detail: [row: AuditEventView] }>() const resultMeta = (row: AuditEventView) => auditResultMeta[row.result] || { label: row.result, type: 'info' as const } diff --git a/src/components/business/audit/AuditInvestigationDrawer.vue b/src/components/business/audit/AuditInvestigationDrawer.vue index d6114c7..e067020 100644 --- a/src/components/business/audit/AuditInvestigationDrawer.vue +++ b/src/components/business/audit/AuditInvestigationDrawer.vue @@ -21,12 +21,172 @@
{{ item.action_name }} - {{ - auditResultMeta[item.result]?.label || item.result - }} + + {{ + auditResultMeta[item.result]?.label || item.result + }} + + {{ auditRiskMeta[item.risk_level]?.label || item.risk_level }}风险 + +
-

{{ item.summary || '-' }}

-
+

{{ item.summary || '-' }}

+ +
{{ item.actor_name || item.actor_id || '-' }} {{ auditSourceLabels[item.source] || item.source }}
@@ -45,16 +205,65 @@
{{ node.title }} - {{ - auditResultDisplay(node.result, node.result_name) - }} + + {{ + auditResultDisplay(node.result, node.result_name) + }} + + 只读引用 + +
-

{{ node.summary || '-' }}

-
+

{{ node.summary || '-' }}

+ +
{{ recordSourceLabels[node.record_source] || node.record_source }} - - 只读引用 -
@@ -75,13 +284,63 @@ {{ auditResultDisplay(item.result, item.result_name) }}
-

- 金额:{{ formatMoney(item.amount, item.currency) }} -

-
- {{ item.code || '-' }} - {{ item.record_source || '-' }} -
+ + + {{ formatMoney(item.amount, item.currency) }} + + + {{ formatMoney(item.balance_before, item.currency) }} + + + {{ formatMoney(item.balance_after, item.currency) }} + + {{ item.currency || '-' }} + + {{ financeRecordSourceLabel(item.record_source) }} + + {{ item.code || '-' }} + + {{ financeWalletTypeLabel(item.wallet?.resource_type) }} + + + + + + + + {{ item.amount_authority.authoritative ? '是' : '否' }} + + + + {{ item.amount_authority.table }} + + + {{ item.amount_authority.field }} + + + {{ item.amount_authority.conflict_rule }} + + + + +
{{ auditJsonWithoutIds(item.facts) }}
+
+
@@ -138,63 +397,176 @@ @@ -226,13 +598,19 @@ } from '@/types/api' import { auditJson, + auditCategoryLabels, + auditResourceRelationLabels, + auditResourceTypeLabels, auditResultDisplay, auditResultMeta, auditRiskMeta, + auditScopeTypeLabels, auditSourceLabels, integrationCategoryMeta } from '@/utils/business/audit' import { formatDateTime } from '@/utils/business/format' + import { canLoadAuditInvestigation } from '@/utils/business/auditAccess' + import { useUserStore } from '@/store/modules/user' import type { AuditInvestigationTarget } from './types' const props = defineProps<{ @@ -240,6 +618,7 @@ target?: AuditInvestigationTarget | null }>() const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>() + const userStore = useUserStore() const visible = computed({ get: () => props.modelValue, @@ -268,6 +647,12 @@ const eventDetail = ref() const integrationDetail = ref() let loadSequence = 0 + const isFullEventTimeline = computed(() => + ['actor', 'resource'].includes(props.target?.mode || '') + ) + const isStructuredLinkTimeline = computed(() => + ['request', 'correlation'].includes(props.target?.mode || '') + ) const recordSourceLabels: Record = { audit_event: '审计事件', @@ -276,6 +661,21 @@ asynq_task: '异步任务引用', domain_ledger_ref: '业务台账引用' } + const financeRecordSourceLabels: Record = { + audit_event: '审计事件', + domain_ledger_ref: '业务台账引用', + agent_wallet_transaction: '代理钱包流水', + asset_wallet_transaction: '资产钱包流水', + agent_wallet_reservation: '代理钱包预留', + order: '订单', + payment: '支付', + refund: '退款', + agent_recharge: '代理充值', + recharge_order: '充值订单', + commission_record: '佣金记录', + commission_withdrawal: '佣金提现', + approval_instance: '审批实例' + } const clear = () => { eventItems.value = [] @@ -288,12 +688,96 @@ } const hasJsonContent = (value?: Record | null) => Boolean(value && Object.keys(value).length) + const auditJsonWithoutIds = (value?: Record | null) => { + const removeIds = (current: unknown): unknown => { + if (Array.isArray(current)) return current.map(removeIds) + if (!current || typeof current !== 'object') return current + return Object.fromEntries( + Object.entries(current as Record) + .filter(([key]) => !/(^|[_-])ids?$/i.test(key)) + .map(([key, nested]) => [key, removeIds(nested)]) + ) + } + return auditJson(removeIds(value)) + } + const withoutIds = (value?: Record | null) => { + const removeIds = (current: unknown): unknown => { + if (Array.isArray(current)) return current.map(removeIds) + if (!current || typeof current !== 'object') return current + return Object.fromEntries( + Object.entries(current as Record) + .filter(([key]) => !/(^|[_-])ids?$/i.test(key)) + .map(([key, nested]) => [key, removeIds(nested)]) + ) + } + return removeIds(value) as Record | null | undefined + } + const hasJsonContentWithoutIds = (value?: Record | null) => + hasJsonContent(withoutIds(value)) + const hasVisibleIntegrationContent = computed(() => + Boolean( + integrationDetail.value && + [ + integrationDetail.value.content.request_summary, + integrationDetail.value.content.response_summary, + integrationDetail.value.content.metadata + ].some(hasJsonContentWithoutIds) + ) + ) + const triggerSourceLabel = (source?: string | null) => + ({ polling: '定时轮询', webhook: '外部回调', manual: '人工触发' })[source || ''] || + source || + '-' + const triggerSceneLabel = (scene?: string | null) => + ({ network_polling: '网络状态轮询' })[scene || ''] || scene || '-' + const financeRecordSourceLabel = (source: string) => + financeRecordSourceLabels[source] || source || '-' + const financeWalletTypeLabel = (type?: 'agent_wallet' | 'asset_wallet') => + type === 'agent_wallet' ? '代理钱包' : type === 'asset_wallet' ? '资产钱包' : '-' + const actorDisplay = (item: AuditEventView) => { + const actor = item.actor_name || item.actor_id || '-' + const organizations = [ + item.actor_shop_name || (item.actor_shop_id ? `店铺 ID: ${item.actor_shop_id}` : ''), + item.actor_enterprise_name || + (item.actor_enterprise_id ? `企业 ID: ${item.actor_enterprise_id}` : '') + ].filter(Boolean) + return organizations.length ? `${actor}(${organizations.join(' · ')})` : actor + } + const scopeDisplay = (item: AuditEventView) => { + const scope = item.scope_name || item.scope_id + const type = auditScopeTypeLabels[item.scope_type] || item.scope_type + return scope ? `${type} · ${scope}` : type || '-' + } + const hasRequestContext = (item: AuditEventView) => + Boolean( + item.request_method || + item.request_path || + item.request_id || + item.correlation_id || + item.ip_address || + item.user_agent + ) + const hasBatchData = (item: AuditEventView) => + Boolean( + (item.batch_total || 0) > 0 || (item.success_count || 0) > 0 || (item.fail_count || 0) > 0 + ) + const resourceKey = (resource: AuditEventView['resources'][number]) => + [resource.resource_type, resource.resource_id || resource.resource_key, resource.sort_order] + .filter((value) => value !== null && value !== undefined && value !== '') + .join(':') const formatMoney = (amount: number, currency?: string | null) => { const value = (amount / 100).toFixed(2) return currency === 'CNY' || !currency ? `¥${value}` : `${value} ${currency}` } const load = async () => { if (!props.target) return + if ( + !canLoadAuditInvestigation(props.target, userStore.info.user_type, userStore.isSuperAdmin) + ) { + clear() + ElMessage.error('当前账号无权访问该审计调查') + return + } const sequence = ++loadSequence clear() loading.value = true @@ -430,10 +914,143 @@ color: var(--el-text-color-secondary); } + .node-tags { + display: inline-flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; + } + + .actor-event-summary { + margin-top: 12px; + } + + .actor-event-sections { + margin-top: 12px; + } + + .actor-resource-list { + display: grid; + gap: 12px; + } + + .actor-resource { + padding: 12px; + background: var(--el-fill-color-lighter); + border: 1px solid var(--el-border-color-lighter); + border-radius: 6px; + } + + .actor-resource__title { + display: flex; + gap: 8px; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; + } + + .resource-json-sections { + margin-top: 8px; + } + .json-section { margin-top: 20px; } + .integration-hero { + display: flex; + gap: 20px; + align-items: center; + justify-content: space-between; + padding: 18px 20px; + margin-bottom: 16px; + background: linear-gradient( + 135deg, + var(--el-color-primary-light-9), + var(--el-fill-color-light) + ); + border: 1px solid var(--el-color-primary-light-7); + border-radius: 8px; + } + + .integration-hero__eyebrow, + .integration-hero p, + .integration-hero__result span, + .content-block > span { + color: var(--el-text-color-secondary); + } + + .integration-hero h3 { + margin: 6px 0; + font-size: 18px; + color: var(--el-text-color-primary); + } + + .integration-hero p { + margin: 0; + font-size: 13px; + } + + .integration-hero__result { + display: grid; + justify-items: end; + gap: 5px; + text-align: right; + } + + .integration-hero__result strong { + font-size: 20px; + color: var(--el-text-color-primary); + } + + .integration-hero__result span { + font-size: 12px; + } + + .integration-detail-grid, + .integration-content-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + } + + .integration-panel { + padding: 16px; + margin-bottom: 12px; + background: var(--el-bg-color); + border: 1px solid var(--el-border-color-lighter); + border-radius: 8px; + } + + .integration-panel h4 { + margin: 0 0 12px; + font-size: 14px; + color: var(--el-text-color-primary); + } + + .integration-panel :deep(.el-descriptions__label) { + width: 76px; + color: var(--el-text-color-secondary); + } + + .integration-content { + margin-bottom: 0; + } + + .integration-content-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .content-block { + min-width: 0; + } + + .content-block > span { + display: block; + margin-bottom: 8px; + font-size: 13px; + } + .json-section h4 { margin: 0 0 8px; } @@ -448,6 +1065,24 @@ border-radius: 6px; } + @media (width <= 640px) { + .integration-hero, + .integration-detail-grid, + .integration-content-grid { + grid-template-columns: 1fr; + } + + .integration-hero { + align-items: flex-start; + flex-direction: column; + } + + .integration-hero__result { + justify-items: start; + text-align: left; + } + } + .pagination { display: flex; justify-content: flex-end; diff --git a/src/components/business/audit/AuditInvestigationLinks.vue b/src/components/business/audit/AuditInvestigationLinks.vue index 80230e9..b7d8eee 100644 --- a/src/components/business/audit/AuditInvestigationLinks.vue +++ b/src/components/business/audit/AuditInvestigationLinks.vue @@ -1,6 +1,5 @@
- + @@ -616,7 +607,6 @@ // Props interface BindingCard { - id: number card_id?: number iccid: string msisdn?: string @@ -747,14 +737,17 @@ const isSubjectActivityView = computed(() => [3, 4].includes(Number(userStore.info.user_type))) const auditTarget = computed(() => { const userType = Number(userStore.info.user_type) + const isCard = props.cardInfo.asset_type === 'card' const permission = userType === 3 - ? AUDIT_PERMISSIONS.agentActivity + ? isCard + ? AUDIT_PERMISSIONS.agentAssetInfoCardActivity + : AUDIT_PERMISSIONS.agentAssetInfoDeviceActivity : userType === 4 - ? AUDIT_PERMISSIONS.enterpriseActivity - : props.cardInfo.asset_type === 'card' - ? AUDIT_PERMISSIONS.cardEntry - : AUDIT_PERMISSIONS.deviceEntry + ? isCard + ? AUDIT_PERMISSIONS.enterpriseAssetInfoCardActivity + : AUDIT_PERMISSIONS.enterpriseAssetInfoDeviceActivity + : AUDIT_PERMISSIONS.assetInfoEntry if (!hasAuth(permission)) return null return resolveAuditResourceTarget({ userType, @@ -771,10 +764,10 @@ const userType = Number(userStore.info.user_type) const permission = userType === 3 - ? AUDIT_PERMISSIONS.agentActivity + ? AUDIT_PERMISSIONS.agentAssetInfoBindingCardActivity : userType === 4 - ? AUDIT_PERMISSIONS.enterpriseActivity - : AUDIT_PERMISSIONS.cardEntry + ? AUDIT_PERMISSIONS.enterpriseAssetInfoBindingCardActivity + : AUDIT_PERMISSIONS.assetInfoBindingCardEntry if (!hasAuth(permission)) return null return resolveAuditResourceTarget({ userType, @@ -783,25 +776,10 @@ businessIdentifier: card.iccid }) } - const getBindingAuditTarget = (card: BindingCard) => { - if ( - ![1, 2].includes(Number(userStore.info.user_type)) || - !hasAuth(AUDIT_PERMISSIONS.resourceTimeline) - ) - return null - return resolveAuditResourceTarget({ - resourceType: 'device_sim_binding', - internalId: card.id - }) - } const openBindingCardAudit = (card: BindingCard) => { const target = getBindingCardAuditTarget(card) if (target) openAuditInvestigation(target) } - const openBindingAudit = (card: BindingCard) => { - const target = getBindingAuditTarget(card) - if (target) openAuditInvestigation(target) - } const { width } = useWindowSize() const CARD_MONTH_USAGE_PERMISSION = 'asset_info:view_card_month_usage' diff --git a/src/views/asset-management/asset-information/components/CurrentPackageCard.vue b/src/views/asset-management/asset-information/components/CurrentPackageCard.vue index 8c5e152..dcf62b9 100644 --- a/src/views/asset-management/asset-information/components/CurrentPackageCard.vue +++ b/src/views/asset-management/asset-information/components/CurrentPackageCard.vue @@ -296,7 +296,7 @@ const descriptionsColumn = computed(() => { if (width.value <= 640) return 1 if (width.value <= 1024) return 2 - return 4 + return 3 }) const emit = defineEmits<{ diff --git a/src/views/asset-management/asset-information/components/WalletTransactionCard.vue b/src/views/asset-management/asset-information/components/WalletTransactionCard.vue index d8e6135..d20509f 100644 --- a/src/views/asset-management/asset-information/components/WalletTransactionCard.vue +++ b/src/views/asset-management/asset-information/components/WalletTransactionCard.vue @@ -45,15 +45,20 @@ 查询 重置 资金链路 资产审计 + isPlatformAuditAccount(userStore.info.user_type, userStore.isSuperAdmin) + ) const openWalletAudit = () => { const target = resolveFinanceAuditTarget('wallet_id', props.walletInfo?.wallet_id) diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue index c43ba5b..5f9da04 100644 --- a/src/views/asset-management/device-list/index.vue +++ b/src/views/asset-management/device-list/index.vue @@ -3161,9 +3161,9 @@ const userType = Number(userStore.getUserInfo.user_type) const auditPermission = userType === 3 - ? AUDIT_PERMISSIONS.agentActivity + ? AUDIT_PERMISSIONS.agentDeviceActivity : userType === 4 - ? AUDIT_PERMISSIONS.enterpriseActivity + ? AUDIT_PERMISSIONS.enterpriseDeviceActivity : AUDIT_PERMISSIONS.deviceEntry if (hasAuth(auditPermission)) { const auditTarget = resolveAuditResourceTarget({ diff --git a/src/views/asset-management/exchange-management/detail.vue b/src/views/asset-management/exchange-management/detail.vue index a9cc144..6724808 100644 --- a/src/views/asset-management/exchange-management/detail.vue +++ b/src/views/asset-management/exchange-management/detail.vue @@ -19,7 +19,7 @@ {{ userType === 3 ? '活动记录' : '换货审计' }} (0) const userType = computed(() => Number(userStore.info.user_type)) const exchangeAuditPermission = computed(() => - userType.value === 3 ? AUDIT_PERMISSIONS.agentActivity : AUDIT_PERMISSIONS.resourceTimeline + userType.value === 3 ? AUDIT_PERMISSIONS.agentExchangeActivity : AUDIT_PERMISSIONS.exchangeEntry ) const exchangeAuditTarget = computed(() => { - if (!exchangeDetail.value || userType.value === 4) return null + if (!exchangeDetail.value || ![1, 2, 3].includes(userType.value)) return null return resolveAuditResourceTarget({ userType: userType.value, resourceType: 'exchange_order', diff --git a/src/views/asset-management/exchange-management/index.vue b/src/views/asset-management/exchange-management/index.vue index 50f5b65..b1d663d 100644 --- a/src/views/asset-management/exchange-management/index.vue +++ b/src/views/asset-management/exchange-management/index.vue @@ -1555,8 +1555,8 @@ const flowType = row.flow_type || 'shipping' // 历史数据兼容 const userType = Number(userStore.info.user_type) const entryPermission = - userType === 3 ? AUDIT_PERMISSIONS.agentActivity : AUDIT_PERMISSIONS.resourceTimeline - if (userType !== 4 && hasAuth(entryPermission)) { + userType === 3 ? AUDIT_PERMISSIONS.agentExchangeActivity : AUDIT_PERMISSIONS.exchangeEntry + if ([1, 2, 3].includes(userType) && hasAuth(entryPermission)) { const exchangeTarget = resolveAuditResourceTarget({ userType, resourceType: 'exchange_order', @@ -1574,7 +1574,7 @@ resourceType: row.old_asset_type, internalId: row.old_asset_id }) - if (oldAssetTarget) + if (oldAssetTarget && hasAuth(AUDIT_PERMISSIONS.exchangeOldAssetEntry)) actions.push({ label: '旧资产审计', handler: () => openAuditInvestigation(oldAssetTarget), @@ -1584,7 +1584,7 @@ resourceType: row.new_asset_type || '', internalId: row.new_asset_id }) - if (newAssetTarget) + if (newAssetTarget && hasAuth(AUDIT_PERMISSIONS.exchangeNewAssetEntry)) actions.push({ label: '新资产审计', handler: () => openAuditInvestigation(newAssetTarget), diff --git a/src/views/asset-management/iot-card-management/index.vue b/src/views/asset-management/iot-card-management/index.vue index bb1d843..1abc21c 100644 --- a/src/views/asset-management/iot-card-management/index.vue +++ b/src/views/asset-management/iot-card-management/index.vue @@ -2315,9 +2315,9 @@ const userType = Number(userStore.getUserInfo.user_type) const auditPermission = userType === 3 - ? AUDIT_PERMISSIONS.agentActivity + ? AUDIT_PERMISSIONS.agentCardActivity : userType === 4 - ? AUDIT_PERMISSIONS.enterpriseActivity + ? AUDIT_PERMISSIONS.enterpriseCardActivity : AUDIT_PERMISSIONS.cardEntry if (hasAuth(auditPermission)) { const auditTarget = resolveAuditResourceTarget({ diff --git a/src/views/asset-management/record-management/asset-assign/detail.vue b/src/views/asset-management/record-management/asset-assign/detail.vue index d521a72..e7bccc3 100644 --- a/src/views/asset-management/record-management/asset-assign/detail.vue +++ b/src/views/asset-management/record-management/asset-assign/detail.vue @@ -19,7 +19,7 @@ {{ userType === 3 ? '活动记录' : '分配审计' }} (null) const userType = computed(() => Number(userStore.info.user_type)) const recordAuditPermission = computed(() => - userType.value === 3 ? AUDIT_PERMISSIONS.agentActivity : AUDIT_PERMISSIONS.resourceTimeline + userType.value === 3 + ? AUDIT_PERMISSIONS.agentAssetAllocationActivity + : AUDIT_PERMISSIONS.assetAllocationEntry ) const recordAuditTarget = computed(() => { - if (!detailData.value || userType.value === 4) return null + if (!detailData.value || ![1, 2, 3].includes(userType.value)) return null return resolveAuditResourceTarget({ userType: userType.value, resourceType: 'asset_allocation_record', diff --git a/src/views/asset-management/record-management/asset-assign/index.vue b/src/views/asset-management/record-management/asset-assign/index.vue index 5aa4f40..582b142 100644 --- a/src/views/asset-management/record-management/asset-assign/index.vue +++ b/src/views/asset-management/record-management/asset-assign/index.vue @@ -232,8 +232,10 @@ const actions: Array<{ label: string; handler: () => void; type: 'primary' }> = [] const userType = Number(userStore.info.user_type) const entryPermission = - userType === 3 ? AUDIT_PERMISSIONS.agentActivity : AUDIT_PERMISSIONS.resourceTimeline - if (!hasAuth(entryPermission) || userType === 4) return actions + userType === 3 + ? AUDIT_PERMISSIONS.agentAssetAllocationActivity + : AUDIT_PERMISSIONS.assetAllocationEntry + if (!hasAuth(entryPermission) || ![1, 2, 3].includes(userType)) return actions const recordTarget = resolveAuditResourceTarget({ userType, resourceType: 'asset_allocation_record', @@ -252,7 +254,7 @@ resourceType: row.asset_type, internalId: row.asset_id }) - if (assetTarget) + if (assetTarget && hasAuth(AUDIT_PERMISSIONS.assetAllocationAssetEntry)) actions.push({ label: '资产审计', handler: () => openAuditInvestigation(assetTarget), diff --git a/src/views/audit/events/detail.vue b/src/views/audit/events/detail.vue index e94940e..48b9a63 100644 --- a/src/views/audit/events/detail.vue +++ b/src/views/audit/events/detail.vue @@ -96,10 +96,7 @@ @@ -143,6 +140,7 @@