## MODIFIED Requirements

### Requirement: 设备列表查询

系统 SHALL 提供设备列表查询功能，支持多维度筛选和分页。

**查询条件**:
- `virtual_no`（可选）: 设备虚拟号，支持模糊匹配（原 `device_no` 字段，已全量改名）
- `device_name`（可选）: 设备名称，支持模糊匹配
- `status`（可选）: 设备状态，枚举值 1-在库 | 2-已分销 | 3-已激活 | 4-已停用
- `shop_id`（可选）: 店铺 ID，NULL 表示平台库存
- `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`
