feat: 新增数据库迁移,重命名 device_no 为 virtual_no,新增 iot_card.virtual_no 和 package.virtual_ratio 字段
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m3s

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-14 18:27:28 +08:00
parent b5147d1acb
commit b9c3875c08
77 changed files with 5832 additions and 2393 deletions

View File

@@ -0,0 +1,133 @@
## MODIFIED Requirements
### Requirement: 设备列表查询
系统 SHALL 提供设备列表查询功能,支持多维度筛选和分页。
**查询条件**:
- `virtual_no`(可选): 设备虚拟号,支持模糊匹配(原 `device_no` 字段,已全量改名)
- `device_name`(可选): 设备名称,支持模糊匹配
- `status`(可选): 设备状态,枚举值 1-在库 | 2-已分销 | 3-已激活 | 4-已停用
- `shop_id`(可选): 店铺 IDNULL 表示平台库存
- `batch_no`(可选): 批次号,精确匹配
- `device_type`(可选): 设备类型
- `manufacturer`(可选): 制造商,支持模糊匹配
- `created_at_start`(可选): 创建时间起始
- `created_at_end`(可选): 创建时间结束
**分页**:
- 默认每页 20 条,最大每页 100 条
- 返回总记录数和总页数
**数据权限**:
- 平台用户可查看所有设备
- 代理用户只能查看自己店铺及下级店铺的设备
**API 端点**: `GET /api/admin/devices`
**响应字段**:
- `id`: 设备 ID
- `virtual_no`: 设备虚拟号(原 `device_no`,已改名)
- `device_name`: 设备名称
- `device_model`: 设备型号
- `device_type`: 设备类型
- `max_sim_slots`: 最大插槽数
- `manufacturer`: 制造商
- `batch_no`: 批次号
- `shop_id`: 店铺 ID
- `shop_name`: 店铺名称
- `status`: 状态
- `status_name`: 状态名称
- `bound_card_count`: 已绑定卡数量
- `activated_at`: 激活时间
- `created_at`: 创建时间
- `updated_at`: 更新时间
#### Scenario: 平台查询所有设备
- **WHEN** 平台管理员查询设备列表,不带任何筛选条件
- **THEN** 系统返回所有设备,按创建时间倒序排列
#### Scenario: 按虚拟号模糊查询
- **WHEN** 管理员输入 virtual_no = "GPS"
- **THEN** 系统返回虚拟号包含 "GPS" 的所有设备
#### Scenario: 按状态筛选设备
- **WHEN** 管理员查询状态为 1在库的设备
- **THEN** 系统只返回在库状态的设备
#### Scenario: 代理查询自己店铺的设备
- **WHEN** 代理用户(店铺 ID=10查询设备列表
- **THEN** 系统只返回 shop_id 为 10 及其下级店铺的设备
#### Scenario: 查询平台库存设备
- **WHEN** 平台管理员查询 shop_id 为空的设备
- **THEN** 系统返回所有平台库存设备shop_id = NULL
---
### Requirement: 设备详情查询
系统 SHALL 提供设备详情查询功能,返回设备的基本信息。
**API 端点**: `GET /api/admin/devices/:id`
**响应字段**:
- 包含设备的所有基本字段(含 `virtual_no`,不再有 `device_no`
- `shop_name`: 店铺名称(如果有)
**数据权限**:
- 平台用户可查看所有设备
- 代理用户只能查看自己店铺及下级店铺的设备
#### Scenario: 查询设备详情成功
- **WHEN** 管理员查询设备详情ID=1
- **THEN** 系统返回该设备的完整基本信息,响应中含 `virtual_no` 字段,不含 `device_no`
#### Scenario: 查询不存在的设备
- **WHEN** 管理员查询不存在的设备ID=999
- **THEN** 系统返回 404 错误,提示"设备不存在"
#### Scenario: 代理查询无权限的设备
- **WHEN** 代理用户(店铺 ID=10查询其他店铺的设备shop_id=20非下级
- **THEN** 系统返回 404 错误,提示"设备不存在"
## ADDED Requirements
### Requirement: device_no 全量改名为 virtual_no
系统 SHALL 将 `tb_device` 表和 `tb_personal_customer_device` 表中的 `device_no` 字段全量改名为 `virtual_no`,确保系统中不再有 `device_no` 的存在。
**数据库变更**:
```sql
ALTER TABLE tb_device RENAME COLUMN device_no TO virtual_no;
ALTER TABLE tb_personal_customer_device RENAME COLUMN device_no TO virtual_no;
```
**代码影响范围**:
- `internal/model/device.go``DeviceNo``VirtualNo`column tag 更新
- `internal/model/personal_customer_device.go``DeviceNo``VirtualNo`column tag 更新
- `internal/model/dto/device_dto.go``DeviceResponse.DeviceNo``VirtualNo`JSON tag 更新为 `"virtual_no"`
- `internal/store/postgres/device_store.go``GetByIdentifier` 查询条件中 `device_no``virtual_no`
- `internal/store/postgres/personal_customer_device_store.go`:所有 `device_no` 引用更新
- 所有 Handler、Service 中引用 `DeviceNo` 字段的代码全量替换
**设备导入模板**:
- 导入 Excel 模板中的列头从 `device_no` 更新为 `virtual_no`
#### Scenario: 改名后查询设备
- **WHEN** 改名迁移完成后,调用 `GetByIdentifier("GPS-001")`
- **THEN** 系统在 `WHERE virtual_no = ? OR imei = ? OR sn = ?` 中正确匹配,与改名前行为一致
#### Scenario: 响应中字段名已更新
- **WHEN** 前端调用设备列表或详情接口
- **THEN** 响应 JSON 中 key 为 `virtual_no`,不再有 `device_no`