feat(import): 用 Excel 格式替换 CSV 导入
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s
- 删除 CSV 解析代码,新增 Excel 解析器 (excelize) - 更新 IoT 卡和设备导入任务处理器 - 更新 API 路由文档和前端接入指南 - 归档变更到 openspec/changes/archive/ - 同步 delta specs 到 main specs
This commit is contained in:
@@ -5,7 +5,7 @@ TBD - created by archiving change add-device-management. Update Purpose after ar
|
||||
## Requirements
|
||||
### Requirement: 设备批量导入
|
||||
|
||||
系统 SHALL 提供设备批量导入功能,通过 CSV 文件导入设备并自动绑定卡,仅平台用户可操作。
|
||||
系统 SHALL 提供设备批量导入功能,通过 Excel 文件导入设备并自动绑定卡,仅平台用户可操作。
|
||||
|
||||
**API 端点**: `POST /api/admin/devices/import`
|
||||
|
||||
@@ -13,11 +13,23 @@ TBD - created by archiving change add-device-management. Update Purpose after ar
|
||||
- `batch_no`: 批次号(必填)
|
||||
- `file_key`: 对象存储文件路径(必填,通过 /storage/upload-url 获取)
|
||||
|
||||
**CSV 格式**:
|
||||
**Excel 格式**:
|
||||
- **文件格式**: 仅支持 `.xlsx` (Excel 2007+)
|
||||
- **Sheet**: 读取第一个sheet,或优先读取名为"导入数据"的sheet
|
||||
- **表头行**: 第1行,列名如下(顺序可任意):
|
||||
```
|
||||
device_no, device_name, device_model, device_type,
|
||||
max_sim_slots, manufacturer, iccid_1, iccid_2, iccid_3, iccid_4
|
||||
```
|
||||
- **数据行**: 从第2行开始
|
||||
- **列格式**: 所有列应设置为文本格式(避免数字被转为科学记数法)
|
||||
|
||||
**示例Excel内容**:
|
||||
```
|
||||
device_no,device_name,device_model,device_type,max_sim_slots,manufacturer,iccid_1,iccid_2,iccid_3,iccid_4
|
||||
DEV-001,GPS追踪器A,GT06N,GPS Tracker,4,Concox,8986001234567890001,8986001234567890002,,
|
||||
DEV-002,GPS追踪器B,GT06N,GPS Tracker,4,Concox,8986001234567890003,,,
|
||||
| device_no | device_name | device_model | device_type | max_sim_slots | manufacturer | iccid_1 | iccid_2 | iccid_3 | iccid_4 |
|
||||
|-----------|--------------|--------------|-------------|---------------|--------------|----------------------|----------------------|---------|---------|
|
||||
| DEV-001 | GPS追踪器A | GT06N | GPS Tracker | 4 | Concox | 8986001234567890001 | 8986001234567890002 | | |
|
||||
| DEV-002 | GPS追踪器B | GT06N | GPS Tracker | 4 | Concox | 8986001234567890003 | | | |
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
@@ -47,7 +59,7 @@ DEV-002,GPS追踪器B,GT06N,GPS Tracker,4,Concox,8986001234567890003,,,
|
||||
|
||||
#### Scenario: 提交设备导入任务
|
||||
|
||||
- **WHEN** 平台管理员上传 CSV 文件并提交导入请求
|
||||
- **WHEN** 平台管理员上传 Excel 文件并提交导入请求
|
||||
- **THEN** 系统创建导入任务,返回任务 ID,开始异步处理
|
||||
|
||||
#### Scenario: 代理尝试导入设备
|
||||
@@ -57,17 +69,24 @@ DEV-002,GPS追踪器B,GT06N,GPS Tracker,4,Concox,8986001234567890003,,,
|
||||
|
||||
#### Scenario: 文件格式错误
|
||||
|
||||
- **WHEN** 平台管理员上传非 CSV 格式或格式不正确的文件
|
||||
- **THEN** 系统创建任务但处理失败,任务状态为"失败",记录错误信息
|
||||
- **WHEN** 平台管理员上传非 Excel 格式(.xlsx)的文件
|
||||
- **THEN** 系统创建任务但处理失败,任务状态为"失败",错误信息为"不支持的文件格式 .csv,请上传Excel文件(.xlsx)"
|
||||
|
||||
#### Scenario: Excel结构错误
|
||||
|
||||
- **WHEN** 平台管理员上传的Excel文件无工作表或无数据行
|
||||
- **THEN** 系统创建任务但处理失败,任务状态为"失败",记录相应错误信息
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 设备导入任务执行
|
||||
|
||||
系统 SHALL 异步执行设备导入任务,逐行处理 CSV 数据。
|
||||
系统 SHALL 异步执行设备导入任务,逐行处理 Excel 数据。
|
||||
|
||||
**处理规则**:
|
||||
- 逐行解析 CSV 文件
|
||||
- 打开Excel文件,选择第一个sheet(或优先"导入数据"sheet)
|
||||
- 读取表头行,识别列索引
|
||||
- 逐行解析数据
|
||||
- 对每行数据执行以下校验:
|
||||
1. 设备号是否已存在(已存在则跳过)
|
||||
2. ICCID 是否存在于系统中(不存在则失败)
|
||||
@@ -85,27 +104,27 @@ DEV-002,GPS追踪器B,GT06N,GPS Tracker,4,Concox,8986001234567890003,,,
|
||||
|
||||
#### Scenario: 导入成功
|
||||
|
||||
- **WHEN** CSV 中所有设备号不重复且 ICCID 有效
|
||||
- **WHEN** Excel 中所有设备号不重复且 ICCID 有效
|
||||
- **THEN** 系统创建所有设备和绑定记录,任务状态为"已完成"
|
||||
|
||||
#### Scenario: 部分导入成功
|
||||
|
||||
- **WHEN** CSV 中部分设备号已存在或部分 ICCID 无效
|
||||
- **WHEN** Excel 中部分设备号已存在或部分 ICCID 无效
|
||||
- **THEN** 系统只导入有效的行,记录跳过和失败的详情,任务状态为"已完成"
|
||||
|
||||
#### Scenario: ICCID 不存在
|
||||
|
||||
- **WHEN** CSV 中某行的 ICCID 在系统中不存在
|
||||
- **WHEN** Excel 中某行的 ICCID 在系统中不存在
|
||||
- **THEN** 该行导入失败,记录失败原因"ICCID 不存在"
|
||||
|
||||
#### Scenario: ICCID 已绑定其他设备
|
||||
|
||||
- **WHEN** CSV 中某行的 ICCID 已绑定到其他设备
|
||||
- **WHEN** Excel 中某行的 ICCID 已绑定到其他设备
|
||||
- **THEN** 该行导入失败,记录失败原因"ICCID 已绑定其他设备"
|
||||
|
||||
#### Scenario: 设备号重复
|
||||
|
||||
- **WHEN** CSV 中某行的设备号在系统中已存在
|
||||
- **WHEN** Excel 中某行的设备号在系统中已存在
|
||||
- **THEN** 该行被跳过,记录跳过原因"设备号已存在"
|
||||
|
||||
---
|
||||
|
||||
@@ -164,63 +164,96 @@ TBD - created by archiving change iot-card-standalone-management. Update Purpose
|
||||
- **WHEN** 更新导入任务时 success_count + skip_count + fail_count > total_count
|
||||
- **THEN** 系统拒绝更新,返回错误信息"统计数量不一致"
|
||||
|
||||
### Requirement: CSV 文件格式规范
|
||||
### Requirement: Excel 文件格式规范
|
||||
|
||||
系统 SHALL 要求 CSV 文件必须包含 ICCID 和 MSISDN 两列。
|
||||
系统 SHALL 要求 Excel 文件必须包含 ICCID 和 MSISDN 两列。
|
||||
|
||||
**文件格式要求**:
|
||||
- 第一列: ICCID(必填,不能为空)
|
||||
- 第二列: MSISDN/接入号(必填,不能为空)
|
||||
- 支持表头行(自动识别并跳过)
|
||||
- 表头识别关键字: iccid/卡号 + msisdn/接入号/手机号
|
||||
- **文件格式**: 仅支持 `.xlsx` (Excel 2007+)
|
||||
- **Sheet**: 读取第一个sheet,或优先读取名为"导入数据"的sheet
|
||||
- **表头行**: 第1行(可选,但建议包含)
|
||||
- **表头识别关键字**:
|
||||
- ICCID列: iccid/ICCID/卡号/号码
|
||||
- MSISDN列: msisdn/MSISDN/接入号/手机号/电话/号码
|
||||
- **列数要求**: 至少2列(ICCID和MSISDN)
|
||||
- **列格式**: 应设置为文本格式(避免长数字被转为科学记数法)
|
||||
|
||||
**解析规则**:
|
||||
- 自动去除首尾空格
|
||||
- 自动检测表头(第1行包含识别关键字则跳过)
|
||||
- 自动去除单元格首尾空格
|
||||
- 跳过空行
|
||||
- 第一行为表头时自动跳过
|
||||
- 列数不足 2 列的文件拒绝导入
|
||||
- ICCID 为空的行记录为失败
|
||||
- MSISDN 为空的行记录为失败
|
||||
|
||||
#### Scenario: 解析标准双列 CSV 文件
|
||||
**示例Excel内容**:
|
||||
```
|
||||
| ICCID | MSISDN |
|
||||
|----------------------|-------------|
|
||||
| 89860012345678901234 | 13800000001 |
|
||||
| 89860012345678901235 | 13800000002 |
|
||||
```
|
||||
|
||||
- **GIVEN** CSV 文件内容为:
|
||||
#### Scenario: 解析标准双列 Excel 文件
|
||||
|
||||
- **GIVEN** Excel 文件内容为:
|
||||
```
|
||||
iccid,msisdn
|
||||
89860012345678901234,13800000001
|
||||
89860012345678901235,13800000002
|
||||
| ICCID | MSISDN |
|
||||
| 89860012345678901234 | 13800000001 |
|
||||
| 89860012345678901235 | 13800000002 |
|
||||
```
|
||||
- **WHEN** 系统解析该 CSV 文件
|
||||
- **WHEN** 系统解析该 Excel 文件
|
||||
- **THEN** 解析结果包含 2 条有效记录,每条包含 ICCID 和 MSISDN
|
||||
|
||||
#### Scenario: 拒绝单列 CSV 文件
|
||||
#### Scenario: 支持中文表头
|
||||
|
||||
- **GIVEN** CSV 文件内容仅包含 ICCID 单列
|
||||
- **WHEN** 系统尝试解析该 CSV 文件
|
||||
- **THEN** 系统返回错误 "CSV 文件格式错误:缺少 MSISDN 列"
|
||||
- **GIVEN** Excel 文件内容为:
|
||||
```
|
||||
| 卡号 | 接入号 |
|
||||
| 89860012345678901234 | 13800000001 |
|
||||
```
|
||||
- **WHEN** 系统解析该 Excel 文件
|
||||
- **THEN** 系统正确识别列,解析结果包含 1 条有效记录
|
||||
|
||||
#### Scenario: 拒绝非Excel格式文件
|
||||
|
||||
- **GIVEN** 上传文件扩展名为 .csv
|
||||
- **WHEN** 系统尝试解析该文件
|
||||
- **THEN** 系统返回错误 "不支持的文件格式 .csv,请上传Excel文件(.xlsx)"
|
||||
|
||||
#### Scenario: Excel文件无工作表
|
||||
|
||||
- **GIVEN** Excel 文件不包含任何工作表
|
||||
- **WHEN** 系统尝试解析该 Excel 文件
|
||||
- **THEN** 系统返回错误 "Excel文件无工作表"
|
||||
|
||||
#### Scenario: MSISDN 为空的行记录失败
|
||||
|
||||
- **GIVEN** CSV 文件内容为:
|
||||
- **GIVEN** Excel 文件内容为:
|
||||
```
|
||||
iccid,msisdn
|
||||
89860012345678901234,13800000001
|
||||
89860012345678901235,
|
||||
| ICCID | MSISDN |
|
||||
| 89860012345678901234 | 13800000001 |
|
||||
| 89860012345678901235 | |
|
||||
```
|
||||
- **WHEN** 系统解析该 CSV 文件
|
||||
- **WHEN** 系统解析该 Excel 文件
|
||||
- **THEN** 第一条记录解析成功,第二条记录标记为失败,原因为 "MSISDN 不能为空"
|
||||
|
||||
#### Scenario: ICCID 为空的行记录失败
|
||||
|
||||
- **GIVEN** CSV 文件内容为:
|
||||
- **GIVEN** Excel 文件内容为:
|
||||
```
|
||||
iccid,msisdn
|
||||
89860012345678901234,13800000001
|
||||
,13800000002
|
||||
| ICCID | MSISDN |
|
||||
| 89860012345678901234 | 13800000001 |
|
||||
| | 13800000002 |
|
||||
```
|
||||
- **WHEN** 系统解析该 CSV 文件
|
||||
- **WHEN** 系统解析该 Excel 文件
|
||||
- **THEN** 第一条记录解析成功,第二条记录标记为失败,原因为 "ICCID 不能为空"
|
||||
|
||||
#### Scenario: 长数字无损解析
|
||||
|
||||
- **GIVEN** Excel 文件中ICCID列设置为文本格式,包含20位数字 "89860012345678901234"
|
||||
- **WHEN** 系统解析该 Excel 文件
|
||||
- **THEN** ICCID 完整保留为 "89860012345678901234",无精度损失,无科学记数法
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 导入时填充 MSISDN 字段
|
||||
|
||||
Reference in New Issue
Block a user