数据质量检测在Go中指针对具体业务数据快速验证完整性、类型一致性及业务规则合规性,核心是可插拔校验,需结合validator/v10(结构体字段级)、自定义函数(跨字段逻辑)和gjson/jsoniter(动态JSON场景)。

什么是数据质量检测在 Go 中的实际含义
数据质量检测不是指用 Go 写个大而全的 ETL 工具,而是针对具体业务数据(比如 CSV 行、HTTP 请求体、数据库查出的 []map[string]interface{} 或结构体切片),快速验证其完整性、类型一致性、业务规则合规性。核心是“可插拔校验”——一个字段能串多个检查器,失败时能明确指出哪条记录、哪个字段、违反了哪条规则。
用 validator 包做基础字段级校验(推荐 go-playground/validator/v10)
它支持 struct tag 声明规则,比手写 if err != nil 更易维护,且错误信息结构化。但要注意:它只校验单个 struct 实例,不处理跨字段逻辑(如“开始时间不能晚于结束时间”需自定义函数)。
常见错误现象:validate:"required" 对零值 int/bool 不生效(因为 0 和 false 是合法零值);嵌套 struct 忘加 validate:"dive" 导致子字段不校验。
- 安装:
go get github.com/go-playground/validator/v10 - 基础用法:给 struct 字段加 tag,例如
Name string `validate:"required,min=2,max=50"` - 校验后必须检查
err是否为validator.ValidationErrors类型,再用err.Translate(trans)获取中文提示(否则默认英文) - 跨字段校验要注册自定义函数,比如
v.RegisterValidation("ltefield", lteFieldValidator),函数里通过fl.Field().Interface()和fl.Parent().FieldByName("EndAt").Interface()拿值对比
用 gjson 或 jsoniter 校验动态 JSON 数据(无预定义 struct 场景)
当数据来源是第三方 API、日志行或用户上传的 JSON 文件,字段名和结构不确定时,validator 失效。此时应先用 gjson.Get(jsonBytes, "user.name") 抽取路径,再对抽取结果做类型/范围判断。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
性能影响:频繁调用 gjson.Get 比解析成 map 快,但若需多次访问同一 JSON,建议用 jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal 解析一次,再用类型断言或反射遍历。
-
gjson适合单次路径提取:检查gjson.GetBytes(data, "items.#.price").Exists()确保数组存在,再用.Array()遍历每个 price 值是否为 number - 避免写
gjson.GetBytes(data, "user.age").Int() —— 若字段不存在,<code>.Int()返回 0,会误判;应先用.Exists()再取值 - 对嵌套过深或路径含变量的场景(如
"data.users.0.name"),用jsoniter的GetIndex()或递归遍历更可控
构建轻量级规则引擎(避免引入 expr 或 rego 这类重型依赖)
真正卡住进度的往往不是单字段校验,而是“订单金额 ≥ 优惠券面额 × 2”这类带计算的业务规则。硬编码 if 判断难复用,全扔配置文件又难调试。折中做法:用 map 定义规则函数,key 是规则 ID,value 是接收 map[string]interface{} 并返回 error 的闭包。
容易踩的坑:规则函数里直接 panic 而非返回 error;未对输入 map 做 key 存在性检查就强制取值导致 panic;浮点数比较用 == 而非 math.Abs(a-b) 。
- 示例规则:
rules["order_amount_ge_coupon_x2"] = func(data map[string]interface{}) error { ... } - 执行时统一包装 recover:用
defer func() { if r := recover(); r != nil { err = fmt.Errorf("rule panic: %v", r) } }() - 规则函数内优先用
data["coupon_amount"]断言为float64,失败则返回明确 error,不要静默跳过 - 所有规则执行完,聚合 error 切片,按
record_id + rule_id去重,避免同一条记录因多个字段缺失报出重复错误
真正的难点不在语法,而在规则变更时如何不改代码——把规则函数的参数从 map 换成带 GetFloat64(key string) 方法的 interface{},后续可对接数据库或配置中心,但初期先写死函数更稳。

















