# Design: 后台任务页与操作审计日志文件下载入口

## Context

当前项目已经具备对象存储上传与下载地址获取能力：

- `StorageService.getUploadUrl()` 已用于上传导入文件
- `StorageService.batchDownloadUrls()` 已封装 `POST /api/admin/storage/batch-download-urls`

但在后台实际业务入口上，下载链路还没有被接起来：

- IoT 卡任务页和设备任务页只有“失败数据”等操作，没有“下载原始导入文件”
- 共享的资产操作审计日志组件虽然会显示 `文件存储键`，但只能看见 `file_key` 字符串，不能直接下载

此外，操作审计日志组件是共享组件，同时用于：

- 资产详情页
- IoT 卡列表中的操作审计日志弹窗
- 设备列表中的操作审计日志弹窗

这意味着“下载文件”能力既要复用统一实现，又要允许三个入口使用不同权限编码。

## Goals / Non-Goals

- Goals:
  - 为 IoT 卡任务页增加原始导入文件下载入口
  - 为设备任务页增加原始导入文件下载入口
  - 为三个后台审计日志入口增加基于 `file_key` 的内联下载入口
  - 为以上 5 个入口分别施加独立权限控制
  - 复用现有 `batch-download-urls` 接口完成下载
- Non-Goals:
  - 不改造对象存储上传流程
  - 不增加批量下载多个文件的 UI
  - 不扩展到 C 端页面
  - 不在本次提案中统一所有历史权限命名差异

## Decisions

- Decision: 所有“下载文件”入口统一通过 `POST /api/admin/storage/batch-download-urls` 获取下载 URL

  - Rationale: 现有基础设施已经覆盖私有文件下载，不需要新增独立接口；单文件下载可直接以 `file_keys: [file_key]` 方式复用批量接口。

- Decision: 任务页“下载文件”继续放在现有操作列中，而不是新增文件列

  - Rationale: IoT 卡任务页和设备任务页已经有行级操作模式；把下载文件放入操作列，和“失败数据”等现有动作一致，改动面更小。

- Decision: 审计日志中的“下载文件”必须紧跟在 `文件存储键` / `file_key` 值后，而不是放到日志行外层操作区

  - Rationale: 需求明确指出“如果变更内容出现了文件存储键，后面也要跟上下载文件”，因此入口应与该字段形成强绑定。

- Decision: 共享 `OperationLogsCard` 作为唯一实现，但下载权限由调用入口传入

  - Rationale: 三个审计日志入口复用同一组件，如果把权限写死在组件内部，就无法满足“3 个不同权限编码”的要求。

- Decision: 本提案默认新增权限编码建议如下

  - 任务页：
    - `iot_card_task:download_file`
    - `device_task:download_file`
  - 审计日志入口：
    - `asset_info:download_log_file`
    - `iot_card:download_log_file`
    - `device:download_log_file`
  - Rationale: 任务页下载权限与现有同页行级动作前缀保持一致；日志下载权限与各自入口页面前缀保持一致，便于角色配置时理解来源。

- Decision: 不在本次变更中顺带统一 IoT 卡任务页历史上的 `lot_task:*` / `iot_card_task:*` 前缀差异

  - Rationale: 当前需求只要求增加下载文件权限入口；若同时做权限前缀清理，会把提案扩大为权限命名治理，超出本次范围。

- Decision: 当 `file_key` 缺失、为空或下载 URL 获取失败时，页面只提示失败，不影响列表或日志正常渲染

  - Rationale: 下载属于增强能力，不应把原有任务列表和审计日志变成硬依赖下载服务的阻塞页面。

## Risks / Trade-offs

- 任务列表前端类型当前未显式声明 `file_key`，实施时若接口已返回但类型未补齐，容易出现“功能依赖字段存在但类型缺口”的问题。
- `OperationLogsCard` 的 props 和渲染逻辑一旦调整，会同时影响资产详情页、IoT 卡列表弹窗和设备列表弹窗，需要联动验证。
- IoT 卡任务页现存 `lot_task:bulk_import` 与 `iot_card_task:*` 并存，新增权限虽可落地，但会继续保留命名历史包袱。

## Migration Plan

1. 明确导入任务记录的 `file_key` 契约，并补齐前端类型。
2. 在 IoT 卡任务页和设备任务页的操作列增加“下载文件”。
3. 为两个任务页接入各自独立的下载权限编码。
4. 扩展共享 `OperationLogsCard`，在 `file_key` 场景渲染内联“下载文件”入口。
5. 由资产详情页、IoT 卡列表、设备列表分别传入对应的日志下载权限编码。
6. 联调验证 5 个入口的权限隔离与下载行为。

## Open Questions

- IoT 卡任务新增下载权限默认建议使用 `iot_card_task:download_file`。如果后端权限树仍希望完全沿用 `lot_task:*` 前缀，需要在评审阶段确认是否改为 `lot_task:download_file`。
- 当前导入任务列表接口是否已经返回 `file_key` 需要评审确认；若未返回，则实施范围需要同步包含接口契约补齐。
