问题
AsterDrive 当前已经提供三类审计查询入口,但都只返回分页 JSON:
- 用户侧团队管理:
GET /api/v1/teams/{id}/audit-logs,团队 owner/admin 查看自己有权管理的团队审计。
- 管理员团队详情:
GET /api/v1/admin/teams/{id}/audit-logs,系统管理员查看指定团队审计。
- 管理员系统审计:
GET /api/v1/admin/audit-logs,系统管理员查看全站审计。
团队 owner/admin 和系统管理员需要把筛选后的审计记录交给合规、故障排查或离线分析流程。当前只能逐页复制界面,难以稳定导出完整结果,也容易遗漏分页、排序和筛选条件。前端不应把分页 JSON 在浏览器里拼成 CSV:大日志量会占用大量内存,且难以形成与权限边界一致的服务端结果集。
目标
在保持现有 JSON 列表接口兼容的前提下,增加服务端生成并下载 CSV 的能力,覆盖用户侧团队审计、管理员团队审计和管理员系统审计。导出结果必须是当前调用者在当前筛选条件下可见的同一批审计记录,使用导出入口不得扩大数据范围。
建议增加独立的导出路由,避免让普通列表响应根据 query 参数隐式改变媒体类型:
GET /api/v1/teams/{id}/audit-logs/export
GET /api/v1/admin/teams/{id}/audit-logs/export
GET /api/v1/admin/audit-logs/export
三个入口复用各自现有列表的过滤参数:
user_id
action
entity_type
entity_id
after
before
- 系统审计额外复用现有
sort_by / sort_order
团队导出沿用团队查询强制的 entity_type=team 和 entity_id=团队 ID,调用者身份仍由现有 service 权限检查决定。不要新增前端根据角色或团队成员列表推断权限的逻辑。
后端与 API 设计
结果与流式行为
- 返回
200 OK、Content-Type: text/csv; charset=utf-8 和 Content-Disposition: attachment,文件名包含导出类型、团队标识(如适用)和 UTC 时间戳;文件名必须经过安全规范化。
- 采用服务端流式/游标式读取,禁止先把全部行收集到
Vec。设置明确的单次导出上限或可配置上限,并在超限时返回稳定错误;上限和错误文案写入 API 文档。
- 导出顺序必须稳定。系统审计按现有
sort_by / sort_order 工作,并用 id 作为同值二级排序;团队审计至少使用 created_at + id 的确定性排序。
- 查询开始前刷新异步审计写入管理器,确保已经完成的审计事件进入结果;导出过程中不得为了生成 CSV 改变审计保留策略。
- 任何数据库、流、取消或客户端断开错误都要记录并结束响应,禁止静默吞错或返回半截成功状态而不留诊断。
CSV 契约
固定、可文档化的列顺序,至少包含:
id,created_at,actor_user_id,actor_username,action,entity_type,entity_id,entity_name,detail,ip_address,user_agent
团队审计可以在同一契约中追加或明确空值列:
member_user_id,member_username,role,previous_role,next_role
- 所有单元格按 RFC 4180 风格正确转义逗号、双引号、换行和 Unicode;空值保持为空,不把
null 写成误导性的字符串。
created_at 使用 UTC RFC 3339;枚举值使用稳定 API 名称,禁止把当前语言的展示文案写成数据契约。
detail 使用结构化审计详情的稳定 JSON 字符串或明确的扁平化字段,必须保证每行只有固定列数;不得把带换行的 presentation 文案直接拼入未转义单元格。
- 密码、token、MFA secret、外部认证凭据、存储 secret 等敏感值排除在导出之外。对已有
details 中可能出现的敏感字段建立服务端白名单/脱敏规则,禁止只依赖前端隐藏列。
- 默认使用 UTF-8;如为桌面表格兼容增加 BOM,必须在 API 文档和测试中固定行为。
权限、审计与一致性
- 用户侧导出必须和
list_team_audit_entries 使用同一团队存在性、成员角色和 can_manage_team() 检查;普通成员、已移除成员和越权团队 ID 均返回现有 403/404 语义。
- 管理员两个导出入口必须复用现有管理员 guard;禁止因为导出是流响应而绕过 admin 权限或隐藏团队检查。
- 导出本身建议记录独立审计动作(包含导出类型、团队 ID、筛选摘要和行数/截断结果),但禁止把完整筛选内容、CSV 内容或任何 secret 写进审计详情;需要避免导出动作递归污染本次结果的定义。
- 同一个请求中的权限判定、筛选条件和数据读取必须来自服务端权威状态。reader 数据库可用于纯读,但要明确快照/滞后边界,禁止依赖前端缓存。
- 记录空结果导出、达到上限和取消/失败,便于管理员区分“没有事件”和“导出失败”。
前端行为
用户侧团队审计
- 在团队管理的审计页提供带下载图标的“导出 CSV”按钮,仅在当前用户可访问审计 tab 时显示。
- 导出携带当前团队和当前筛选条件;下载期间显示 loading/禁用重复点击,成功后恢复,可处理 401/403、上限和网络错误并给出本地化提示。
- 保持现有分页、空状态、审计详情展示和 URL 查询状态;导出是完整筛选结果,不只导出当前页。
管理员侧
- 在系统审计页提供导出按钮,带上当前 action/entity type/时间/用户等筛选和排序条件。
- 在管理员团队详情的审计页提供同样入口,导出指定团队的完整筛选结果。
- 统一通过 service/resource 层发起二进制下载,组件不直接拼
/api、credentials、blob URL 或 Content-Disposition 解析;下载策略、文件名解析、错误映射和 blob URL 释放集中实现并可测试。
- 英文和中文文案、无权限/空结果/超限/失败状态完整覆盖;按钮使用现有 Icon 组件并提供可访问名称。
验收标准
相关代码
src/api/routes/teams.rs
src/api/routes/admin/teams.rs
src/api/routes/admin/audit_logs.rs
src/services/workspace/team/mod.rs
src/services/ops/audit/filters.rs
src/services/ops/audit/query.rs
src/services/ops/audit/models.rs
src/db/repository/audit_log_repo.rs
frontend-panel/src/services/teamService.ts
frontend-panel/src/services/adminService.ts
frontend-panel/src/services/auditService.ts
frontend-panel/src/components/settings/team-manage-detail/TeamManageAuditSection.tsx
frontend-panel/src/components/admin/admin-team-detail/AdminTeamDetailAuditSection.tsx
frontend-panel/src/pages/admin/AdminAuditPage.tsx
frontend-panel/src/services/http.ts
非目标
- 不在本 issue 重做审计事件记录矩阵、保留策略、presentation 国际化或团队文件活动的定义。
- 不导出审计以外的文件、分享、用户、配置或存储报表;不新增异步报表任务、邮件投递或云端归档。
- 不让前端合并分页数据或维护一套独立于后端的 CSV 字段/权限矩阵。
Checklist
问题
AsterDrive 当前已经提供三类审计查询入口,但都只返回分页 JSON:
GET /api/v1/teams/{id}/audit-logs,团队 owner/admin 查看自己有权管理的团队审计。GET /api/v1/admin/teams/{id}/audit-logs,系统管理员查看指定团队审计。GET /api/v1/admin/audit-logs,系统管理员查看全站审计。团队 owner/admin 和系统管理员需要把筛选后的审计记录交给合规、故障排查或离线分析流程。当前只能逐页复制界面,难以稳定导出完整结果,也容易遗漏分页、排序和筛选条件。前端不应把分页 JSON 在浏览器里拼成 CSV:大日志量会占用大量内存,且难以形成与权限边界一致的服务端结果集。
目标
在保持现有 JSON 列表接口兼容的前提下,增加服务端生成并下载 CSV 的能力,覆盖用户侧团队审计、管理员团队审计和管理员系统审计。导出结果必须是当前调用者在当前筛选条件下可见的同一批审计记录,使用导出入口不得扩大数据范围。
建议增加独立的导出路由,避免让普通列表响应根据 query 参数隐式改变媒体类型:
三个入口复用各自现有列表的过滤参数:
user_idactionentity_typeentity_idafterbeforesort_by/sort_order团队导出沿用团队查询强制的
entity_type=team和entity_id=团队 ID,调用者身份仍由现有 service 权限检查决定。不要新增前端根据角色或团队成员列表推断权限的逻辑。后端与 API 设计
结果与流式行为
200 OK、Content-Type: text/csv; charset=utf-8和Content-Disposition: attachment,文件名包含导出类型、团队标识(如适用)和 UTC 时间戳;文件名必须经过安全规范化。Vec。设置明确的单次导出上限或可配置上限,并在超限时返回稳定错误;上限和错误文案写入 API 文档。sort_by/sort_order工作,并用id作为同值二级排序;团队审计至少使用created_at+id的确定性排序。CSV 契约
固定、可文档化的列顺序,至少包含:
团队审计可以在同一契约中追加或明确空值列:
null写成误导性的字符串。created_at使用 UTC RFC 3339;枚举值使用稳定 API 名称,禁止把当前语言的展示文案写成数据契约。detail使用结构化审计详情的稳定 JSON 字符串或明确的扁平化字段,必须保证每行只有固定列数;不得把带换行的 presentation 文案直接拼入未转义单元格。details中可能出现的敏感字段建立服务端白名单/脱敏规则,禁止只依赖前端隐藏列。权限、审计与一致性
list_team_audit_entries使用同一团队存在性、成员角色和can_manage_team()检查;普通成员、已移除成员和越权团队 ID 均返回现有 403/404 语义。前端行为
用户侧团队审计
管理员侧
/api、credentials、blob URL 或Content-Disposition解析;下载策略、文件名解析、错误映射和 blob URL 释放集中实现并可测试。验收标准
git diff --check。相关代码
src/api/routes/teams.rssrc/api/routes/admin/teams.rssrc/api/routes/admin/audit_logs.rssrc/services/workspace/team/mod.rssrc/services/ops/audit/filters.rssrc/services/ops/audit/query.rssrc/services/ops/audit/models.rssrc/db/repository/audit_log_repo.rsfrontend-panel/src/services/teamService.tsfrontend-panel/src/services/adminService.tsfrontend-panel/src/services/auditService.tsfrontend-panel/src/components/settings/team-manage-detail/TeamManageAuditSection.tsxfrontend-panel/src/components/admin/admin-team-detail/AdminTeamDetailAuditSection.tsxfrontend-panel/src/pages/admin/AdminAuditPage.tsxfrontend-panel/src/services/http.ts非目标
Checklist