# Capability: 资产实名策略 ## ADDED Requirements ### Requirement: 资产实名策略字段定义 系统 SHALL 在 `IotCard` 和 `Device` 模型上各新增 `realname_policy` 字段(VARCHAR(20),NOT NULL,DEFAULT '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** 系统返回错误码 1187(CodeNeedRealname) #### 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** 系统返回错误码 1187(CodeNeedRealname),订单不创建 #### 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=ICCID,realname_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 字段(前端可据此展示提示)