# Tasks: IoT 卡单卡管理与所有权模型重构

## 1. 模型重构（清理 Owner 字段）

- [x] 1.1 修改 IotCard 模型：移除 OwnerType、OwnerID 字段
- [x] 1.2 修改 Device 模型：移除 OwnerType、OwnerID 字段
- [x] 1.3 创建数据库迁移：删除 tb_iot_card 的 owner_type、owner_id 列
- [x] 1.4 创建数据库迁移：删除 tb_device 的 owner_type、owner_id 列
- [x] 1.5 更新相关 DTO：移除 OwnerType、OwnerID 相关字段
- [x] 1.6 更新相关 Service/Store：移除 Owner 相关逻辑，改用 ShopID

## 2. 导入任务模型

- [x] 2.1 创建 IotCardImportTask 模型
- [x] 2.2 创建数据库迁移：tb_iot_card_import_task 表
- [x] 2.3 创建 IotCardImportTaskStore
- [x] 2.4 创建导入任务相关 DTO

## 3. ICCID 校验逻辑

- [x] 3.1 在 pkg/validator 中添加 ICCID 校验函数
- [x] 3.2 实现根据运营商校验 ICCID 长度（电信19位，其他20位）
- [x] 3.3 支持字母数字混合校验（移动有字母）
- [x] 3.4 更新现有导入逻辑使用新校验函数

## 4. 单卡列表 API

- [x] 4.1 创建单卡列表查询 DTO（请求/响应）
- [x] 4.2 实现 IotCardStore.ListStandalone 方法（未绑定设备的卡）
- [x] 4.3 实现 IotCardService.ListStandalone 方法
- [x] 4.4 实现 IotCardHandler.ListStandalone 方法
- [x] 4.5 注册路由 GET /api/admin/iot-cards/standalone

## 5. 批量导入 API

- [x] 5.1 创建导入请求 DTO（含 CSV 文件上传）
- [x] 5.2 实现 CSV 解析逻辑
- [x] 5.3 实现 IotCardImportService.CreateImportTask 方法
- [x] 5.4 实现 IotCardImportHandler.Import 方法
- [x] 5.5 注册路由 POST /api/admin/iot-cards/import

## 6. 异步导入 Worker

- [x] 6.1 创建 IotCardImportTask Asynq 任务类型
- [x] 6.2 实现 IotCardImportHandler（Worker 处理器）
- [x] 6.3 实现分批处理逻辑（1000条/批）
- [x] 6.4 实现 ICCID 去重检查
- [x] 6.5 实现进度更新和结果记录

## 7. 导入任务查询 API

- [x] 7.1 创建导入任务列表查询 DTO
- [x] 7.2 创建导入任务详情 DTO
- [x] 7.3 实现 IotCardImportTaskService.List 方法
- [x] 7.4 实现 IotCardImportTaskService.GetByID 方法
- [x] 7.5 实现 IotCardImportTaskHandler.List 方法
- [x] 7.6 实现 IotCardImportTaskHandler.GetByID 方法
- [x] 7.7 注册路由 GET /api/admin/iot-cards/import-tasks
- [x] 7.8 注册路由 GET /api/admin/iot-cards/import-tasks/:id

## 8. 测试

- [x] 8.1 IotCardStore.ListStandalone 单元测试
- [x] 8.2 ICCID 校验函数单元测试
- [x] 8.3 CSV 解析逻辑单元测试
- [x] 8.4 导入 Worker 单元测试
- [x] 8.5 单卡列表 API 集成测试
- [x] 8.6 批量导入 API 集成测试

## 9. 文档和规范更新

- [x] 9.1 更新 iot-card/spec.md（同步 delta 变更）
- [x] 9.2 更新 iot-device/spec.md（同步 delta 变更）
- [x] 9.3 创建 iot-card-import-task/spec.md
- [x] 9.4 更新 API 文档（通过 openspec archive 自动完成）
