实现换货资产快照与新旧独立搜索
This commit is contained in:
@@ -3323,7 +3323,7 @@ components:
|
||||
nullable: true
|
||||
type: boolean
|
||||
new_identifier:
|
||||
description: 新资产标识符,direct 流程必填(ICCID/虚拟号/IMEI/SN)
|
||||
description: 新资产输入标识,direct 流程必填;卡支持 ICCID、接入号、虚拟号,设备支持虚拟号、IMEI、SN
|
||||
maxLength: 100
|
||||
minLength: 1
|
||||
type: string
|
||||
@@ -3331,7 +3331,7 @@ components:
|
||||
description: 旧资产类型 (iot_card:物联网卡, device:设备)
|
||||
type: string
|
||||
old_identifier:
|
||||
description: 旧资产标识符(ICCID/虚拟号/IMEI/SN)
|
||||
description: 旧资产输入标识,卡支持 ICCID、接入号、虚拟号,设备支持虚拟号、IMEI、SN;响应快照使用权威标识
|
||||
maxLength: 100
|
||||
minLength: 1
|
||||
type: string
|
||||
@@ -5175,7 +5175,7 @@ components:
|
||||
nullable: true
|
||||
type: integer
|
||||
new_asset_identifier:
|
||||
description: 新资产标识符
|
||||
description: 新资产权威快照,卡为完整 ICCID,设备按虚拟号、IMEI、SN 优先级取值;历史记录保持原值
|
||||
type: string
|
||||
new_asset_type:
|
||||
description: 新资产类型 (iot_card:物联网卡, device:设备)
|
||||
@@ -5185,7 +5185,7 @@ components:
|
||||
minimum: 0
|
||||
type: integer
|
||||
old_asset_identifier:
|
||||
description: 旧资产标识符
|
||||
description: 旧资产权威快照,卡为完整 ICCID,设备按虚拟号、IMEI、SN 优先级取值;历史记录保持原值
|
||||
type: string
|
||||
old_asset_type:
|
||||
description: 旧资产类型 (iot_card:物联网卡, device:设备)
|
||||
@@ -5247,7 +5247,7 @@ components:
|
||||
description: 是否执行全量迁移 (true:执行, false:不执行)
|
||||
type: boolean
|
||||
new_identifier:
|
||||
description: 新资产标识符(ICCID/虚拟号/IMEI/SN)
|
||||
description: 新资产输入标识,卡支持 ICCID、接入号、虚拟号,设备支持虚拟号、IMEI、SN;保存快照使用权威标识
|
||||
maxLength: 100
|
||||
minLength: 1
|
||||
type: string
|
||||
@@ -16195,6 +16195,7 @@ paths:
|
||||
schema:
|
||||
description: 页码
|
||||
minimum: 1
|
||||
nullable: true
|
||||
type: integer
|
||||
- description: 每页数量
|
||||
in: query
|
||||
@@ -16203,6 +16204,7 @@ paths:
|
||||
description: 每页数量
|
||||
maximum: 100
|
||||
minimum: 1
|
||||
nullable: true
|
||||
type: integer
|
||||
- description: 换货状态 (1:待填写信息, 2:待发货, 3:已发货待确认, 4:已完成, 5:已取消)
|
||||
in: query
|
||||
@@ -16222,11 +16224,18 @@ paths:
|
||||
- shipping
|
||||
- direct
|
||||
type: string
|
||||
- description: 资产标识符搜索(旧资产/新资产标识符模糊匹配)
|
||||
- description: 旧资产关键词,支持卡 ICCID、接入号、虚拟号或设备虚拟号、IMEI、SN;与新资产关键词按 AND 组合
|
||||
in: query
|
||||
name: identifier
|
||||
name: old_asset_keyword
|
||||
schema:
|
||||
description: 资产标识符搜索(旧资产/新资产标识符模糊匹配)
|
||||
description: 旧资产关键词,支持卡 ICCID、接入号、虚拟号或设备虚拟号、IMEI、SN;与新资产关键词按 AND 组合
|
||||
maxLength: 100
|
||||
type: string
|
||||
- description: 新资产关键词,支持卡 ICCID、接入号、虚拟号或设备虚拟号、IMEI、SN;与旧资产关键词按 AND 组合
|
||||
in: query
|
||||
name: new_asset_keyword
|
||||
schema:
|
||||
description: 新资产关键词,支持卡 ICCID、接入号、虚拟号或设备虚拟号、IMEI、SN;与旧资产关键词按 AND 组合
|
||||
maxLength: 100
|
||||
type: string
|
||||
- description: 创建时间起始
|
||||
|
||||
72
docs/ur45-exchange-asset-search/功能总结.md
Normal file
72
docs/ur45-exchange-asset-search/功能总结.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# UR#45 换货资产快照与新旧资产独立搜索功能总结
|
||||
|
||||
## 本次交付范围
|
||||
|
||||
本次完成两张可独立发布的 Ticket:
|
||||
|
||||
- Ticket 01:统一物流换货创建、直接换货创建和物流发货三个写入入口的资产解析与权威快照。
|
||||
- Ticket 02:将 `GET /api/admin/exchanges` 列表读取收口到独立 Query,提供新旧资产独立搜索、完整校验、权限、分页、错误转换、OpenAPI 和真实 PostgreSQL 验证。
|
||||
|
||||
未迁移换货详情、状态机、完成、取消、资料迁移、旧资产转新等旧用例;未回填历史快照;未实现 UR#86 资产前后代关系或 UR#98 店铺继承。
|
||||
|
||||
## 权威快照规则
|
||||
|
||||
- IoT 卡可通过 ICCID、接入号或虚拟号定位,但新写入的 `old_asset_identifier` 和 `new_asset_identifier` 始终保存数据库中的完整 ICCID。
|
||||
- 设备可通过虚拟号、IMEI 或 SN 定位,快照按“虚拟号 → IMEI → SN”选择首个非空稳定标识。
|
||||
- 物流创建的旧资产、直接换货的新旧资产、物流发货的新资产共享同一规范化能力。
|
||||
- 标识解析继续应用既有店铺数据范围、资产类型、状态和并发校验。不存在、类型不匹配或数据库故障使用统一错误体系,不返回底层错误。
|
||||
- 历史换货单保持原快照,不做自动清洗或回填;列表搜索按资产类型和资产主键命中,因此仍可通过资产当前支持的任一标识找到历史记录。
|
||||
|
||||
## 列表搜索契约
|
||||
|
||||
`GET /api/admin/exchanges` 使用以下可选参数:
|
||||
|
||||
- `old_asset_keyword`:最长 100 字符,只过滤旧资产一侧。
|
||||
- `new_asset_keyword`:最长 100 字符,只过滤新资产一侧。
|
||||
- 两者同时提交时按 AND 组合,并继续与 `status`、`flow_type`、`created_at_start`、`created_at_end` 和分页条件按 AND 组合。
|
||||
- 空值不增加对应过滤;无候选或无换货单命中时返回成功空分页。
|
||||
- 卡候选对 ICCID、接入号和虚拟号做包含匹配;设备候选对虚拟号、IMEI 和 SN 做包含匹配;GORM 默认排除软删除资产和换货单。
|
||||
- 旧通用 `identifier` 已从新 DTO 与 OpenAPI 契约移除。
|
||||
|
||||
列表采用 `Handler → Query → GORM/DTO` 通道。候选资产通过数据库子查询参与最终换货单条件,不逐条读取资产;`total` 和 `items` 复用同一过滤链,按 `created_at DESC` 排序。最终换货单查询继续应用既有店铺范围,候选解析不会扩大平台、超级管理员或代理的可见数据。
|
||||
|
||||
## 校验与错误边界
|
||||
|
||||
Handler 对完整请求 DTO 执行校验。显式 `page=0`、`page_size=0`、超上限分页、非法状态、非法流程类型、超长关键词和非法时间格式统一返回 HTTP 400:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1001,
|
||||
"msg": "参数验证失败",
|
||||
"data": null,
|
||||
"timestamp": "RFC3339 时间"
|
||||
}
|
||||
```
|
||||
|
||||
候选或换货单查询失败返回脱敏 HTTP 500、`code=2002`、`msg=数据库错误`,不得将故障降级为空结果,也不向客户端暴露 SQL、候选数量或底层错误。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- 真实 PostgreSQL 写用例测试覆盖卡 ICCID、接入号、虚拟号和设备虚拟号、IMEI、SN 输入,并验证物流创建、直接创建和物流发货的持久化快照。
|
||||
- 设备无虚拟号时的 IMEI、SN 回退规则由规范化单元测试覆盖;历史非规范快照保持原值。
|
||||
- Query 集成测试覆盖六类标识、新旧独立、双关键词 AND、状态和时间组合、空结果、历史快照、软删除资产、店铺范围、分页排序与数据库故障。
|
||||
- 代表性数据集在真实 PostgreSQL 中创建 120 条匹配换货单;连续执行 100 次第 2 页、每页 20 条查询,总数与排序保持一致,每次固定执行 2 条 SQL(计数 + 分页),并断言 P95 `<200ms`、P99 `<500ms`;测试同时执行 `EXPLAIN` 记录等价查询计划,证明没有逐行资产反查。
|
||||
- HTTP 测试验证完整 DTO 校验、独立关键词传递、统一分页外层和脱敏数据库错误。
|
||||
- OpenAPI 使用 `go run ./cmd/gendocs` 重新生成并核验新旧关键词、长度限制、AND 说明、权威快照响应语义以及旧 `identifier` 不再属于列表参数。
|
||||
|
||||
## 前端人工验收清单
|
||||
|
||||
- 将原单一资产输入框拆为“旧资产”和“新资产”两个输入框。
|
||||
- 空值不提交;两个非空值同时提交并按 AND 展示结果。
|
||||
- 表格分别展示旧资产类型/标识和新资产类型/标识,不混列。
|
||||
- 前端不解析接入号、虚拟号或 ICCID,不在当前页本地过滤。
|
||||
- 清空、分页、加载、空态和失败反馈沿用现有交互;失败时不得把旧结果伪装为新查询结果。
|
||||
|
||||
当前仓库没有可实施该页面的前端工程,因此前端部分以接口契约和本清单交付,等待同维护窗口的前端仓库实施与浏览器人工验收。
|
||||
|
||||
## 发布与回滚
|
||||
|
||||
- 发布时后端与前端在同一维护窗口切换:前端停止提交列表 `identifier`,改为提交 `old_asset_keyword` / `new_asset_keyword`。
|
||||
- 本次无数据库迁移,不修改历史数据;后端回滚只需回退应用与 OpenAPI 产物。
|
||||
- 若仅回滚前端,旧 `identifier` 不会产生筛选效果,因此不支持前后端跨版本长期混用;应整体回滚到上一版本。
|
||||
- 发布后重点观察列表 HTTP 5xx、查询耗时和空结果比例;异常时按应用版本整体回滚,不执行数据修复。
|
||||
Reference in New Issue
Block a user