修复待处理换货资产归属
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m53s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m53s
This commit is contained in:
@@ -519,7 +519,12 @@ func (s *Service) GetPending(ctx context.Context, identifier string) (*dto.Clien
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !s.customerOwnsAsset(ctx, asset) {
|
||||
customerID := middleware.GetCustomerIDFromContext(ctx)
|
||||
owned, err := s.customerBinding.OwnsAsset(ctx, customerID, asset.AssetType, asset.AssetID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !owned {
|
||||
return nil, errors.New(errors.CodeAssetNotFound)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-24
|
||||
@@ -0,0 +1,38 @@
|
||||
## Context
|
||||
|
||||
待处理换货读取已先解析资产、再校验个人客户归属、最后读取物流换货单。换货服务已经注入个人客户资产归属服务,但当前读取路径未使用它,而是直接以资产虚拟号查询设备号绑定。无虚拟号物联网卡应使用 ICCID 绑定;空字符串被用作设备号查询时会把无关历史绑定误判为归属。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 让待处理换货读取复用个人客户资产归属的单一权威判定。
|
||||
- 消除空虚拟号导致的错误可见性与错误拒绝。
|
||||
- 保持换货单筛选、响应、错误码和路由不变。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不变更个人客户绑定模型、现有绑定数据或换货单数据。
|
||||
- 不处理手机号—资产关联、测试环境配置或其他个人客户资产读取入口。
|
||||
- 不引入迁移、兼容别名或新的抽象层。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 用已注入的客户归属服务替换旧本地校验
|
||||
|
||||
`GetPending` 从用户上下文取得当前个人客户 ID,并以已解析资产的类型和 ID 调用现有客户归属服务。该服务已按资产类型与虚拟号是否为空路由到设备号或 ICCID 绑定表,是当前风险换卡等 C 端路径采用的归属口径。
|
||||
|
||||
不修改归属服务,也不在换货服务复制 ICCID/虚拟号分支。这样保证后续绑定规则演进只有一个判定来源。
|
||||
|
||||
### 保持外部不可枚举语义
|
||||
|
||||
归属查询返回未拥有、资产不存在或底层不可见结果时,待处理换货接口继续映射为既有“资产不存在”。已通过归属校验但没有活动物流换货单时继续成功返回空数据。
|
||||
|
||||
### 回归验证聚焦无虚拟号与空键
|
||||
|
||||
在现有 Go 测试基础设施可覆盖时,测试应构造无虚拟号卡、有效 ICCID 绑定和待处理物流换货单,断言返回待处理投影;再构造仅有空设备号绑定的客户,断言不能读取同一卡。同步保留有虚拟号资产的回归样例,证明既有路径未改变。若当前仓库无可隔离的持久化测试面,则使用维护者指定测试环境的两张卡完成原请求 smoke,并记录脱敏响应。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 归属服务读取资产一次,`GetPending` 已解析资产后会产生重复读取;本次优先消除授权歧义,不为单次读取引入新接口或缓存。
|
||||
- 既有依赖空虚拟号误授权读取换货单的请求将被拒绝;这是纠正越权行为,不提供兼容路径。
|
||||
@@ -0,0 +1,26 @@
|
||||
## Why
|
||||
|
||||
`GET /api/c/v1/exchange/pending` 对无虚拟号物联网卡沿用仅按设备虚拟号查询的旧归属校验,无法按卡 ICCID 绑定确认当前个人客户,导致已存在的待处理物流换货单被错误返回为“资产不存在”。该判定还会将空虚拟号历史绑定误用为资产归属依据,存在越权读取风险。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 统一个人客户待处理换货查询与当前客户资产归属服务的判定口径。
|
||||
- 无虚拟号物联网卡按当前有效 ICCID 绑定校验归属;有虚拟号物联网卡与设备保持既有设备号绑定判定。
|
||||
- 移除待处理换货查询中将空虚拟号作为资产归属键的旧查询路径。
|
||||
- 保持待处理物流换货单的状态范围、响应结构,以及对无归属资产返回“资产不存在”的外部契约不变。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- 无。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `personal-customer`: 修正个人客户查询待处理换货单时的资产归属判定,使已绑定的无虚拟号物联网卡可读取其待处理物流换货单。
|
||||
|
||||
## Impact
|
||||
|
||||
- 代码:`internal/service/exchange/service.go`、服务装配与覆盖该归属分支的测试。
|
||||
- 不涉及数据库 Schema、迁移、外部渠道、手机号—资产关联功能或测试环境部署配置。
|
||||
- C 端 `GET /api/c/v1/exchange/pending` 对无虚拟号物联网卡的可见性恢复为其当前有效个人客户 ICCID 绑定范围。
|
||||
@@ -0,0 +1,35 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 资产归属查询
|
||||
|
||||
系统 SHALL 只向个人客户返回当前有效绑定的卡与设备资产;所有以资产标识读取个人客户换货信息的入口 SHALL 依据资产类型采用与个人客户绑定一致的归属判定。对于有虚拟号的物联网卡和设备,归属由当前有效设备号绑定确定;对于无虚拟号的物联网卡,归属由当前有效 ICCID 绑定确定。空虚拟号不得作为任何资产的归属键。
|
||||
#### Scenario: 资产归属查询
|
||||
|
||||
- **GIVEN** 客户已认证
|
||||
- **WHEN** 查询资产列表或详情
|
||||
- **THEN** 返回该客户可见的资产而不包含其他客户资产
|
||||
|
||||
|
||||
#### Scenario: 无虚拟号卡查询待处理物流换货单
|
||||
|
||||
- **GIVEN** 当前个人客户存在一张无虚拟号物联网卡的有效 ICCID 绑定,且该卡存在状态为待填写收货信息、待发货或已发货的物流换货单
|
||||
- **WHEN** 客户以该卡任一已登记标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 响应返回该物流换货单的既有待处理投影
|
||||
|
||||
#### Scenario: 无虚拟号卡无待处理物流换货单
|
||||
|
||||
- **GIVEN** 当前个人客户存在一张无虚拟号物联网卡的有效 ICCID 绑定,且该卡不存在状态为待填写收货信息、待发货或已发货的物流换货单
|
||||
- **WHEN** 客户以该卡任一已登记标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 请求成功且数据为空
|
||||
|
||||
#### Scenario: 空虚拟号历史绑定不授予换货可见性
|
||||
|
||||
- **GIVEN** 当前个人客户仅存在空虚拟号的历史设备号绑定,且未拥有目标无虚拟号物联网卡的有效 ICCID 绑定
|
||||
- **WHEN** 客户以该卡标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 系统返回既有“资产不存在”错误,且不得返回该卡的换货信息
|
||||
|
||||
#### Scenario: 有虚拟号资产保持既有换货可见性
|
||||
|
||||
- **GIVEN** 当前个人客户拥有一项有虚拟号的物联网卡或设备资产,且该资产存在待处理物流换货单
|
||||
- **WHEN** 客户以该资产标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 响应保持返回该换货单的既有待处理投影
|
||||
@@ -0,0 +1,10 @@
|
||||
## 1. 待处理换货归属判定
|
||||
|
||||
- [x] 1.1 在 `internal/service/exchange/service.go` 将 `GetPending` 的个人客户归属校验改为复用已注入的客户资产归属服务,并将未拥有资产统一保持为既有“资产不存在”错误。
|
||||
- [x] 1.2 删除仅用于待处理换货读取的旧本地归属判定及其空虚拟号查询路径,保留其他换货流程现有行为。
|
||||
|
||||
## 2. 回归验证
|
||||
|
||||
- [ ] 2.1 使用维护者指定测试环境验证:有效 ICCID 绑定的无虚拟号卡请求 `GET /api/c/v1/exchange/pending` 返回既有待处理物流换货单;无待处理单时成功返回空数据。
|
||||
- [ ] 2.2 使用未拥有目标卡有效 ICCID 绑定、但存在空虚拟号历史设备绑定的个人客户验证:同一接口仍返回既有“资产不存在”,不泄露换货信息。
|
||||
- [ ] 2.3 运行 `gofmt -w internal/service/exchange/service.go`、`go build ./cmd/api ./cmd/worker`、`go run cmd/gendocs/main.go`、`openspec validate --all`,并记录测试环境 smoke 的脱敏结果。
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
### Requirement: 资产归属查询
|
||||
|
||||
系统 SHALL 只向个人客户返回当前绑定或可识别的卡与设备资产。
|
||||
系统 SHALL 只向个人客户返回当前有效绑定的卡与设备资产。所有以资产标识读取个人客户换货信息的入口 SHALL 依据资产类型采用与个人客户绑定一致的归属判定:有虚拟号的物联网卡和设备按当前有效设备号绑定确定归属;无虚拟号的物联网卡按当前有效 ICCID 绑定确定归属。空虚拟号不得作为任何资产的归属键。
|
||||
|
||||
#### Scenario: 资产归属查询
|
||||
|
||||
@@ -30,6 +30,30 @@
|
||||
- **WHEN** 查询资产列表或详情
|
||||
- **THEN** 返回该客户可见的资产而不包含其他客户资产
|
||||
|
||||
#### Scenario: 无虚拟号卡查询待处理物流换货单
|
||||
|
||||
- **GIVEN** 当前个人客户存在一张无虚拟号物联网卡的有效 ICCID 绑定,且该卡存在状态为待填写收货信息、待发货或已发货的物流换货单
|
||||
- **WHEN** 客户以该卡任一已登记标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 响应返回该物流换货单的既有待处理投影
|
||||
|
||||
#### Scenario: 无虚拟号卡无待处理物流换货单
|
||||
|
||||
- **GIVEN** 当前个人客户存在一张无虚拟号物联网卡的有效 ICCID 绑定,且该卡不存在状态为待填写收货信息、待发货或已发货的物流换货单
|
||||
- **WHEN** 客户以该卡任一已登记标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 请求成功且数据为空
|
||||
|
||||
#### Scenario: 空虚拟号历史绑定不授予换货可见性
|
||||
|
||||
- **GIVEN** 当前个人客户仅存在空虚拟号的历史设备号绑定,且未拥有目标无虚拟号物联网卡的有效 ICCID 绑定
|
||||
- **WHEN** 客户以该卡标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 系统返回既有“资产不存在”错误,且不得返回该卡的换货信息
|
||||
|
||||
#### Scenario: 有虚拟号资产保持既有换货可见性
|
||||
|
||||
- **GIVEN** 当前个人客户拥有一项有虚拟号的物联网卡或设备资产,且该资产存在待处理物流换货单
|
||||
- **WHEN** 客户以该资产标识请求 `GET /api/c/v1/exchange/pending`
|
||||
- **THEN** 响应保持返回该换货单的既有待处理投影
|
||||
|
||||
### Requirement: 资产信息展示当前可用或已用完主套餐
|
||||
|
||||
系统 SHALL 在个人客户查询其已绑定资产的 `GET /api/c/v1/asset/info` 时,将当前世代中状态为生效中(`status=1`)或已用完(`status=2`)的主套餐作为当前套餐摘要返回。返回的套餐摘要 SHALL 包含套餐名称、套餐使用记录 ID、开始时间、到期时间、真流量与虚流量指标。待生效、已过期和已失效的主套餐不得作为当前套餐摘要返回。
|
||||
|
||||
Reference in New Issue
Block a user