# device-import Specification ## Purpose TBD - created by archiving change add-device-management. Update Purpose after archive. ## Requirements ### Requirement: 设备批量导入 系统 SHALL 提供设备批量导入功能,通过 Excel 文件导入设备并自动绑定卡,按固定列位置读取,表头行内容不影响解析结果,仅平台用户可操作。 **API 端点**: `POST /api/admin/devices/import` **请求参数**: - `batch_no`: 批次号(必填) - `file_key`: 对象存储文件路径(必填,通过 /storage/upload-url 获取) **Excel 格式**: - **文件格式**: 仅支持 `.xlsx` (Excel 2007+) - **Sheet**: 读取第一个sheet,或优先读取名为"导入数据"的sheet - **表头行**: 第1行固定为表头,永远跳过,内容不限(中文、英文均可) - **列位置(固定,不可变)**: ``` 第1列(索引0): 虚拟号(必填,全局唯一) 第2列(索引1): 设备名称(可选) 第3列(索引2): 设备型号(可选) 第4列(索引3): 设备类型(可选) 第5列(索引4): IMEI(可选) 第6列(索引5): 制造商(可选) 第7列(索引6): 最大SIM槽数(可选,默认4,范围1-4) 第8列(索引7): 卡1 ICCID(可选) 第9列(索引8): 卡2 ICCID(可选) 第10列(索引9): 卡3 ICCID(可选) 第11列(索引10): 卡4 ICCID(可选) ``` - **列格式**: 所有列应设置为文本格式(避免数字被转为科学记数法) **失败原因文本**: - VirtualNo 为空:`"设备虚拟号(virtual_no)不能为空"` - MaxSimSlots 越界:`"最大SIM槽数必须在1-4之间"` **导入规则**: - 按列索引取值,不识别列名,第一行永远跳过 - 若整行目标列皆为空,视为空行跳过,不计入 total,不计入失败 - MaxSimSlots 为空或 0 回填默认值 4;非空且不在 [1,4] 记录为失败 - 导入的设备 shop_id = NULL(平台库存) - 导入的设备 status = 1(在库) - 设备号重复则该行跳过 - ICCID 必须已存在于系统中(先导入卡,再导入设备) - ICCID 不存在则该行失败 - ICCID 已绑定其他设备则该行失败 - 导入通过异步任务处理,立即返回任务 ID **权限**: 仅平台用户 **响应**: - `task_id`: 导入任务 ID - `task_no`: 任务编号 - `message`: 提示信息 #### Scenario: 提交设备导入任务 - **WHEN** 平台管理员上传 Excel 文件并提交导入请求 - **THEN** 系统创建导入任务,返回任务 ID,开始异步处理 #### Scenario: 中文表头正常导入 - **GIVEN** Excel 文件第1行表头为 `虚拟号 | 设备名称 | 设备型号 | 设备类型 | IMEI | 制造商 | 最大SIM槽数 | 卡1 | 卡2 | 卡3 | 卡4` - **WHEN** 系统解析该 Excel 文件 - **THEN** 系统跳过第1行,从第2行开始按列位置解析数据 #### Scenario: 代理尝试导入设备 - **WHEN** 代理用户尝试导入设备 - **THEN** 系统返回 403 错误,提示"无权限执行此操作" #### Scenario: 文件格式错误 - **WHEN** 平台管理员上传非 Excel 格式(.xlsx)的文件 - **THEN** 系统创建任务但处理失败,任务状态为"失败",错误信息为"不支持的文件格式 .csv,请上传Excel文件(.xlsx)" #### Scenario: Excel结构错误 - **WHEN** 平台管理员上传的Excel文件无工作表或无数据行 - **THEN** 系统创建任务但处理失败,任务状态为"失败",记录相应错误信息 #### Scenario: VirtualNo 为空的行记录为失败 - **WHEN** Excel 中某行第1列(虚拟号)为空 - **THEN** 该行计入失败(`fail_count++`),失败原因为"设备虚拟号(virtual_no)不能为空" - **THEN** 其他合法行继续导入,不因此行中断 --- ### Requirement: 设备导入任务执行 系统 SHALL 异步执行设备导入任务,逐行处理 Excel 数据。 **处理规则**: - 打开Excel文件,选择第一个sheet(或优先"导入数据"sheet) - 跳过第1行(表头行),从第2行开始按固定列位置解析数据 - 若整行目标列皆为空,视为空行跳过,不计入 total - 逐行解析数据 - 对每行数据执行以下校验: 1. 设备号是否已存在(已存在则跳过) 2. ICCID 是否存在于系统中(不存在则失败) 3. ICCID 是否已绑定其他设备(已绑定则失败) - 校验通过后: 1. 创建设备记录 2. 创建设备-卡绑定记录 - 记录处理结果(成功/跳过/失败) **任务状态**: - 1: 待处理 - 2: 处理中 - 3: 已完成 - 4: 失败 #### Scenario: 导入成功 - **WHEN** Excel 中所有设备号不重复且 ICCID 有效 - **THEN** 系统创建所有设备和绑定记录,任务状态为"已完成" #### Scenario: 部分导入成功 - **WHEN** Excel 中部分设备号已存在或部分 ICCID 无效 - **THEN** 系统只导入有效的行,记录跳过和失败的详情,任务状态为"已完成" #### Scenario: ICCID 不存在 - **WHEN** Excel 中某行的 ICCID 在系统中不存在 - **THEN** 该行导入失败,记录失败原因"ICCID 不存在" #### Scenario: ICCID 已绑定其他设备 - **WHEN** Excel 中某行的 ICCID 已绑定到其他设备 - **THEN** 该行导入失败,记录失败原因"ICCID 已绑定其他设备" #### Scenario: 设备号重复 - **WHEN** Excel 中某行的设备号在系统中已存在 - **THEN** 该行被跳过,记录跳过原因"设备号已存在" #### Scenario: VirtualNo 为空的行记录为失败 - **WHEN** Excel 中某行第1列(虚拟号)为空 - **THEN** 该行计入失败(`fail_count++`),失败原因为"设备虚拟号(virtual_no)不能为空" - **THEN** 其他合法行继续导入,不因此行中断 #### Scenario: 导入任务结果报告包含 VirtualNo 失败原因 - **WHEN** 导入任务处理完毕,含有 VirtualNo 为空的行 - **THEN** 失败明细列表中,该行的 `reason` 字段为"设备虚拟号(virtual_no)不能为空",`line` 字段为对应行号 --- ### Requirement: 设备导入任务列表查询 系统 SHALL 提供设备导入任务列表查询功能,仅平台用户可操作。 **API 端点**: `GET /api/admin/devices/import/tasks` **查询条件**: - `status`(可选): 任务状态 1-4 - `batch_no`(可选): 批次号,模糊匹配 - `start_time`(可选): 创建时间起始 - `end_time`(可选): 创建时间结束 **分页**: - 默认每页 20 条,最大每页 100 条 **响应字段**: - `id`: 任务 ID - `task_no`: 任务编号 - `status`: 任务状态 - `status_text`: 任务状态文本 - `batch_no`: 批次号 - `file_name`: 文件名 - `total_count`: 总数 - `success_count`: 成功数 - `skip_count`: 跳过数 - `fail_count`: 失败数 - `started_at`: 开始时间 - `completed_at`: 完成时间 - `error_message`: 错误信息 - `created_at`: 创建时间 **权限**: 仅平台用户 #### Scenario: 查询导入任务列表 - **WHEN** 平台管理员查询导入任务列表 - **THEN** 系统返回所有导入任务,按创建时间倒序排列 #### Scenario: 按状态筛选任务 - **WHEN** 平台管理员查询状态为 3(已完成)的任务 - **THEN** 系统只返回已完成的任务 #### Scenario: 代理尝试查询导入任务 - **WHEN** 代理用户尝试查询导入任务 - **THEN** 系统返回 403 错误,提示"无权限执行此操作" --- ### Requirement: 设备导入任务详情查询 系统 SHALL 提供设备导入任务详情查询功能,包含跳过和失败记录的详细信息。 **API 端点**: `GET /api/admin/devices/import/tasks/:id` **响应字段**: - 包含任务列表的所有字段 - `skipped_items`: 跳过记录详情列表 - `line`: 行号 - `device_no`: 设备号 - `reason`: 跳过原因 - `failed_items`: 失败记录详情列表 - `line`: 行号 - `device_no`: 设备号 - `reason`: 失败原因 **权限**: 仅平台用户 #### Scenario: 查询导入任务详情 - **WHEN** 平台管理员查询导入任务详情(ID=1) - **THEN** 系统返回任务的完整信息,包括跳过和失败记录详情 #### Scenario: 查询不存在的任务 - **WHEN** 平台管理员查询不存在的任务(ID=999) - **THEN** 系统返回 404 错误,提示"导入任务不存在"