# 01 — 资产详情展示可控前代换货信息

**What to build:** 在统一资产详情中增加稳定的 `exchange_trace` 对象，并打通当前资产作为换货新资产时的前代查询。关系以未软删除的已完成换货单为权威来源，展示旧资产的不可变标识快照和换货单号；关联资产仍在调用方现有数据权限内时返回真实 ID 并允许跳转，无权限时保留历史文本但隐藏 ID。主架构通道为 Query，数据库索引为辅助 Infrastructure；完整边界止于“当前资产 → 前代”的只读投影，不迁移换货状态机、换货写侧或资产详情的其他旧读取逻辑，也不新建关系表。

**Blocked by:** None — can start immediately

**Status:** completed

- [x] 卡和设备通过统一资产详情查询时始终返回 `exchange_trace` 对象；没有前代时 `previous_asset` 为 `null`，不省略整个对象。
- [x] 当前资产作为新资产出现在未软删除且状态为已完成的换货单中时，`previous_asset` 返回旧资产类型、换货单快照标识、换货单号、可空资产 ID 和可见性标记。
- [x] 前代标识直接使用换货单保存的不可变快照，不回查或改写关联资产的当前标识。
- [x] 关联前代资产通过现有卡或设备数据权限检查；有权限时返回真实 ID 和 `can_view=true`，无权限时返回 `asset_id=null` 和 `can_view=false`，且不返回店铺、客户、套餐、钱包、状态等额外信息。
- [x] 关系查询不应用关联资产的数据权限过滤，不会因关联资产不可见而抹掉换货关系；当前资产本身仍先经过现有权限校验，未授权请求维持原有防枚举响应。
- [x] 待填写、待发货、已发货待确认、已取消及软删除换货单均不会形成前代关系。
- [x] 查询存储故障返回统一脱敏内部错误，不会被降级为“无换货关系”，并记录包含查询方向和当前资产上下文的中文错误日志。
- [x] 增加已完成且未软删除范围内支持“新资产类型 + 新资产 ID”最新记录查询的非唯一部分 B-tree 索引，并验证向上迁移、向下迁移和代表性查询计划。
- [x] 同一资产存在多条“当前资产为新资产”的异常完成记录时，按 `completed_at` 降序、主键降序确定性选择最新一条，并记录包含当前资产、候选数量和最终换货单的中文异常日志。
- [x] Query 测试覆盖无关系、卡前代、设备前代、可见前代、不可见前代、非完成状态、软删除记录和异常重复完成记录。
- [x] HTTP 集成测试使用真实 PostgreSQL、Redis、JWT 和认证中间件，贯穿当前资产权限、资产解析、前代 Query 与统一响应格式，并证明当前资产无权限时不会因换货关系泄露其存在。
- [x] 更新资产详情 OpenAPI 的前代响应契约，明确 `exchange_trace` 始终存在、`previous_asset` 可为 `null`，以及 `can_view` 与可空 `asset_id` 的跳转规则。
- [x] 在 UR#86 中文总结中记录前代查询的架构通道、权限防泄露、异常选择规则、索引发布与回滚方式，并准备无换货、前代可见和前代不可见的人工验收步骤。
