现象
用户成功打开某个监控实例后,应用会记录“最近访问”;返回监控首页时,这条合法记录却可能消失。正常情况下应按稳定实例 ID 恢复原对象,当前实现先把 ID 拆成关键字,再拿关键字搜索实例名称,ID 与展示名称不相同时就找不到。
触发场景
实例 ID 与展示名称不同即可触发,例如 ID 为 ('k8s-prod',)、名称为“生产集群”,或 ID 为 ('h1',)、名称为“主机1”。用户访问详情形成本地记录,随后打开默认的最近访问 Tab;每条最近记录都会走一次该恢复路径。
业务影响
最近访问这一默认导航能力会对仓内已有的合法实例形态确定性失效,用户看到空列表并误以为历史没有保存。多个恢复失败还会被 Promise.allSettled 静默折叠,页面无法区分“确实没有记录”和“全部解析失败”。
根因(设计层)
本地存储只保留稳定 ID,恢复函数却把由 ID 解析出的片段传给服务端 name 过滤,再从结果里精确匹配 ID:
const lookup = parseMonitorInstanceLookupHints ( instanceId ) ;
await apiGet ( url , { name : keyword } ) ;
return results . find ( ( item ) => item . id === instanceId ) ;
服务端 name 参数执行的是 name__icontains,不是实例 ID 查询。恢复契约把“标识符”和“展示名称”混成了同一个检索字段。
涉及文件
文件:行
说明
mobile/src/features/monitor/recent-views-storage.ts:20
旧存储契约只持久化 objectId 与 instanceId
mobile/src/features/monitor/model.ts:215
将 tuple 形式 instanceId 拆成名称 hint
mobile/src/features/monitor/adapter.ts:108
把这些 ID 片段逐个传给 name 参数
mobile/src/features/monitor/adapter.ts:258
最近记录并行恢复,失败与 null 最终被静默丢弃
server/apps/monitor/services/monitor_instance.py:716
服务端 name 参数仅执行 name__icontains
调用链
flowchart TD
A["详情页记录 objectId 与 instanceId"] --> B["默认最近访问 Tab 读取旧记录"]
B --> C["getMonitorInstance 拆分 instanceId"]
C --> D["list 接口按 name 过滤"]
D -.-> E["ID 与名称不同则返回空"]
E -.-> F["allSettled 将记录静默丢弃"]
Loading
证据 / 复现
adapter.ts:108-133 明确把 instanceId 拆出的值传给 name,随后才按 ID 精确查找;服务端 monitor_instance.py:722-724,739-741 对 name 使用 name__icontains。recent-views-storage.ts:20-38 证实存量 localStorage 只有 ID,没有可依赖的 name。服务端测试数据已包含 ('k8s-prod',)/“生产集群”、('i1',)/“remote-host”、('h1',)/“主机1”等合法 ID/名称不等形态;把这些 ID 写入最近记录后,名称过滤返回空。现有移动测试只检查符号和路由文本存在,没有行为断言。
建议修复方向
优先在现有列表接口增加可选的精确 instance_id 条件,或提供等价的单实例查询;查询必须复用当前团队、超级用户与实例级权限作用域。移动端恢复时先用精确 ID,旧服务端尚不支持时可做能力探测并回退现有路径;同时让“部分失败、全部失败、确实为空”成为可区分状态。
功能影响 / 回归风险
按接口与权限边界迁移处理。存量调用方继续使用原 name 参数,新增 instance_id 为可选参数,不翻转默认行为、不新增必传字段、不删除响应字段;旧 localStorage 只有 ID,修复必须直接兼容,不能要求 name。服务端先部署并保留原 403/404 失败方式,移动端随后能力探测启用;新查询必须经过与列表相同的团队和实例权限过滤,禁止用裸主键绕过。无需数据迁移,回滚移动调用即可恢复旧路径。兼容测试覆盖旧存储、ID/名称不等、重命名、复合 ID、删除、越权、跨团队和旧 name 调用方。
严重性判断
仓内已有合法数据形态能确定性触发,且影响监控根页默认 Tab;但不会损坏实例数据,范围限于最近访问恢复,因此定 Medium。需要前后端可选契约、权限复用与兼容测试,工作量为 M。
严重度:Medium 工作量:M
负责人:@yvtian0107
现象
用户成功打开某个监控实例后,应用会记录“最近访问”;返回监控首页时,这条合法记录却可能消失。正常情况下应按稳定实例 ID 恢复原对象,当前实现先把 ID 拆成关键字,再拿关键字搜索实例名称,ID 与展示名称不相同时就找不到。
触发场景
实例 ID 与展示名称不同即可触发,例如 ID 为
('k8s-prod',)、名称为“生产集群”,或 ID 为('h1',)、名称为“主机1”。用户访问详情形成本地记录,随后打开默认的最近访问 Tab;每条最近记录都会走一次该恢复路径。业务影响
最近访问这一默认导航能力会对仓内已有的合法实例形态确定性失效,用户看到空列表并误以为历史没有保存。多个恢复失败还会被
Promise.allSettled静默折叠,页面无法区分“确实没有记录”和“全部解析失败”。根因(设计层)
本地存储只保留稳定 ID,恢复函数却把由 ID 解析出的片段传给服务端
name过滤,再从结果里精确匹配 ID:服务端
name参数执行的是name__icontains,不是实例 ID 查询。恢复契约把“标识符”和“展示名称”混成了同一个检索字段。涉及文件
mobile/src/features/monitor/recent-views-storage.ts:20mobile/src/features/monitor/model.ts:215mobile/src/features/monitor/adapter.ts:108name参数mobile/src/features/monitor/adapter.ts:258server/apps/monitor/services/monitor_instance.py:716name参数仅执行name__icontains调用链
flowchart TD A["详情页记录 objectId 与 instanceId"] --> B["默认最近访问 Tab 读取旧记录"] B --> C["getMonitorInstance 拆分 instanceId"] C --> D["list 接口按 name 过滤"] D -.-> E["ID 与名称不同则返回空"] E -.-> F["allSettled 将记录静默丢弃"]证据 / 复现
adapter.ts:108-133明确把 instanceId 拆出的值传给name,随后才按 ID 精确查找;服务端monitor_instance.py:722-724,739-741对name使用name__icontains。recent-views-storage.ts:20-38证实存量 localStorage 只有 ID,没有可依赖的 name。服务端测试数据已包含('k8s-prod',)/“生产集群”、('i1',)/“remote-host”、('h1',)/“主机1”等合法 ID/名称不等形态;把这些 ID 写入最近记录后,名称过滤返回空。现有移动测试只检查符号和路由文本存在,没有行为断言。建议修复方向
优先在现有列表接口增加可选的精确
instance_id条件,或提供等价的单实例查询;查询必须复用当前团队、超级用户与实例级权限作用域。移动端恢复时先用精确 ID,旧服务端尚不支持时可做能力探测并回退现有路径;同时让“部分失败、全部失败、确实为空”成为可区分状态。功能影响 / 回归风险
按接口与权限边界迁移处理。存量调用方继续使用原
name参数,新增instance_id为可选参数,不翻转默认行为、不新增必传字段、不删除响应字段;旧 localStorage 只有 ID,修复必须直接兼容,不能要求 name。服务端先部署并保留原 403/404 失败方式,移动端随后能力探测启用;新查询必须经过与列表相同的团队和实例权限过滤,禁止用裸主键绕过。无需数据迁移,回滚移动调用即可恢复旧路径。兼容测试覆盖旧存储、ID/名称不等、重命名、复合 ID、删除、越权、跨团队和旧 name 调用方。严重性判断
仓内已有合法数据形态能确定性触发,且影响监控根页默认 Tab;但不会损坏实例数据,范围限于最近访问恢复,因此定 Medium。需要前后端可选契约、权限复用与兼容测试,工作量为 M。
严重度:Medium 工作量:M
负责人:@yvtian0107