## MODIFIED Requirements

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

系统 SHALL 要求 Excel 文件必须包含 ICCID 和 MSISDN 两列，并支持可选的 `virtual_no` 列。

**文件格式要求**:
- **文件格式**: 仅支持 `.xlsx` (Excel 2007+)
- **Sheet**: 读取第一个sheet，或优先读取名为"导入数据"的sheet
- **表头行**: 第1行（可选，但建议包含）
- **表头识别关键字**:
  - ICCID 列: iccid/ICCID/卡号/号码
  - MSISDN 列: msisdn/MSISDN/接入号/手机号/电话/号码
  - virtual_no 列（新增，可选）: virtual_no/VirtualNo/虚拟号/设备号
- **列数要求**: 至少 2 列（ICCID 和 MSISDN），virtual_no 为可选第三列
- **列格式**: 应设置为文本格式（避免长数字被转为科学记数法）

**解析规则**:
- 自动检测表头（第1行包含识别关键字则跳过）
- 自动去除单元格首尾空格
- 跳过空行
- ICCID 为空的行记录为失败
- MSISDN 为空的行记录为失败
- virtual_no 为空的行：跳过该列（不填入，保留原值）

**virtual_no 导入规则（只补空白）**:
- 该行 virtual_no 不为空 + 数据库当前值为 NULL：填入新值
- 该行 virtual_no 不为空 + 数据库当前值已有值：跳过（不覆盖）
- **批次级唯一性检查**：在执行导入前，先检查整批中所有非空 virtual_no 是否与数据库现存值重复；有任意冲突则**整批失败**，响应中返回冲突的 virtual_no 及行号列表

**示例 Excel 内容**:
```
| ICCID                | MSISDN      | 虚拟号    |
|----------------------|-------------|-----------|
| 89860012345678901234 | 13800000001 | CARD-001  |
| 89860012345678901235 | 13800000002 |           |
```

#### Scenario: 解析标准双列 Excel 文件（无 virtual_no 列）

- **WHEN** Excel 文件只含 ICCID 和 MSISDN 两列，无虚拟号列
- **THEN** 解析结果包含 2 条有效记录，virtual_no 字段为空，不影响导入逻辑

#### Scenario: 解析含 virtual_no 列的三列 Excel

- **WHEN** Excel 文件含 ICCID、MSISDN、虚拟号三列，某行 virtual_no = "CARD-001"，对应卡当前 virtual_no 为 NULL
- **THEN** 解析后该卡的 virtual_no 填入 "CARD-001"

#### Scenario: virtual_no 已有值时不覆盖

- **WHEN** Excel 中某行 virtual_no = "CARD-NEW"，但该卡数据库中已有 virtual_no = "CARD-OLD"
- **THEN** 该卡的 virtual_no 保持 "CARD-OLD" 不变，该行跳过（不报错，不计入失败）

#### Scenario: 批次中有 virtual_no 与现存数据重复

- **WHEN** Excel 中某行 virtual_no = "CARD-001"，但数据库中另一张卡已有 virtual_no = "CARD-001"
- **THEN** 系统拒绝整批导入，响应返回冲突的 virtual_no 值和行号，提示"虚拟号重复，整批导入已终止"

#### Scenario: 支持中文表头

- **GIVEN** Excel 文件表头为 `卡号 | 接入号 | 虚拟号`
- **WHEN** 系统解析该 Excel 文件
- **THEN** 系统正确识别三列，按规则处理 virtual_no

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

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

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

- **GIVEN** Excel 文件第二行 MSISDN 为空
- **WHEN** 系统解析该 Excel 文件
- **THEN** 第一条记录解析成功，第二条记录标记为失败，原因为"MSISDN 不能为空"

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

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