## 1. Excel 按列位置读取（IoT 卡）

- [x] 1.1 重写 `parseCardRows`：移除 `findCardColumns` 调用，第1行固定跳过，按 col0=ICCID、col1=MSISDN、col2=VirtualNo 取值
- [x] 1.2 保留空行跳过逻辑：若 ICCID、MSISDN、VirtualNo 三列皆为空，`continue` 且不计入 total（与原实现一致，避免尾部空行被判失败）
- [x] 1.3 对齐失败原因文案，与 spec 的 Scenario 完全一致（"ICCID 不能为空"/"MSISDN 不能为空"/"虚拟号(virtual_no)不能为空"，注意半角空格）
- [x] 1.4 删除 `findCardColumns` 函数

## 2. Excel 按列位置读取（设备）

- [x] 2.1 重写 `ParseDeviceExcel`：移除 `buildDeviceColumnIndex` 调用，第1行固定跳过，按确认列顺序取值（col0=虚拟号、col1=设备名称、col2=设备型号、col3=设备类型、col4=IMEI、col5=制造商、col6=最大SIM槽数、col7~10=卡1~4）
- [x] 2.2 保留空行跳过逻辑：若整行目标列皆为空，`continue` 且不计入 total
- [x] 2.3 `MaxSimSlots` 范围校验：读取后若不在 [1,4]，计入失败（原因："最大SIM槽数必须在1-4之间"）；为空/0 时回填默认值 4
- [x] 2.4 删除 `buildDeviceColumnIndex` 函数

## 3. IoT 卡导入支持卡业务类型

- [x] 3.1 `ImportIotCardRequest` DTO 新增 `CardCategory string` 字段，tag：`json:"card_category" validate:"omitempty,oneof=normal industry" description:"卡业务类型 (normal:普通卡, industry:行业卡)，默认 normal"`
- [x] 3.2 `IotCardImportTask` model 新增 `CardCategory` 字段，gorm tag：`column:card_category;type:varchar(20);default:normal;not null;comment:卡业务类型(normal/industry)`
- [x] 3.3 创建数据库迁移 `000111_add_card_category_to_import_task`（up/down 成对），为 `tb_iot_card_import_task` 新增 `card_category VARCHAR(20) NOT NULL DEFAULT 'normal'` 列并加 COMMENT
- [x] 3.4 修改 `internal/service/iot_card_import/service.go CreateImportTask`：构建 `IotCardImportTask` 时写入 `CardCategory`，空串时兜底为 `constants.CardCategoryNormal`
- [x] 3.5 修改 `internal/task/iot_card_import.go` 的 `processBatch`：将写死的 `CardCategory: constants.CardCategoryNormal` 改为 `task.CardCategory`
- [x] 3.6 更新 `ImportTaskResponse` DTO 增加 `card_category` 字段返回，便于列表/详情查询时展示批次类型

## 4. 模板文件同步

- [x] 4.1 更新仓库内（若有）的 IoT 卡、设备导入模板 `.xlsx`，使列顺序与新实现完全一致；若模板由前端维护，在提案交接时通知前端同步更新下载模板

## 5. 手动验证

- [ ] 5.1 准备中文表头的 IoT 卡样例 xlsx（含正常行、空行、ICCID/MSISDN/虚拟号为空行、虚拟号重复行），调用 `POST /api/admin/iot-cards/import` 指定 `card_category=industry`，用 PostgreSQL MCP 验证 `tb_iot_card_import_task.card_category='industry'` 且新增卡的 `tb_iot_card.card_category='industry'`
- [ ] 5.2 准备中文表头的设备样例 xlsx（含 IMEI 在第5列），调用 `POST /api/admin/devices/import`，验证成功/失败行数与失败原因文案
- [ ] 5.3 不传 `card_category` 时验证默认值为 `normal`
- [ ] 5.4 传入 `card_category=invalid` 时验证返回参数校验错误
