Files
huang 477a9fc98d
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat: 添加设备IMEI和单卡ICCID查询接口
- 新增 GET /api/admin/devices/by-imei/:imei 接口,支持通过设备号查询设备详情
- 新增 GET /api/admin/iot-cards/by-iccid/:iccid 接口,支持通过ICCID查询单卡详情
- 添加对应的 Service 层方法和 Handler
- 更新 OpenAPI 文档
- 添加集成测试并修复测试环境配置(使用环境变量)
- 归档已完成的 OpenSpec 变更记录

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-27 09:59:54 +08:00

7.1 KiB
Raw Permalink Blame History

Design: 设备管理功能

Context

背景

系统已有完整的单卡IoT Card管理功能包括单卡列表、分配、回收、导入。现需要在单卡之上增加设备维度的管理能力。

设备是比单卡更高一层的管理维度:

  • 一个设备可绑定 1-4 张 IoT 卡
  • 设备和绑定的卡作为一个整体进行分配和回收
  • 设备由平台统一管理(导入、绑卡),代理商只能查看和分配

现有实现

组件 状态 说明
model/device.go 已有 Device Model
model/package.go 已有 DeviceSimBinding Model
tb_device 已有 设备表
tb_device_sim_binding 已有 设备卡绑定表
Store/Service/Handler 需新增 设备业务逻辑

约束

  • 遵循现有分层架构Handler → Service → Store → Model
  • 复用现有的资产分配记录asset-allocation-record能力
  • 参考现有 ICCID 导入实现异步任务
  • 权限控制:导入、绑卡、删除仅平台用户

Goals / Non-Goals

Goals

  1. 实现设备基础管理(列表、详情、删除)
  2. 实现设备导入CSV 批量导入,自动绑定卡)
  3. 实现设备卡绑定管理(绑定、解绑、查询)
  4. 实现设备分配/回收(自动同步绑定的卡)
  5. 复用现有资产分配记录能力

Non-Goals

  1. 设备操作(远程重启、改密码、重置)
  2. 设备套餐购买和流量共享
  3. 设备创建/编辑 API通过导入创建

Decisions

Decision 1: 设备导入时绑定卡

决策: 导入设备时必须同时指定要绑定的卡iccid_1~iccid_4而非导入设备后再单独绑定。

原因:

  • 业务流程:平台在外部系统报单后发货,设备和卡是一起出库的
  • 减少操作步骤:一次导入完成设备创建和卡绑定
  • 数据一致性:避免"空设备"状态

CSV 格式:

device_no,device_name,device_model,device_type,max_sim_slots,manufacturer,iccid_1,iccid_2,iccid_3,iccid_4

备注: 绑定/解绑 API 仅用于导入后的调整(换卡、补卡)。

Decision 2: 设备分配时自动同步卡的 shop_id

决策: 分配/回收设备时,自动同步修改绑定卡的 shop_id。

原因:

  • 业务需求:设备和卡作为整体分配,不能分开
  • 数据一致性:设备和卡的归属必须一致
  • 简化操作:代理商无需感知卡的存在

实现:

// 分配设备时
func (s *Service) AllocateDevices(ctx, req) {
    // 1. 更新设备 shop_id
    // 2. 查询设备绑定的所有卡
    // 3. 批量更新卡的 shop_id
    // 4. 创建分配记录related_card_ids
}

Decision 3: 导入时卡必须已存在

决策: 设备导入时CSV 中的 ICCID 必须已存在于系统中。

原因:

  • 数据完整性:卡有运营商、成本价等信息,需要先通过 ICCID 导入
  • 业务流程:通常先导入卡,再导入设备绑定卡
  • 错误处理ICCID 不存在时明确报错,便于排查

备选方案: 导入时自动创建不存在的卡 → 需要更多字段(运营商等),增加复杂度

Decision 4: 复用异步任务模式

决策: 设备导入使用与 ICCID 导入相同的异步任务模式。

