1. 首页

js golang gin雪花算法生成的id返回json化精度丢失问题的解决方案

前俩天刚给自己的一个项目加上了个雪花算法生成订单,好家伙,不生成不要紧,刚生成就出bug了

雪花算法生成id,后期如果要扩展到多服务器的话就方便很多,所以很多id生成都改成了雪花算法

我给购物车的订单id,地址id,用户id设置成由雪花算法生成的id,方便后期扩展到分布式部署

用户上传地址用的就是雪花算法的id,正常生成是没有问题的

但是回传到前端小程序的时候就出bug了,,他的订单使用c.json()返回数据的时候,精度丢失了


比如我的地址id是432812951087353858,传到前端后,这个值就变成了432812951087353860,再生成一个id432812951087353859,返回到前端也变成了432812951087353860。。。

碰到这种问题懵了哪,赶紧查数据库,跟前端对比一下,这怎么传过来的不一样呢,是哪一步导致了数据的失精问题,


查了不少资料才得到,原来数据生成Json的时候,他会默认换成float64再去序列化,导致丢失了俩位的精度,网上给的解决办法是

有种Json化数据的方法可以不丢失精度

        j, _ := json.Marshal(data)
        reg := regexp.MustCompile(`id\":(\d{16,20}),"`)
        l := len(reg.FindAllString(string(j), -1)) //正则匹配16-20位的数字,如果找到了就开始正则替换并解析
        if l != 0 {
            var mapResult map[string]interface{}
            str := reg.ReplaceAllString(string(j), `id": "${1}",`)
            json.Unmarshal([]byte(str), &mapResult)
            data = &mapResult
        }

通过生成后再返回val数据。。。生成后通过fmt 打印出来数据是没有问题,大数正常

但再通过c.json()返回数据后,还会再进行一次json化,,,他的id精度还是丢失了


其中也试过直接用json.marshal转成字符串传到前端,前端再去序列化成对象使用。。。。。特么传到前端再用JSON.parse(str)去获取数据。本来以为这种方法应该可以解决了,但是特么,前端使用这个方法,精度也一样丢失,太特么尴尬了


最后换了一种方法,先转换成正常的json文字,再正则匹配到id大数,把int64换成string字符串,就不存在丢失精度 的问题了

代码贴出来如下

j, _ := json.Marshal(data)
        reg := regexp.MustCompile(`id\":(\d{16,20}),"`)
        l := len(reg.FindAllString(string(j), -1)) //正则匹配16-20位的数字,如果找到了就开始正则替换并解析
        if l != 0 {
            fmt.Printf("\n正则替换前的数据%+v", data)
            var mapResult map[string]interface{}
            str := reg.ReplaceAllString(string(j), `id": "${1}","`)
            json.Unmarshal([]byte(str), &mapResult)
            data = &mapResult
        }

因为返回数据是做了一个统一的返回接口的,所以data数据是一个interface{}类型,就直接把数据转成json文字类型,再正则看是否有id+16-20位的数字,如果有,那么就正则替换把数字转成字符串,如果没有匹配到就像往常一样的,直接c.json返回给用户就行了

以上是最终解决的方法。。。因为gin的c.json无论如何都会重新转换一次,从而导致精度丢失,所以转字符串是比较好的方法


再配上Json正则匹配数据的代码

 db.data = db.data.replace(/\id":(\d{16,20}),/g, 'id": "$1",');    
 db.data = JSON.parse(db.data);


当然也想到另外 一个解决方法就是直接设置好头部信息后,使用c.write.write()输出数据,也可以实现,原理就是自己写一个输出数据的方法,但这个方法对gin的底层方法了解度够高,并且不怕麻烦才能实现,所以就选择了上方的方法来解决精度 丢失的问题了


本文来自投稿,不代表本人立场,如若转载,请注明出处;如有问题您可以发邮件到:itlun@qq.com