## MODIFIED Requirements

### Requirement: 导入任务实体定义

系统 SHALL 定义 IoT 卡导入任务(IotCardImportTask)实体,用于跟踪 IoT 卡批量导入的进度和结果。

**实体字段**:

**任务信息**:
- `id`: 任务 ID(主键,BIGINT)
- `task_no`: 任务编号(VARCHAR(50),唯一,格式: IMP-YYYYMMDD-XXXXXX)
- `status`: 任务状态(INT,1-待处理 2-处理中 3-已完成 4-失败)

**导入参数**:
- `carrier_id`: 运营商 ID(BIGINT,必填)
- `carrier_type`: 运营商类型(VARCHAR(10),CMCC/CUCC/CTCC/CBN)
- `batch_no`: 批次号(VARCHAR(100),可选)
- `file_name`: 原始文件名(VARCHAR(255),可选)

**待导入数据**:
- `card_list`: 待导入卡列表(JSONB,结构: [{iccid, msisdn}],替代原 iccid_list)

**进度统计**:
- `total_count`: 总数(INT,CSV 文件总行数)
- `success_count`: 成功数(INT,成功导入的卡数量)
- `skip_count`: 跳过数(INT,因重复等原因跳过的数量)
- `fail_count`: 失败数(INT,因格式错误等原因失败的数量)

**结果详情**:
- `skipped_items`: 跳过记录详情(JSONB,结构: [{line, iccid, msisdn, reason}])
- `failed_items`: 失败记录详情(JSONB,结构: [{line, iccid, msisdn, reason}])

**时间和错误**:
- `started_at`: 开始处理时间(TIMESTAMP,可空)
- `completed_at`: 完成时间(TIMESTAMP,可空)
- `error_message`: 任务级错误信息(TEXT,可空,如文件解析失败等)

**系统字段**:
- `shop_id`: 店铺 ID(BIGINT,可空,记录发起导入的店铺)
- `created_at`: 创建时间(TIMESTAMP,自动填充)
- `updated_at`: 更新时间(TIMESTAMP,自动填充)
- `creator`: 创建人 ID(BIGINT)
- `updater`: 更新人 ID(BIGINT)

#### Scenario: 创建导入任务

- **GIVEN** 管理员上传包含 ICCID 和 MSISDN 两列的 CSV 文件
- **WHEN** 系统解析 CSV 并创建导入任务
- **THEN** 系统创建导入任务记录,`card_list` 包含 [{iccid, msisdn}] 结构,`status` 为 1(待处理)

---

## ADDED Requirements

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

系统 SHALL 要求 CSV 文件必须包含 ICCID 和 MSISDN 两列。

**文件格式要求**:
- 第一列: ICCID（必填，不能为空）
- 第二列: MSISDN/接入号（必填，不能为空）
- 支持表头行（自动识别并跳过）
- 表头识别关键字: iccid/卡号 + msisdn/接入号/手机号

**解析规则**:
- 自动去除首尾空格
- 跳过空行
- 第一行为表头时自动跳过
- 列数不足 2 列的文件拒绝导入
- ICCID 为空的行记录为失败
- MSISDN 为空的行记录为失败

#### Scenario: 解析标准双列 CSV 文件

- **GIVEN** CSV 文件内容为:
  ```
  iccid,msisdn
  89860012345678901234,13800000001
  89860012345678901235,13800000002
  ```
- **WHEN** 系统解析该 CSV 文件
- **THEN** 解析结果包含 2 条有效记录,每条包含 ICCID 和 MSISDN

#### Scenario: 拒绝单列 CSV 文件

- **GIVEN** CSV 文件内容仅包含 ICCID 单列
- **WHEN** 系统尝试解析该 CSV 文件
- **THEN** 系统返回错误 "CSV 文件格式错误：缺少 MSISDN 列"

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

- **GIVEN** CSV 文件内容为:
  ```
  iccid,msisdn
  89860012345678901234,13800000001
  89860012345678901235,
  ```
- **WHEN** 系统解析该 CSV 文件
- **THEN** 第一条记录解析成功,第二条记录标记为失败,原因为 "MSISDN 不能为空"

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

- **GIVEN** CSV 文件内容为:
  ```
  iccid,msisdn
  89860012345678901234,13800000001
  ,13800000002
  ```
- **WHEN** 系统解析该 CSV 文件
- **THEN** 第一条记录解析成功,第二条记录标记为失败,原因为 "ICCID 不能为空"

---

### Requirement: 导入时填充 MSISDN 字段

系统 SHALL 在创建 IoT 卡记录时填充 MSISDN 字段。

**处理规则**:
- 从 `card_list` 中获取 ICCID 和 MSISDN
- 创建 `IotCard` 记录时同时设置 `iccid` 和 `msisdn` 字段

#### Scenario: 创建卡记录时填充 MSISDN

- **GIVEN** 导入任务包含卡数据 [{iccid: "898600...", msisdn: "13800000001"}]
- **WHEN** Worker 处理导入任务创建卡记录
- **THEN** 创建的 `IotCard` 记录 `iccid` 为 "898600...",`msisdn` 为 "13800000001"