原因:

  • 一致性:用户体验和代码模式保持一致
  • 可靠性:大文件处理不会超时
  • 可追溯:任务状态和结果可查询

实现:

  • 新增 tb_device_import_task 表(参考 tb_iot_card_import_task
  • 新增 task/device_import.go 异步处理器
  • 复用 pkg/queuepkg/storage 能力

Decision 5: 权限控制策略

决策: 设备导入、绑卡、删除仅限平台用户;列表查询、分配回收所有人可用。

操作 平台用户 代理用户
设备列表/详情 (数据权限过滤)
设备导入
绑卡/解绑
删除设备
分配设备 (只能给直属下级)
回收设备 (只能回收直属下级)

原因:

  • 平台统一管理设备库存和卡绑定关系
  • 代理商只需要分配/回收能力

Risks / Trade-offs

Risk 1: 导入时卡校验性能

风险: 大批量导入时,逐行校验 ICCID 是否存在可能较慢。

缓解:

  • 批量查询 ICCID 存在性IN 查询)
  • 批量查询 ICCID 绑定状态
  • 导入任务异步执行,不阻塞请求

Risk 2: 设备和卡 shop_id 不一致

风险: 如果代码逻辑有 bug可能导致设备和卡的 shop_id 不一致。

缓解:

  • 分配/回收使用事务,保证原子性
  • 添加集成测试验证一致性
  • 考虑后期添加数据一致性检查脚本

Risk 3: 删除设备时卡的处理

风险: 删除设备时,绑定的卡如何处理?

决策: 删除设备时自动解绑所有卡,卡的 shop_id 保持不变。

原因: 卡是有价值的资产,不应随设备删除而丢失。

Data Model

新增表: tb_device_import_task

CREATE TABLE tb_device_import_task (
    id BIGSERIAL PRIMARY KEY,
    task_no VARCHAR(50) NOT NULL UNIQUE,
    status INT NOT NULL DEFAULT 1,           -- 1-待处理 2-处理中 3-已完成 4-失败
    batch_no VARCHAR(100),
    file_key VARCHAR(500),
    file_name VARCHAR(255),
    total_count INT DEFAULT 0,
    success_count INT DEFAULT 0,
    skip_count INT DEFAULT 0,
    fail_count INT DEFAULT 0,
    skipped_items JSONB,
    failed_items JSONB,
    error_message TEXT,
    started_at TIMESTAMP,
    completed_at TIMESTAMP,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    deleted_at TIMESTAMP,
    creator BIGINT,
    updater BIGINT
);

CREATE INDEX idx_device_import_task_status ON tb_device_import_task(status);
CREATE INDEX idx_device_import_task_batch_no ON tb_device_import_task(batch_no);

现有表(无需修改)

  • tb_device: 设备表
  • tb_device_sim_binding: 设备卡绑定表
  • tb_asset_allocation_record: 资产分配记录表(已支持 device 类型)

API Design

设备管理

方法 路径 说明
GET /api/admin/devices 设备列表
GET /api/admin/devices/:id 设备详情
DELETE /api/admin/devices/:id 删除设备
GET /api/admin/devices/:id/cards 获取绑定的卡
POST /api/admin/devices/:id/cards 绑定卡
DELETE /api/admin/devices/:id/cards/:cardId 解绑卡
POST /api/admin/devices/allocate 批量分配
POST /api/admin/devices/recall 批量回收

设备导入

方法 路径 说明
POST /api/admin/devices/import 提交导入任务
GET /api/admin/devices/import/tasks 导入任务列表
GET /api/admin/devices/import/tasks/:id 导入任务详情

Open Questions

  1. 设备导入失败后是否支持重试?

    • 当前设计:不支持,用户需修正 CSV 重新导入
    • 可后续添加:断点续传、失败重试功能
  2. 设备和卡 shop_id 不一致时如何修复?

    • 需要管理员工具或 SQL 脚本修复
    • 建议后续添加数据一致性检查接口