我填的经纬度,保存后变成了湖州
用户反馈:在某后台管理系统填入门店经纬度 30.550353,120.728027,保存后再打开,变成了 30.56369,120.007133。差距很大,不像精度截断,也不像格式转换。第一反应是前端 v-model 绑错了,或者读取路径有 mutator——但翻来翻去都没问题。
现象
- 浏览器 DevTools → Network → 保存请求 payload:前端发出的就是正确的
30.550353,120.728027 - 页面重新打开后 detail 回显
30.56369,120.007133 - 数据库直接查,
lat/lng字段里存的就是30.56369,120.007133 - 把门店地址填成「测试 123」重新新建一条,lat/lng 还是
30.56369,120.007133
第三条是关键:DB 里就是错的,跟读取路径无关,问题在写入阶段。
根因
后端服务在 create 方法里做了一个「地址反查坐标」的数据增强逻辑:
// 反模式:反查 + 失败硬编码兜底
Map<String, String> json = getGeocoderLatitude(store.getAddress());
if (json != null) {
store.setLat(json.get("lat")); // 覆盖前端值
store.setLng(json.get("lng"));
} else {
store.setLat("30.56369"); // 地图 API 失败 → 硬编码某地坐标
store.setLng("120.007133");
}
save(store);三个错叠加:
- 从不检查前端是否已传值——反查无条件执行,前端入参被静默丢弃
- 反查作为不可绕过的副作用——与写入完全耦合
- 失败兜底用硬编码业务坐标——地图 API 超时、地址含「测试」、quota 用完,全都走
else,结果全表新建记录都共享同一个坐标
任何外部反查(地理编码、手机号查会员、地址查门店 ID)一旦失败,fallback 里绝不能写一个真实的业务坐标 / 真实手机号 / 真实订单号作为默认值。
这个坏数据看起来"像是正常数据",不会触发任何告警,会在 DB 里安静地躺着,直到有人认真核对才发现全表污染。
排查步骤
Step 1 — 先查 DB,确认是写入问题还是读取问题
SELECT id, lat, lng, create_at, update_at, update_by
FROM store WHERE id = '刚保存的记录 ID' LIMIT 1;DB 里就是 30.56369 → 问题在写入阶段,读取路径不用看。
Step 2 — 看元数据,区分 insert 还是 update 分支
update_by IS NULL 且 update_at = create_at → 该记录从未走过 update 分支,元凶在 insert 路径。
Step 3 — 用 DB 里的实际值去全栈 grep
grep -rn "30.56369\|120.007133" /path/to/backend命中 StoreServiceImpl.java:782,找到硬编码常量,根因证据链闭合。
正解
层 1(必做):尊重前端入参,仅在前端未传时才触发反查
// 前端已传坐标 → 跳过反查,直接用
if (StringUtils.isBlank(store.getLat()) || StringUtils.isBlank(store.getLng())) {
Map<String, String> json = getGeocoderLatitude(store.getAddress());
if (json != null) {
store.setLat(json.get("lat"));
store.setLng(json.get("lng"));
}
// 反查失败 → 保留 null,不写任何业务默认值
}
save(store);层 2(兜底):失败时保留 null 或 throw,绝不写硬编码业务值
层 3(双后端特殊场景):若架构约定不改某一侧服务,可在另一侧写入完成后串行覆盖:前端拿到新 ID 后立即调「干净的」服务用原始 lat/lng 再写一次,注意是串行而非 Promise.all 并发,否则顺序无法保证。
反查类数据增强应该是 opt-in:「前端没传 → 服务端补」,而不是「服务端无条件覆盖,外部 API 挂了再给个默认值」。update_by / update_at 元数据是免费的法医证据,业务表建表时务必加上。
一句话外卖
服务端在写入路径做反查增强时,先判断前端入参是否已有值;fallback 分支只允许保留 null 或抛异常,永远不要写一个"看起来正常"的硬编码业务默认值。