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