feat: 实现企业卡授权和授权记录管理功能
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m9s

主要功能:
- 添加企业卡授权/回收接口 (POST /enterprises/:id/allocate-cards, recall-cards)
- 添加授权记录管理接口 (GET/PUT /authorizations)
- 实现代理用户数据权限过滤(只能查看自己店铺下企业的授权记录)
- 添加 GORM callback 支持授权记录表的数据权限过滤

技术改进:
- 原生 SQL 查询手动添加数据权限过滤(ListWithJoin, GetByIDWithJoin)
- 移除卡授权预检接口(allocate-cards/preview),保留内部方法
- 完善单元测试和集成测试覆盖
This commit is contained in:
2026-01-26 15:07:03 +08:00
parent 45aa7deb87
commit fdcff33058
42 changed files with 4782 additions and 298 deletions

View File

@@ -0,0 +1,162 @@
## ADDED Requirements
### Requirement: 企业单卡授权管理
系统 SHALL 支持将 IoT 卡授权给企业使用,授权不转移所有权,仅授予使用权限。
**授权规则**
- 代理只能授权自己的卡owner_type="agent" 且 owner_id=自己的 shop_id给自己的企业
- 平台可以授权任意卡,但如果是代理的卡,只能授权给该代理的企业
- 只能授权单张卡,不支持批量选择
- 已绑定设备的卡不能授权(设备卡应整体授权,而非单卡)
- 只能授权状态为 "已分销(2)" 的卡
**授权记录存储**
- 使用 `enterprise_card_authorization` 表记录授权关系
- 不使用 `asset_allocation_record` 表(该表用于分配,非授权)
**权限控制**
- 企业用户只能查看被授权的卡
- 授权后卡的 shop_id 保持不变(所有权不转移)
- 回收授权后企业立即失去访问权限
#### Scenario: 代理授权自己的卡给自己的企业
- **WHEN** 代理shop_id=10将自己的卡owner_type="agent", owner_id=10授权给企业enterprise_id=5, owner_shop_id=10
- **THEN** 系统创建授权记录,企业可以查看和管理该卡,卡的 shop_id 保持为 10
#### Scenario: 平台授权任意卡给企业
- **WHEN** 平台管理员将卡授权给企业
- **THEN** 系统创建授权记录,不检查卡的所有者,企业获得该卡的访问权限
#### Scenario: 代理无法授权其他代理的卡
- **WHEN** 代理shop_id=10尝试授权其他代理的卡owner_id=20给企业
- **THEN** 系统拒绝操作,返回权限错误
#### Scenario: 已绑定设备的卡不能授权
- **WHEN** 用户尝试授权已绑定到设备的卡
- **THEN** 系统拒绝操作,提示该卡已绑定设备,请使用设备授权功能
#### Scenario: 只能授权已分销状态的卡
- **WHEN** 用户尝试授权非"已分销"状态的卡
- **THEN** 系统拒绝操作,提示只能授权"已分销"状态的卡
---
### Requirement: 企业卡授权数据模型
系统 SHALL 定义 EnterpriseCardAuthorization 实体,记录企业卡授权关系。
**实体字段**
- `id`: 主键BIGINT
- `enterprise_id`: 被授权企业IDBIGINT关联 enterprises 表)
- `card_id`: IoT卡IDBIGINT关联 iot_cards 表)
- `authorizer_id`: 授权人账号IDBIGINT关联 accounts 表)
- `authorizer_type`: 授权人类型VARCHAR(20)"platform" | "agent"
- `authorized_at`: 授权时间TIMESTAMP
- `revoked_at`: 回收时间TIMESTAMP可空
- `revoked_by`: 回收人账号IDBIGINT可空
- `created_at`: 创建时间TIMESTAMP
- `updated_at`: 更新时间TIMESTAMP
#### Scenario: 创建授权记录
- **WHEN** 授权卡给企业时
- **THEN** 系统创建 EnterpriseCardAuthorization 记录authorized_at 设置为当前时间revoked_at 为 NULL
#### Scenario: 回收授权
- **WHEN** 回收企业的卡授权时
- **THEN** 系统更新对应记录的 revoked_at 和 revoked_by 字段,不删除记录(保留历史)
---
### Requirement: 批量授权接口
系统 SHALL 提供批量授权接口,支持一次授权多张卡给企业,不需要预检接口。
**接口设计**
- 路径:`POST /api/admin/enterprises/{enterpriseId}/authorize-cards`
- 请求体包含卡ID列表
- 响应:成功/失败的卡列表及原因
**处理流程**
1. 验证每张卡的授权权限
2. 检查卡状态是否为"已分销"
3. 检查卡是否已绑定设备
4. 检查是否已授权给其他企业
5. 创建授权记录
6. 返回处理结果
#### Scenario: 批量授权成功
- **WHEN** 代理批量授权 5 张符合条件的卡给企业
- **THEN** 系统创建 5 条授权记录,返回全部成功
#### Scenario: 批量授权部分成功
- **WHEN** 代理批量授权 5 张卡,其中 2 张不符合条件1 张已绑定设备1 张非已分销状态)
- **THEN** 系统创建 3 条授权记录,返回 3 张成功、2 张失败及失败原因
---
### Requirement: 企业查看授权卡信息
系统 SHALL 允许企业查看被授权卡的特定信息,同时隐藏商业敏感信息。
**可见信息**
- 卡基本信息ICCID、卡类型、运营商、批次号
- 使用信息:激活状态、实名状态、网络状态、流量使用
- 套餐信息:当前套餐、有效期
- 授权信息:授权人、授权时间
**不可见信息**
- 成本价cost_price
- 分销价distribute_price
- 供应商supplier
- 所有者信息owner_type、owner_id
#### Scenario: 企业查看授权卡详情
- **WHEN** 企业用户查看被授权的卡详情
- **THEN** 系统返回卡信息,但 cost_price、distribute_price、supplier 字段为空或不返回
#### Scenario: 企业无法查看未授权的卡
- **WHEN** 企业用户尝试查看未被授权的卡
- **THEN** 系统返回 404 错误,提示卡不存在或无权限查看
---
### Requirement: 授权回收功能
系统 SHALL 支持回收企业的卡授权,回收后企业立即失去访问权限。
**回收规则**
- 代理可以回收自己授权的卡
- 平台可以回收任何授权
- 回收操作不可逆(需重新授权才能恢复访问)
**回收效果**
- 更新 revoked_at 和 revoked_by 字段
- 企业立即无法查看该卡
- 保留授权历史记录
#### Scenario: 代理回收自己的授权
- **WHEN** 代理回收之前授权给企业的卡
- **THEN** 系统更新授权记录的回收字段,企业立即无法访问该卡
#### Scenario: 平台回收任意授权
- **WHEN** 平台管理员回收任意企业的卡授权
- **THEN** 系统更新授权记录,不检查原授权人,企业失去访问权限
#### Scenario: 回收后企业无法访问
- **WHEN** 授权被回收后,企业用户尝试查看该卡
- **THEN** 系统返回 404 错误,如同该卡从未被授权过

