# 统一导出任务功能总结

## 1. 功能概览

本次交付新增统一导出任务系统，提供全局任务入口，支持：

- 创建导出任务（`scene=device/iot_card`，`format=xlsx/csv`）
- 导出任务列表与详情
- 导出任务取消（待处理/处理中）
- 异步三段式执行：`dispatch -> shard -> finalize`
- 分片产物与最终产物上传 OSS
- 详情接口直出下载链接（固定 24 小时）

## 2. 新增接口

- `POST /api/admin/export-tasks`：创建导出任务
- `GET /api/admin/export-tasks`：导出任务列表（支持 `scene/status/time` 过滤）
- `GET /api/admin/export-tasks/:id`：导出任务详情（已完成时返回 `download_url`）
- `POST /api/admin/export-tasks/:id/cancel`：取消导出任务

## 3. 状态机

### 3.1 主任务状态

- `1` 待处理
- `2` 处理中
- `3` 已完成
- `4` 已失败
- `5` 已取消

### 3.2 分片状态

- `1` 待处理
- `2` 处理中
- `3` 已成功
- `4` 已失败
- `5` 已取消

## 4. 核心设计

### 4.1 模型与迁移

新增两张表：

- `tb_export_task`：主任务
- `tb_export_shard_task`：分片任务

关键点：

- 无外键，仅使用 `task_id` 做逻辑关联
- 主任务保留进度、分片统计、权限快照、产物信息
- 分片任务保留游标范围、分片产物、执行状态

### 4.2 场景策略注册中心

新增 `internal/exporter` 场景注册中心与策略接口，首批接入：

- `device` 场景策略
- `iot_card` 场景策略

统一定义：

- `Headers()`：导出表头
- `NextShardBoundary()`：按 keyset 方式计算分片边界
- `QueryRows()`：按分片边界查询行数据

### 4.3 执行链路

- `export:dispatch`：构建分片并入队 `export:shard`
- `export:shard`：按分片查询数据、写文件、上传 OSS、回写分片结果
- `export:finalize`：汇总分片结果并生成最终文件，更新主任务状态

### 4.4 幂等与重试安全

- dispatch/shard/finalize 增加 Redis 锁，避免并发重复执行
- 分片状态条件更新，重复消费不会重复计数
- shard/finalize 利用 Asynq 重试上下文，在最终重试失败时才落库失败状态

### 4.5 取消语义

- 待处理：直接更新为已取消
- 处理中：仅设置 `cancel_requested=true`，由 Worker 检查点协作停止
- dispatch/shard/finalize 均增加取消检查点

### 4.6 下载链接

- 仅 `status=已完成` 返回 `download_url`
- 链接有效期固定 `24h`（常量 `ExportDownloadURLExpire`）
- URL 由详情接口实时生成，过期后再次访问可刷新

## 5. 权限与可见性

- 平台/超管：可见全部导出任务
- 代理：仅可见其店铺范围内创建的导出任务
- 详情越权统一返回：`无权限操作该资源或资源不存在`

## 6. 文档与生成器更新

已同步更新：

- 路由文档（新增 `导出任务` Tag 的 4 个接口）
- 文档构建 Handler 集合（`pkg/openapi/handlers.go` 新增 `ExportTask`）
- OpenAPI 文件重新生成：`docs/admin-openapi.yaml`
