beego 客户端bigint精度丢失问题探讨

74 阅读2分钟

第一种方法 改成字符串类型

在全局返回方法中将bigint转成string

package controller
​
import (
    "encoding/json"
    beego "github.com/beego/beego/v2/server/web"
    "github.com/gookit/validate"
    "regexp"
    "strconv"
    "strings"
)
​
var (
    SUCCESS_CODE         = 20000
    FAIL_CODE            = 40001
    SUCCESS_MSG          = "success"
    FAIL_MSG             = "fail"
    VALIDATOR_MAP        = map[string]string{"code": "701", "msg": "属性验证有误"}
    BINDING_PAMATERS_MAP = map[string]string{"code": "702", "msg": "参数绑定有误"}
)
​
/*父控制器 -- 继承beego.Controller*/
type BaseController struct {
    beego.Controller
}
​
/*统一返回*/
type Response struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data"`
}
​
func (web BaseController) Ok(data any) {
    data = web.TransferData(data)
    web.Data["json"] = &Response{Code: SUCCESS_CODE, Message: SUCCESS_MSG, Data: data}
    web.ServeJSON()
}
​
/**
 * @desc 对param参数进行转换
 */
func (web *BaseController) ParseParamUint64(key string, def ...uint64) (uint64, error) {
    strv := web.Ctx.Input.Param(":" + key)
    if len(strv) == 0 && len(def) > 0 {
        return def[0], nil
    }
    return strconv.ParseUint(strv, 10, 64)
}
​
/**
 * @desc  对param参数进行转换
 */
func (web *BaseController) ParseParamInt64(key string, def ...int64) (int64, error) {
    strv := web.Ctx.Input.Param(":" + key)
    if len(strv) == 0 && len(def) > 0 {
        return def[0], nil
    }
    return strconv.ParseInt(strv, 10, 64)
}
​
/**
 * @desc json参数的绑定
 */
func (web *BaseController) BindJSONToStruct(obj interface{}) error {
    return json.Unmarshal(web.Ctx.Input.RequestBody, obj)
}
​
/*
*
  - 获取用户IP地址
  - @desc
    由于前端使用了 nginx 做反向代理,所以通过 beego 框架内 提供的方法
    Input.IP()获取到的IP是内网IP。不满足需求
    nginx 代理中已经设置
    location / {
    proxy_set_header   X-real-ip $remote_addr;
    proxy_pass http://upstream/;
    }
*/
func (web *BaseController) GetIpAddr() string {
    header := web.Ctx.Request.Header
    ip := header.Get("x-forwarded-for")
​
    if ip == "" || len(ip) == 0 || "unknown" == ip {
        ip = header.Get("Proxy-Client-IP")
    }
​
    if ip == "" || len(ip) == 0 || "unknown" == ip {
        ip = header.Get("X-Forwarded-For")
    }
​
    if ip == "" || len(ip) == 0 || "unknown" == ip {
        ip = header.Get("WL-Proxy-Client-IP")
    }
​
    if ip == "" || len(ip) == 0 || "unknown" == ip {
        ip = header.Get("X-Real-IP")
    }
​
    if ip == "" || len(ip) == 0 || "unknown" == ip {
        ip = web.Ctx.Input.IP()
    }
​
    if "0:0:0:0:0:0:0:1" == ip || strings.Contains(ip, "::1") {
        return "127.0.0.1"
    } else {
        return ip
    }
}
​
/**
 * @desc 解决精度丢失的问题
 */
func (web BaseController) TransferData(data any) any {
    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
    }
    return data
}
​

原因web.ServeJSON()中并未对int64处理 所以需要使用TransferData 将int转成字符串

第二种方法 自定数据类型

1:自定义数据类型

package lib
​
import (
    "encoding/json"
    "fmt"
    "strconv"
    "strings"
)
​
type BigInt struct {
    Data string
}
​
/**
 * @desc 序列号方法
 */
func (t *BigInt) MarshalJSON() ([]byte, error) {
    stamp := fmt.Sprintf(""%s"", t.Data)
    return []byte(stamp), nil
}
​
/**
 * @desc 反序列化
 */
func (t *BigInt) UnmarshalJSON(data []byte) error {
    t.Data = strings.ReplaceAll(string(data), """, "")
    return nil
}
​
// String 重写String方法
func (t *BigInt) String() string {
    data, _ := json.Marshal(t)
    return string(data)
}
​
/**
 * @desc 获取具体的数值
 */
func (t *BigInt) Uint64() uint64 {
    value, _ := strconv.ParseUint(t.Data, 10, 64)
    return value
}
​

2: 使用

定义结构体

/**
 * @desc 用于文章列表查询分页
 */
type CommentContext struct {
    ID       lib.BigInt `validate:"required" json:"id"`       //评论ID
    Opid     lib.BigInt `validate:"required" json:"opid"`     //文章ID
    ParentId lib.BigInt `validate:"required" json:"parentId"` //回复的父ID
    Uuid     string     `json:"uuid"`                         //用户UUID
    UserId   uint64     `json:"userId"`                       //用户ID
}

在方法中使用

var ctx context.CommentContext
ctx.ParentId.Uint64()