Files
huang b02175271a
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m39s
feat: 实现企业设备授权功能并归档 OpenSpec 变更
- 新增企业设备授权模块(Model、DTO、Service、Handler、Store)
- 实现设备授权的创建、查询、更新、删除等完整业务逻辑
- 添加企业卡授权与设备授权的关联关系
- 新增 2 个数据库迁移脚本
- 同步 OpenSpec delta specs 到 main specs
- 归档 add-enterprise-device-authorization 变更
- 更新 API 文档和路由配置
- 新增完整的集成测试和单元测试覆盖
2026-01-29 13:18:49 +08:00

320 lines
9.7 KiB
Markdown
Raw Permalink 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.
## ADDED Requirements
### Requirement: 设备授权企业数据模型
系统 SHALL 定义 EnterpriseDeviceAuthorization 实体,记录设备与企业的授权关系。
**实体字段**
- `id`: 主键BIGSERIAL
- `enterprise_id`: 被授权企业IDBIGINTNOT NULL
- `device_id`: 被授权设备IDBIGINTNOT NULL
- `authorized_by`: 授权人账号IDBIGINTNOT NULL
- `authorized_at`: 授权时间TIMESTAMPNOT NULL
- `authorizer_type`: 授权人类型SMALLINT2=平台用户 3=代理账号)
- `revoked_by`: 回收人账号IDBIGINT可空
- `revoked_at`: 回收时间TIMESTAMP可空
- `remark`: 备注VARCHAR(500)
- `created_at`, `updated_at`, `deleted_at`: 标准时间字段
**唯一性约束**
- 一个设备同时只能授权给一个企业:`UNIQUE (device_id) WHERE revoked_at IS NULL AND deleted_at IS NULL`
**表名**`tb_enterprise_device_authorization`
#### Scenario: 创建设备授权记录
- **WHEN** 授权设备给企业时
- **THEN** 系统创建 EnterpriseDeviceAuthorization 记录authorized_at 设置为当前时间revoked_at 为 NULL
#### Scenario: 设备重复授权被拒绝
- **WHEN** 尝试将已授权给企业A的设备未回收再授权给企业B
- **THEN** 系统拒绝操作,返回错误"设备已授权给其他企业"
#### Scenario: 回收后可重新授权
- **WHEN** 设备授权已被回收后,重新授权给同一企业或其他企业
- **THEN** 系统允许创建新的授权记录
---
### Requirement: 卡授权记录关联设备授权
系统 SHALL 在 EnterpriseCardAuthorization 表中添加 device_auth_id 字段,关联设备授权记录。
**新增字段**
- `device_auth_id`: 关联的设备授权IDBIGINT可空
- NULL = 通过单卡授权创建
- 有值 = 通过设备授权创建
**索引**
- `idx_eca_device_auth ON tb_enterprise_card_authorization(device_auth_id)`
#### Scenario: 设备授权创建关联卡授权
- **WHEN** 通过设备授权创建卡授权记录时
- **THEN** 卡授权记录的 device_auth_id 字段设置为对应的设备授权ID
#### Scenario: 单卡授权不关联设备
- **WHEN** 通过单卡授权创建卡授权记录时
- **THEN** 卡授权记录的 device_auth_id 字段为 NULL
---
### Requirement: 设备授权管理功能
系统 SHALL 提供设备授权给企业的功能,支持批量授权和回收。
**授权规则**
- 代理只能授权自己店铺的设备给自己店铺下的企业
- 平台可以授权任意设备给任意企业
- 设备 MUST 属于操作者(平台或代理店铺)
- 设备 MUST 处于"已分销"状态status=2
- 设备 MUST 未授权给其他企业(唯一性约束)
**授权联动**
- 授权设备时,系统 SHALL 自动授权设备下所有已绑定的卡
- 卡授权记录的 device_auth_id 指向设备授权记录
- 如果设备没有绑定卡,仍然创建设备授权记录(无卡授权)
#### Scenario: 代理授权设备给自己的企业
- **WHEN** 代理shop_id=10将自己店铺的设备授权给企业owner_shop_id=10
- **THEN** 系统创建设备授权记录,并为设备下所有已绑定的卡创建卡授权记录
#### Scenario: 平台授权任意设备
- **WHEN** 平台管理员授权设备给任意企业
- **THEN** 系统创建授权记录,不检查设备和企业的归属关系
#### Scenario: 代理无法授权其他店铺的设备
- **WHEN** 代理shop_id=10尝试授权其他店铺的设备shop_id=20
- **THEN** 系统拒绝操作,返回权限错误
#### Scenario: 设备授权联动卡授权
- **WHEN** 授权一个绑定了3张卡的设备给企业
- **THEN** 系统创建1条设备授权记录和3条卡授权记录所有卡授权的 device_auth_id 指向该设备授权
---
### Requirement: 批量授权设备接口
系统 SHALL 提供批量授权设备给企业的后台接口。
**接口设计**
- 路径:`POST /api/admin/enterprises/:id/allocate-devices`
- 请求体:
```json
{
"device_nos": ["D001", "D002", "D003"],
"remark": "批量授权备注"
}
```
- 响应体:
```json
{
"success_count": 2,
"fail_count": 1,
"failed_items": [
{ "device_no": "D003", "reason": "设备不存在" }
],
"authorized_devices": [
{ "device_id": 1, "device_no": "D001", "card_count": 3 },
{ "device_id": 2, "device_no": "D002", "card_count": 2 }
]
}
```
**处理流程**
1. 验证企业存在且有权限
2. 验证每个设备的授权权限
3. 检查设备状态和唯一性约束
4. 在事务内创建设备授权和卡授权记录
5. 返回处理结果
#### Scenario: 批量授权成功
- **WHEN** 平台批量授权3个符合条件的设备给企业
- **THEN** 系统创建3条设备授权记录和对应的卡授权记录返回全部成功
#### Scenario: 批量授权部分成功
- **WHEN** 代理批量授权3个设备其中1个已授权给其他企业
- **THEN** 系统创建2条设备授权记录返回2个成功、1个失败及失败原因
---
### Requirement: 设备授权回收功能
系统 SHALL 提供回收设备授权的功能,回收时同步回收关联的卡授权。
**回收规则**
- 代理可以回收自己授权的设备
- 平台可以回收任何设备授权
- 回收操作在事务内完成
**回收联动**
- 回收设备授权时,系统 SHALL 同步回收所有 device_auth_id 指向该设备授权的卡授权记录
- 更新 revoked_at 和 revoked_by 字段
**接口设计**
- 路径:`POST /api/admin/enterprises/:id/recall-devices`
- 请求体:
```json
{
"device_nos": ["D001", "D002"]
}
```
#### Scenario: 回收设备授权联动回收卡授权
- **WHEN** 回收一个绑定了3张卡的设备的授权
- **THEN** 系统更新设备授权的 revoked_at同时更新3条关联卡授权的 revoked_at
#### Scenario: 回收后企业无法访问设备和卡
- **WHEN** 设备授权被回收后,企业用户查询设备或卡
- **THEN** 系统不返回该设备和其下的卡
---
### Requirement: 后台企业设备列表
系统 SHALL 提供后台管理查询企业授权设备列表的接口。
**接口设计**
- 路径:`GET /api/admin/enterprises/:id/devices`
- 查询参数:`page`, `page_size`, `device_no`, `status`
- 响应:设备列表,包含设备信息和绑定卡数量
**数据权限**
- 平台用户可查看所有企业的授权设备
- 代理用户只能查看自己店铺下企业的授权设备
#### Scenario: 查询企业授权设备列表
- **WHEN** 管理员查询企业ID=5的授权设备
- **THEN** 系统返回该企业所有授权设备列表,每个设备包含绑定卡数量
---
### Requirement: 企业端设备列表
系统 SHALL 提供企业用户查询自己授权设备列表的 H5 接口。
**接口设计**
- 路径:`GET /api/h5/enterprise/devices`
- 查询参数:`page`, `page_size`, `device_no`
- 响应:
```json
{
"list": [
{
"device_id": 1,
"device_no": "D001",
"device_name": "GPS追踪器-001",
"device_model": "GT-100",
"card_count": 3,
"authorized_at": "2025-01-29T10:00:00Z"
}
],
"total": 10
}
```
**数据权限**
- 企业用户只能看到授权给自己企业的设备
- 通过 GORM Callback 自动过滤
#### Scenario: 企业用户查看设备列表
- **WHEN** 企业用户查询设备列表
- **THEN** 系统返回授权给该企业的所有设备,包含设备信息和卡数量
#### Scenario: 企业用户无法看到未授权设备
- **WHEN** 企业用户查询设备列表
- **THEN** 系统不返回未授权给该企业的设备
---
### Requirement: 企业端设备详情
系统 SHALL 提供企业用户查询设备详情的 H5 接口,包含设备绑定的卡列表。
**接口设计**
- 路径:`GET /api/h5/enterprise/devices/:device_id`
- 响应:
```json
{
"device": {
"device_id": 1,
"device_no": "D001",
"device_name": "GPS追踪器-001",
"device_model": "GT-100",
"device_type": "GPS",
"authorized_at": "2025-01-29T10:00:00Z"
},
"cards": [
{
"card_id": 101,
"iccid": "8986001234567890",
"msisdn": "1380000001",
"carrier_name": "中国联通",
"network_status": 1,
"network_status_name": "开机"
}
]
}
```
**可见信息**
- 设备基本信息:设备号、名称、型号、类型
- 卡信息ICCID、MSISDN、运营商、网络状态
**不可见信息**
- 成本价、分销价、供应商等商业敏感信息
#### Scenario: 企业用户查看设备详情
- **WHEN** 企业用户查看授权设备ID=1的详情
- **THEN** 系统返回设备信息和该设备绑定的所有卡信息
#### Scenario: 企业用户无法查看未授权设备
- **WHEN** 企业用户尝试查看未授权的设备详情
- **THEN** 系统返回 404 错误
---
### Requirement: 企业端设备卡停机复机
系统 SHALL 提供企业用户对设备下的卡进行停机/复机操作的 H5 接口。
**接口设计**
- 停机:`POST /api/h5/enterprise/devices/:device_id/cards/:card_id/suspend`
- 复机:`POST /api/h5/enterprise/devices/:device_id/cards/:card_id/resume`
**权限校验**
- 设备 MUST 授权给当前企业
- 卡 MUST 属于该设备(通过 device_sim_binding 验证)
- 卡 MUST 通过设备授权device_auth_id 不为空且有效)
#### Scenario: 企业用户停机设备下的卡
- **WHEN** 企业用户对授权设备下的卡执行停机操作
- **THEN** 系统更新卡的 network_status 为 0停机
#### Scenario: 企业用户复机设备下的卡
- **WHEN** 企业用户对授权设备下的卡执行复机操作
- **THEN** 系统更新卡的 network_status 为 1开机
#### Scenario: 无法操作未授权设备的卡
- **WHEN** 企业用户尝试操作未授权设备下的卡
- **THEN** 系统返回 403 错误