# 通用对象存储能力

## Why

当前 ICCID 导入功能通过后端接收上传文件，占用服务器带宽和内存，大文件处理效率低。同时，未来的导出功能也需要文件存储能力。需要接入联通云对象存储（S3 兼容），采用预签名 URL 方案实现前端直传，提升性能和安全性。

此外，本项目作为公司后端模板项目，对象存储应设计为通用能力，方便复用到其他系统。

## What Changes

- **新增通用对象存储包**：`pkg/storage/` 提供 S3 兼容的对象存储能力
  - 支持上传、下载、删除、检查存在性
  - 支持生成预签名上传/下载 URL
  - 可扩展支持多云（阿里云、腾讯云等，仅需更换 Endpoint）

- **新增存储 API 接口**：供前端获取预签名上传 URL
  - `POST /api/admin/storage/upload-url`：获取上传预签名 URL

- **改造 ICCID 导入流程**：
  - 移除原有的文件上传处理（`c.FormFile`）
  - 改为接收 `file_key` 参数（对象存储路径）
  - Worker 从对象存储下载文件后处理
  - 处理完成后删除本地临时文件

- **数据模型变更**：
  - `IotCardImportTask` 新增 `storage_bucket`、`storage_key` 字段
  - 需要数据库迁移

- **配置结构扩展**：
  - 新增 `storage` 配置块（endpoint、region、bucket、credentials）

## Capabilities

### New Capabilities

- `object-storage`: 通用对象存储能力，提供 S3 兼容的文件上传、下载、删除、预签名 URL 生成功能

### Modified Capabilities

- `iot-card-import`: ICCID 导入流程改造，从直接上传改为对象存储集成

## Impact

### 代码变更

| 层级 | 变更内容 |
|------|----------|
| `pkg/storage/` | 新增：Provider 接口、S3 实现、配置类型 |
| `pkg/config/` | 修改：新增 StorageConfig 结构 |
| `configs/` | 修改：新增 storage 配置块 |
| `internal/bootstrap/` | 修改：初始化 Storage Provider |
| `internal/handler/admin/` | 新增：StorageHandler（获取上传 URL）|
| `internal/handler/admin/` | 修改：IotCardImportHandler（移除文件上传）|
| `internal/service/iot_card_import/` | 修改：接收 file_key 而非文件流 |
| `internal/task/` | 修改：从对象存储下载文件处理 |
| `internal/model/` | 修改：IotCardImportTask 新增字段 |
| `internal/routes/` | 修改：新增 storage 路由 |
| `migrations/` | 新增：添加 storage 字段迁移 |

### API 变更

| 接口 | 变更类型 | 说明 |
|------|----------|------|
| `POST /api/admin/storage/upload-url` | 新增 | 获取预签名上传 URL |
| `POST /api/admin/iot-cards/import` | **BREAKING** | 移除文件上传，改为传 file_key |

### 依赖变更

| 依赖 | 说明 |
|------|------|
| `github.com/aws/aws-sdk-go` | 新增：AWS S3 兼容 SDK（已验证联通云支持） |

### 配置变更

```yaml
storage:
  provider: "s3"
  s3:
    endpoint: "http://obs-helf.cucloud.cn"
    region: "cn-langfang-2"
    bucket: "cmp"
    access_key_id: "${OSS_ACCESS_KEY_ID}"
    secret_access_key: "${OSS_SECRET_ACCESS_KEY}"
    use_ssl: false
    path_style: true
  presign:
    upload_expires: "15m"    # 上传预签名有效期
    download_expires: "24h"  # 下载预签名有效期
  temp_dir: "/tmp/junhong-storage"  # 临时文件目录
```

### 前端适配

前端需要配合修改上传流程：
1. 先调用 `POST /api/admin/storage/upload-url` 获取预签名 URL
2. 直接 PUT 到预签名 URL 上传文件
3. 上传成功后调用导入接口，传入 `file_key`
