Files
junhong_cmp_fiber/openspec/specs/iot-card-import-task/spec.md
huang 45aa7deb87
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m33s
feat: 添加环境变量管理工具和部署配置改版
主要改动:
- 新增交互式环境配置脚本 (scripts/setup-env.sh)
- 新增本地启动快捷脚本 (scripts/run-local.sh)
- 新增环境变量模板文件 (.env.example)
- 部署模式改版:使用嵌入式配置 + 环境变量覆盖
- 添加对象存储功能支持
- 改进 IoT 卡片导入任务
- 优化 OpenAPI 文档生成
- 删除旧的配置文件,改用嵌入式默认配置
2026-01-26 10:28:29 +08:00

240 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# iot-card-import-task Specification
## Purpose
TBD - created by archiving change iot-card-standalone-management. Update Purpose after archive.
## 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(待处理)
---
### Requirement: 导入任务状态流转
系统 SHALL 管理导入任务的状态流转,确保状态变更符合业务规则。
**状态定义**:
- **1-待处理**: 任务已创建,等待 Worker 处理
- **2-处理中**: Worker 正在处理导入
- **3-已完成**: 导入处理完成(可能有部分失败)
- **4-失败**: 任务级别错误,导入中断
**状态流转规则**:
- 待处理(1) → 处理中(2): Worker 开始处理
- 处理中(2) → 已完成(3): 处理完成
- 处理中(2) → 失败(4): 发生严重错误
- 待处理(1) → 失败(4): 文件验证失败等
#### Scenario: 正常状态流转
- **WHEN** 导入任务经历完整生命周期
- **THEN** 状态依次变更: 待处理(1) → 处理中(2) → 已完成(3)
#### Scenario: 异常状态流转
- **WHEN** 导入任务处理过程中发生严重错误
- **THEN** 状态变更: 待处理(1) → 处理中(2) → 失败(4)
---
### Requirement: 导入任务列表查询
系统 SHALL 支持查询导入任务列表,用于管理和监控导入任务。
**查询条件**:
- 任务状态(status): 可选,1-待处理 2-处理中 3-已完成 4-失败
- 运营商 ID(carrier_id): 可选
- 批次号(batch_no): 可选,模糊匹配
- 创建时间范围: 可选
**分页**:
- 默认每页 20 条,最大每页 100 条
- 默认按创建时间倒序排列
**数据权限**:
- 基于 shop_id 自动应用数据权限过滤
- 代理只能看到自己店铺及下级店铺发起的导入任务
#### Scenario: 查询所有导入任务
- **WHEN** 管理员查询导入任务列表
- **THEN** 系统返回导入任务列表,包含任务编号、状态、运营商、总数、成功数、跳过数、失败数、创建时间
#### Scenario: 按状态筛选导入任务
- **WHEN** 管理员查询状态为 2(处理中) 的导入任务
- **THEN** 系统返回所有正在处理的导入任务列表
---
### Requirement: 导入任务详情查询
系统 SHALL 支持查询单个导入任务的详细信息,包括跳过/失败记录详情。
**详情信息**:
- 任务基本信息: 任务编号、状态、运营商、批次号、文件名
- 进度统计: 总数、成功数、跳过数、失败数
- 时间信息: 创建时间、开始时间、完成时间
- 跳过记录详情: 行号、ICCID、原因
- 失败记录详情: 行号、ICCID、原因
- 错误信息: 任务级错误(如有)
#### Scenario: 查询导入任务详情
- **WHEN** 管理员查询导入任务(ID 为 1)的详情
- **THEN** 系统返回任务完整信息,包括跳过和失败记录的详细列表
#### Scenario: 查询导入任务的跳过记录
- **WHEN** 管理员查询导入任务(ID 为 1)的跳过记录
- **THEN** 系统返回跳过记录列表,每条包含: 行号(line)、ICCID、原因(如"ICCID 已存在")
#### Scenario: 查询导入任务的失败记录
- **WHEN** 管理员查询导入任务(ID 为 1)的失败记录
- **THEN** 系统返回失败记录列表,每条包含: 行号(line)、ICCID、原因(如"电信 ICCID 必须为 19 位")
---
### Requirement: 导入任务数据校验
系统 SHALL 对导入任务数据进行校验,确保数据完整性和一致性。
**校验规则**:
- 任务编号(task_no): 必填,系统自动生成,格式 IMP-YYYYMMDD-XXXXXX,唯一
- 任务状态(status): 必填,枚举值 1(待处理) | 2(处理中) | 3(已完成) | 4(失败)
- 运营商 ID(carrier_id): 必填,必须是有效的运营商 ID
- 总数(total_count): 必填,≥ 0
- 成功数(success_count): 必填,≥ 0,≤ total_count
- 跳过数(skip_count): 必填,≥ 0,≤ total_count
- 失败数(fail_count): 必填,≥ 0,≤ total_count
- 数量一致性: success_count + skip_count + fail_count ≤ total_count
#### Scenario: 创建任务时运营商 ID 无效
- **WHEN** 创建导入任务时 carrier_id 不存在
- **THEN** 系统拒绝创建,返回错误信息"运营商 ID 无效"
#### Scenario: 更新任务时数量不一致
- **WHEN** 更新导入任务时 success_count + skip_count + fail_count > total_count
- **THEN** 系统拒绝更新,返回错误信息"统计数量不一致"
### 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"