## MODIFIED Requirements

### Requirement: Excel 文件格式规范

系统 SHALL 要求 Excel 文件必须包含 ICCID、MSISDN、虚拟号三列，按固定列位置读取，表头行内容不影响解析结果。

**文件格式要求**:
- **文件格式**: 仅支持 `.xlsx` (Excel 2007+)
- **Sheet**: 读取第一个sheet，或优先读取名为"导入数据"的sheet
- **表头行**: 第1行固定为表头，永远跳过，内容不限（中文、英文均可）
- **列位置（固定，不可变）**:
  - 第1列（索引0）: ICCID
  - 第2列（索引1）: MSISDN
  - 第3列（索引2）: 虚拟号（必填）
- **列格式**: 应设置为文本格式（避免长数字被转为科学记数法）

**解析规则**:
- 按列索引取值，不识别列名，第一行永远跳过
- 自动去除单元格首尾空格
- 若 ICCID、MSISDN、virtual_no 三列皆为空，视为空行跳过，不计入 total，不计入失败
- ICCID 为空（但其他列非空）的行记录为失败，失败原因为"ICCID 不能为空"
- MSISDN 为空（但其他列非空）的行记录为失败，失败原因为"MSISDN 不能为空"
- virtual_no（第3列）为空的行记录为失败，失败原因为"虚拟号(virtual_no)不能为空"

**virtual_no 导入规则**:
- virtual_no 为必填，为空则该行记录为失败
- virtual_no 全局唯一（跨卡和设备），重复则失败，原因为"虚拟号已被占用: <值>"

#### Scenario: 正常导入（ICCID 和 VirtualNo 均有值）

- **WHEN** Excel 中某行第1列 ICCID="898600XXXXX"，第3列 virtual_no="CARD-001"，第2列 msisdn="13800000001"
- **THEN** 导入成功，卡记录写入数据库，VirtualNo 和 ICCID 同步注册到 `tb_asset_identifier`

#### Scenario: 中文表头正常导入

- **GIVEN** Excel 文件第1行表头为 `ICCID | 接入号 | 虚拟号`（任意中文或英文内容）
- **WHEN** 系统解析该 Excel 文件
- **THEN** 系统跳过第1行，从第2行开始按列位置解析数据

#### Scenario: VirtualNo 为空的行被拒绝

- **WHEN** Excel 中某行第3列为空
- **THEN** 该行计入失败，失败原因为"虚拟号(virtual_no)不能为空"
- **THEN** 其他合法行继续导入，不因此行中断

#### Scenario: VirtualNo 重复被拒绝

- **WHEN** Excel 中某行第3列的值与已有卡/设备的 VirtualNo 重复（跨表）
- **THEN** 该行计入失败，原因为"虚拟号已被占用: <值>"

#### Scenario: MSISDN 为空的行记录失败

- **WHEN** Excel 中某行第2列为空
- **THEN** 该行标记为失败，原因为"MSISDN 不能为空"

#### Scenario: 长数字无损解析

- **GIVEN** Excel 文件中第1列设置为文本格式，包含 20 位数字 "89860012345678901234"
- **WHEN** 系统解析该 Excel 文件
- **THEN** ICCID 完整保留为 "89860012345678901234"，无精度损失，无科学记数法

#### Scenario: 拒绝非Excel格式文件

- **GIVEN** 上传文件扩展名为 .csv
- **WHEN** 系统尝试解析该文件
- **THEN** 系统返回错误"不支持的文件格式 .csv，请上传Excel文件(.xlsx)"

---

## ADDED Requirements

### Requirement: 导入批次支持卡业务类型

系统 SHALL 在 IoT 卡导入请求中支持 `card_category` 参数，以批次为单位指定导入卡的业务类型，默认为普通卡。

**请求字段**:
- `card_category`: 卡业务类型（枚举：`normal` / `industry`，可选，默认 `normal`）

**任务字段**:
- `IotCardImportTask` 新增 `card_category` 字段（VARCHAR(20)，默认 `normal`）

**导入行为**:
- 导入任务中所有卡统一使用 `card_category` 的值，不支持同一批次混用

#### Scenario: 不传 card_category 时默认为普通卡

- **WHEN** 导入请求未包含 `card_category` 字段
- **THEN** 导入的所有卡 `card_category` 为 `normal`

#### Scenario: 指定行业卡导入

- **WHEN** 导入请求中 `card_category` = `industry`
- **THEN** 该批次导入的所有卡 `card_category` 均为 `industry`

#### Scenario: 传入无效 card_category

- **WHEN** 导入请求中 `card_category` = `unknown`（非枚举值）
- **THEN** 系统返回参数校验错误，提示卡业务类型无效
