Files
junhong_cmp_fiber/.scratch/ur45-exchange-asset-search/PRD.md
2026-07-21 15:26:07 +09:00

90 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PRDUR#45 换货资产标识与新旧资产独立搜索
Status: ready-for-agent
---
## Problem Statement
换货单中的卡资产标识没有统一:旧资产可能保存虚拟号,新资产可能原样保存操作员输入的 ICCID、接入号或虚拟号导致同类记录展示不一致。现有列表又只有一个同时匹配新旧资产快照的 `identifier` 参数,无法明确查“旧资产”还是“新资产”,也无法稳定通过卡的其他标识找到对应换货单。
## Solution
新建换货单时,将卡的新旧资产标识统一规范为 ICCID设备使用稳定的设备号。换货列表用 `old_asset_keyword``new_asset_keyword` 替代通用 `identifier`,先将关键词解析为候选卡/设备 ID再按换货单的新旧资产主键筛选。两个参数可独立使用同时提供时按 AND 组合。
## User Stories
1. 作为运营人员,我希望换货列表分别展示旧资产和新资产,避免混淆换出和换入对象。
2. 作为运营人员,我希望通过 ICCID、接入号或虚拟号搜索卡对应的旧资产换货记录。
3. 作为运营人员,我希望用相同标识搜索新资产,但不会误命中旧资产。
4. 作为运营人员,我希望同时填写新旧资产条件,以定位一条明确的换货关系。
5. 作为维护人员,我希望新换货单保存规范化快照,历史展示不再取决于操作员当时输入了哪种标识。
6. 作为维护人员,我希望大结果集查询不逐条反查资产,也不绕过换货单数据权限。
## Implementation Decisions
### 快照规范
- 卡资产的 `old_asset_identifier``new_asset_identifier` 均保存已解析卡记录的完整 ICCID不保存请求原文、接入号或虚拟号。
- 设备资产优先保存虚拟号;虚拟号为空时依次使用 IMEI、SN确保保存稳定的设备标识。
- 规范化覆盖物流换货和直接换货:创建旧资产快照、直接换货的新资产快照、物流发货时的新资产快照均使用同一解析能力。
- 请求仍可使用接口当前支持的任一资产标识定位资产,但保存快照必须取解析后的权威字段。
- 历史换货单不回填、不改写;历史列表继续按原快照展示,新的搜索能力通过资产主键关联命中历史记录。
### 列表接口
- 复用 `GET /api/admin/exchanges`
- 新增可选 `old_asset_keyword``new_asset_keyword`,各自最长 100 个字符。
- 移除新契约中的通用 `identifier`;前后端在同一维护窗口切换,不再把一个参数解释为“新资产或旧资产”。
- 单独提供旧资产关键词时只过滤 `old_asset_type + old_asset_id`;单独提供新资产关键词时只过滤 `new_asset_type + new_asset_id`
- 两个关键词同时提供时按 AND 组合,并继续与状态、流程类型、创建时间和分页条件按 AND 组合。
- 空参数不增加对应条件;无候选资产或无换货单命中时返回成功空分页,不返回 404。
- 卡关键词对 ICCID、接入号、虚拟号做包含匹配设备关键词对虚拟号、IMEI、SN 做包含匹配。
- 候选资产查询必须排除软删除资产;换货单查询继续排除软删除记录并应用现有店铺数据范围。
- 候选 ID 解析和换货单过滤使用固定次数的批量查询或数据库子查询,不允许按换货单逐行读取卡或设备。
- `total``items` 必须使用完全相同的过滤条件,排序继续使用创建时间倒序。
- 响应继续分别返回新旧资产类型、ID、快照标识、状态及状态名称不改变统一响应外层结构。
### 校验、权限与架构
- 列表 Handler 对整个请求 DTO 执行校验;非法长度、分页、状态、流程类型或时间参数统一返回 HTTP 400、`code=1001``msg=参数验证失败`,详细原因仅写日志。
- 代理账号只能查询其现有店铺范围内的换货单;关键词解析不得扩大最终换货单范围。
- 无权限和不存在的换货单继续使用现有防枚举错误策略,不向客户端暴露候选资产数量或主键。
- 快照规范属于换货写用例的一部分,应由统一资产解析/规范化能力提供,不在多个 Handler 或 Service 分支复制。
- 列表属于读取用例,可在现有 Store 上做简单增量;若候选解析和分页组合已达到复杂查询程度,则收口到 Exchange Query但不得为此迁移整个换货模块。
- 数据库或候选查询失败返回脱敏的统一内部错误,不得降级为“无结果”。
### 前端与文档
- 换货列表筛选区将单一资产搜索拆为“旧资产”和“新资产”两个输入框。
- 空值不提交;两个非空值同时提交并展示 AND 查询结果。
- 表格分别显示旧资产类型/标识和新资产类型/标识;卡直接展示后端 ICCID设备展示后端设备号。
- 前端不把接入号或虚拟号转换为 ICCID也不在本地过滤当前页。
- 沿用列表加载、空态、失败反馈和分页交互。
- 更新 OpenAPI 参数及描述,重新生成文档;新增 UR#45 中文总结并更新 README 索引。
## Testing Decisions
- 写用例测试分别用 ICCID、接入号、虚拟号创建卡换货验证新旧快照始终为数据库 ICCID。
- 设备测试覆盖虚拟号、IMEI、SN 输入以及标识优先级。
- 列表 HTTP 集成测试覆盖仅旧关键词、仅新关键词、两个关键词 AND、与状态/时间组合、空参数、无匹配和非法参数。
- 覆盖卡 ICCID、接入号、虚拟号以及设备虚拟号、IMEI、SN 的候选映射。
- 构造历史非规范快照,验证不回填但仍可通过资产主键搜索命中。
- 代理、平台和超级管理员测试验证现有换货单数据范围不被关键词绕过。
- 使用真实 PostgreSQL 执行代表性大结果集测试,验证查询次数固定、无逐行反查,计数和分页结果一致并满足项目性能目标。
- 验证数据库错误返回脱敏 500不能返回空列表掩盖故障。
- 前端人工验收新旧字段不混列、组合搜索、清空、分页和错误状态。
## Out of Scope
- 不回填或清洗历史换货快照。
- 不新增独立搜索接口或换货关系表。
- 不改变换货状态机、归属继承、资料迁移或完成规则。
- 不做跨新旧资产 OR 搜索;旧通用 `identifier` 不保留为第二套长期语义。
- 不由前端解析或规范化资产标识。
## Further Notes
- UR#45 负责“快照规范与列表检索”;资产详情前代/后代关系由 UR#86 提供,店铺继承由 UR#98 提供。
- UR#86 应直接使用这里形成的不可变快照;历史非规范快照保持原样。