View File

@@ -0,0 +1,42 @@
## MODIFIED Requirements
### Requirement: IoT 卡查询和权限控制
系统 SHALL 支持基于用户类型和授权关系的 IoT 卡查询权限控制。
**查询权限规则**
- **超级管理员/平台用户**:可以查询所有 IoT 卡
- **代理用户**:可以查询自己店铺和下级店铺的 IoT 卡
- **企业用户**
- 可以查询分配给自己企业的卡owner_type="enterprise" 且 owner_id=自己的企业ID
- 可以查询授权给自己企业的卡(通过 enterprise_card_authorization 表关联)
- **个人客户**:只能查询自己拥有的卡
**数据过滤**
- 企业用户查询时自动过滤敏感商业信息cost_price、distribute_price、supplier
- 其他用户类型可以看到完整信息
#### Scenario: 企业用户查询自己拥有的卡
- **WHEN** 企业用户查询 IoT 卡列表,且存在 owner_type="enterprise" 且 owner_id=该企业ID 的卡
- **THEN** 系统返回这些卡的信息,但隐藏 cost_price、distribute_price、supplier 字段
#### Scenario: 企业用户查询被授权的卡
- **WHEN** 企业用户查询 IoT 卡列表,且存在通过 enterprise_card_authorization 授权给该企业的卡
- **THEN** 系统返回这些授权卡的信息,但隐藏商业敏感字段,同时包含授权人和授权时间信息
#### Scenario: 企业用户无法查询未授权的卡
- **WHEN** 企业用户尝试查询既不属于自己也未被授权的卡
- **THEN** 系统在查询结果中不包含这些卡,如同它们不存在
#### Scenario: 代理用户正常查询
- **WHEN** 代理用户查询 IoT 卡
- **THEN** 系统返回该代理店铺及其下级店铺的所有卡,包含完整信息
#### Scenario: 授权被回收后企业无法查询
- **WHEN** 卡的授权被回收后revoked_at 不为空),企业用户查询该卡
- **THEN** 系统不返回该卡信息,企业无法再看到该卡