记一次雪花算法 ID 精度丢失的Bug:前端接收到的 Long 被“四舍五入”了?

后端生成的 ID:1961005746230337538

前端收到的 ID:1961005746230337500

------ 少了 38?!这不是 Bug,是 JavaScript 的"安全整数"陷阱!

本文记录一次真实项目中因 雪花算法 ID 精度丢失 导致的线上问题,并给出彻底、可复用的解决方案。


🐛 问题现象:Long 类型在前后端传输中"变短"了

❌ 看似正常的后端代码

java 复制代码
// 使用雪花算法生成 ID
@TableName("user")
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id; // 1961005746230337538
    private String name;
    // getter/setter...
}

接口返回:

javascript 复制代码
{
  "id": 1961005746230337538,
  "name": "Chaya"
}

前端接收到的数据

javascript 复制代码
console.log(user.id); // 输出:1961005746230337500

末尾的 38 没了!被"四舍五入"了!

根本原因:JavaScript 的 Number 精度限制

  • JavaScript 的 Number 类型是 双精度浮点数(64-bit)
  • 它能安全表示的整数范围是:-2^53 + 12^53 - 1 (即 ±9,007,199,254,740,991)。
  • 而雪花算法生成的 ID 通常是 19 位 Long,远超 JS 安全范围。
  • 浏览器在解析 JSON 时,会自动将大整数转换为 Number,导致精度丢失

📌 举例:1961005746230337538 超过 2^53,JS 无法精确表示,自动"对齐"到最近的可表示值 → 1961005746230337500

解决方案:后端将 Long 序列化为字符串!

核心思路

在 Spring Boot 返回 JSON 时,将所有 Long 类型字段自动序列化为字符串,避免前端解析时精度丢失。

配置方式

java 复制代码
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Bean
    public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        jsonConverter.setObjectMapper(objectMapper);
        return jsonConverter;
    }


    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(customJackson2HttpMessageConverter());
        super.addDefaultHttpMessageConverters(converters);
    }

}

🔍 关键代码解析

java 复制代码
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);

作用:注册一个 Jackson 模块,告诉 ObjectMapper:

  • 所有 Longlong 类型,在序列化成 JSON 时,不要转成数字,而是转成字符串!

✅ 效果对比

类型 原始 JSON 修复后 JSON
Long ID "id": 1961005746230337538 "id": "1961005746230337538"
前端接收 ❌ 精度丢失 ✅ 完整字符串,无损

其他解决方案对比

方案 优点 缺点
全局 Long 转 String(本文方案) 一劳永逸,无需改实体类 所有 Long 都变字符串
@JsonFormat(shape = JsonFormat.Shape.STRING) 精准控制字段 每个字段都要加,易遗漏
前端使用 BigInt 不改后端 兼容性差,JSON 不支持 BigInt
ID 返回为 String 类型 类型安全 实体类不"纯洁",影响数据库映射

推荐:全局配置 + 按需排除(如某些计数字段仍保留 Long)

如果某些 Long 字段不需要转字符串(如 agecount),可以自定义序列化器:

java 复制代码
public class CustomLongSerializer extends JsonSerializer<Long> {
    @Override
    public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // 根据字段名或注解判断是否转字符串
        if (isIdField(gen)) {
            gen.writeString(value.toString());
        } else {
            gen.writeNumber(value);
        }
    }
}

但大多数场景下,统一转字符串更简单可靠

相关推荐
仰望星空的凡人11 小时前
【BUG排查】调试瑞萨RH850F1KMS1时候随机出现进入到unused_isr
bug·瑞萨·renesas
蓝蜂物联网1 天前
告别出差!蓝蜂物联网网关让PLC程序远程修改零延迟
物联网·自动化·bug
软测进阶2 天前
【第三章】软件测试缺陷管理:从判断到回归的全流程实践指南
测试工具·bug·源代码管理
lijiatu100862 天前
【ROS2】 忽略局域网多机通信导致数据接收的bug
bug·ros2
张较瘦_3 天前
[论文阅读] 软件工程 | GPS算法:用“路径摘要”当向导,软件模型检测从此告别“瞎找bug”
论文阅读·算法·bug
玩转数据库管理工具FOR DBLENS3 天前
精准测试的密码:解密等价类划分,让Bug无处可逃
数据库·单元测试·测试用例·bug·数据库开发
程序猿阿伟3 天前
《微服务架构下API网关流量控制Bug复盘:从熔断失效到全链路防护》
微服务·架构·bug
funfan05174 天前
奇怪的“bug”--数据库的“隐式转换”行为
数据库·bug
海鸥_5 天前
C++中不加{}导致的BUG
c++·bug