提案以及归档
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m39s

This commit is contained in:
2026-04-18 09:10:29 +08:00
parent 6caf0f6141
commit 4aab0bcbf2
40 changed files with 936 additions and 8 deletions

View File

@@ -0,0 +1,171 @@
# Capability: 资产实名策略
## ADDED Requirements
### Requirement: 资产实名策略字段定义
系统 SHALL 在 `IotCard``Device` 模型上各新增 `realname_policy` 字段VARCHAR(20)NOT NULLDEFAULT 'none'),用于控制该资产的实名认证要求。
**枚举值**
- `none`:无需实名,充值/购买/实名链接均不受限
- `before_order`:先实名后充值/购买,充值/购买前若未实名则拦截并返回 `CodeNeedRealname`
- `after_order`:先充值/购买后实名,充值/购买放行;实名链接在无有效充值/订单记录前拦截
**常量定义**`pkg/constants/iot.go``pkg/constants/realname.go`
```go
const (
RealnmePolicyNone = "none" // 无需实名
RealnmePolicyBeforeOrder = "before_order" // 先实名后充值/购买
RealnmePolicyAfterOrder = "after_order" // 先充值/购买后实名
)
```
#### Scenario: 默认值为 none
- **WHEN** 新建 IotCard 或 Device 时未传入 realname_policy
- **THEN** 系统自动填充 `realname_policy = "none"`
#### Scenario: 存量数据迁移后行为不变
- **WHEN** 数据库迁移执行后,存量 IotCard 和 Device 的 realname_policy 均为 "none"
- **THEN** 充值、购买、实名链接行为与迁移前完全相同(均放行)
---
### Requirement: 生效策略优先级(设备卡 vs 单卡)
系统 SHALL 按以下规则确定一张卡的生效实名策略:
- **单卡**(该卡未绑定任何设备):使用 `IotCard.realname_policy`
- **设备卡**(该卡已绑定设备):使用 `Device.realname_policy`,忽略 `IotCard.realname_policy`
此规则 SHALL 封装为 Service 层公共方法 `GetEffectiveRealnamePolicy`,所有需要策略判断的场景均调用该方法,不得在多处重复实现。
#### Scenario: 设备卡使用设备策略
- **WHEN** IotCard.realname_policy="none" 且该卡绑定了 Device.realname_policy="before_order"
- **THEN** 生效策略为 "before_order"(设备策略覆盖卡策略)
#### Scenario: 单卡使用卡策略
- **WHEN** IotCard.realname_policy="before_order" 且该卡未绑定任何设备
- **THEN** 生效策略为 "before_order"
---
### Requirement: C 端充值接口实名策略拦截
系统 SHALL 在 C 端充值预检接口C3 `GET /api/c/v1/wallet/recharge-check`和充值下单接口C4 `POST /api/c/v1/wallet/recharge`)中,按生效实名策略执行以下逻辑:
- `before_order` + `real_name_status=0`未实名MUST 拦截,返回 `CodeNeedRealname`(错误码 1187
- `before_order` + `real_name_status=1`(已实名):放行
- `after_order`:放行(不检查实名状态)
- `none`:放行
#### Scenario: before_order 模式未实名时充值被拦截
- **WHEN** 资产 realname_policy="before_order" 且 real_name_status=0用户调用充值下单接口
- **THEN** 系统返回错误码 1187CodeNeedRealname
#### Scenario: before_order 模式已实名时充值放行
- **WHEN** 资产 realname_policy="before_order" 且 real_name_status=1用户调用充值下单接口
- **THEN** 系统正常创建充值订单
#### Scenario: after_order 模式充值放行
- **WHEN** 资产 realname_policy="after_order" 且 real_name_status=0用户调用充值下单接口
- **THEN** 系统正常创建充值订单,不检查实名状态
---
### Requirement: C 端购买套餐/订单接口实名策略拦截
系统 SHALL 在 C 端创建订单接口D1 `POST /api/c/v1/orders/create`和支付接口D4 `POST /api/c/v1/orders/:id/pay`按生效实名策略执行与充值相同的拦截规则。D1 中原 `REALNAME-03` 注释代码 SHALL 被删除,由新策略逻辑替代。
#### Scenario: before_order 模式未实名时购买套餐被拦截
- **WHEN** 资产 realname_policy="before_order" 且 real_name_status=0用户调用创建订单接口
- **THEN** 系统返回错误码 1187CodeNeedRealname订单不创建
#### Scenario: none 模式购买套餐放行
- **WHEN** 资产 realname_policy="none",用户调用创建订单接口
- **THEN** 系统正常创建订单,不检查实名状态
---
### Requirement: C 端实名链接接口策略拦截
系统 SHALL 在 C 端实名链接接口E1 `GET /api/c/v1/realname/link`)中,按生效实名策略执行以下逻辑:
- `none`:放行,正常返回实名链接(用户可自愿实名)
- `before_order`:放行,正常返回实名链接(引导用户先实名再充值)
- `after_order`:检查该资产当前 generation 内是否存在有效充值(`status=2`)或已支付订单(`payment_status=2`
- 有记录 → 放行
- 无记录 → 拦截,返回错误码(新错误码 `CodeRealnameNotAvailable`),消息为"请先完成充值或购买套餐后再进行实名认证"
#### Scenario: after_order 模式有充值记录时实名链接放行
- **WHEN** 资产 realname_policy="after_order" 且当前 generation 内存在 status=2 的充值记录
- **THEN** 系统正常返回实名链接
#### Scenario: after_order 模式无充值/订单记录时实名链接被拦截
- **WHEN** 资产 realname_policy="after_order" 且当前 generation 内无任何有效充值或已支付订单
- **THEN** 系统返回错误,消息为"请先完成充值或购买套餐后再进行实名认证"
#### Scenario: before_order 模式正常返回实名链接
- **WHEN** 资产 realname_policy="before_order"
- **THEN** 系统正常返回实名链接(引导用户完成实名)
---
### Requirement: 后台管理更新资产实名策略接口
系统 SHALL 提供 `PATCH /api/admin/assets/:identifier/realname-mode`,仅限后台管理端认证用户访问。接口通过 `assetService.Resolve()` 将标识符ICCID/虚拟号)解析为具体资产,按 asset_type 分别更新 `IotCard.realname_policy``Device.realname_policy`
**请求体**
```json
{ "realname_policy": "none | before_order | after_order" }
```
**响应体**
```json
{ "asset_type": "card | device", "asset_id": 123, "realname_policy": "before_order" }
```
#### Scenario: 通过 ICCID 更新单卡策略
- **WHEN** 管理员传入 identifier=ICCIDrealname_policy="before_order"
- **THEN** 系统更新对应 IotCard 的 realname_policy 为 "before_order",返回 asset_type="card"
#### Scenario: 通过设备号更新设备策略
- **WHEN** 管理员传入 identifier=设备虚拟号realname_policy="after_order"
- **THEN** 系统更新对应 Device 的 realname_policy 为 "after_order",返回 asset_type="device"
#### Scenario: 传入无效枚举值被拒绝
- **WHEN** 管理员传入 realname_policy="invalid_value"
- **THEN** 系统返回参数校验错误,提示实名策略值无效
---
### Requirement: 所有查询接口返回 realname_policy 字段
以下所有 DTO SHALL 新增 `realname_policy` 字段string及对应的 description 标签:
**后台管理端 DTO**
- `StandaloneIotCardResponse`IoT 卡详情/列表)
- `DeviceResponse`(设备详情/列表)
- `AssetResolveResponse`(统一资产解析)
- `AssetRealtimeStatusResponse`(资产实时状态)
- `DeviceCardBindingResponse`(设备绑卡记录)
- `BoundCardInfo`asset_dto.go 中的通用子结构)
- `ImportTaskResponse`iot 卡导入任务)
- `DeviceImportTaskResponse`(设备导入任务)
**C 端 DTO**
- `AssetInfoResponse`B1 资产信息)
- `BoundCardInfo`client_asset_dto.go 中的 C 端子结构)
- `DeviceCardItem`F1 设备卡列表项)
**description 标签统一格式**
```go
RealnamePolicy string `json:"realname_policy" description:"实名认证策略 (none:无需实名, before_order:先实名后充值/购买, after_order:先充值/购买后实名)"`
```
#### Scenario: 资产详情接口返回 realname_policy
- **WHEN** 后台管理员查询 IoT 卡详情或资产解析
- **THEN** 响应中包含 realname_policy 字段
#### Scenario: C 端资产信息接口返回 realname_policy
- **WHEN** C 端用户调用 GET /api/c/v1/asset/info
- **THEN** 响应中包含 realname_policy 字段(前端可据此展示提示)