wrapper/tool/define/diff.go
2024-11-21 15:11:50 +08:00

246 lines
7.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package define ...
//
// Description : define ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-03-08 11:04
package define
import (
"fmt"
"git.zhangdeman.cn/zhangdeman/util"
"git.zhangdeman.cn/zhangdeman/wrapper"
"reflect"
"strings"
)
// DiffOption 做数据对比时的选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:12 2024/3/8
type DiffOption struct {
StrictMode bool `json:"strict_mode"` // 采用严格模式: 1 != 1.0 , 采用非严格模式 1 == 1.0
AllowStringNumber bool `json:"allow_string_number"` // 是否允许字符串数字, 在非严格模式下, 若允许, 则 1 == "1" , 不允许, 则 1 != "1"
IgnoreNotFoundField bool `json:"ignore_not_found_field"` // 忽略不存在的字段
IgnoreEmptyString bool `json:"ignore_empty_string"` // 忽略空字符串, 若输入值为空字符串, 则不做比较, 认为两个值相同
IgnoreZeroNumber bool `json:"ignore_zero_number"` // 忽略置为0的数字, 若输入的数据为数字类型, 则不做比较, 认为两个值相同
IgnoreNil bool `json:"ignore_nil"` // 忽略 nil 值, 若输入值为NIL , 则不做比较, 认为两个值相同
CustomDiffFuncTable map[string]CustomDiffFunc `json:"-"` // 外部自定义的字段是否相同的比较函数, 会优先使用外部输入的比较函数
}
// NewDiffOption ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:06 2024/3/8
func NewDiffOption() *DiffOption {
return &DiffOption{
StrictMode: false,
IgnoreNotFoundField: false,
IgnoreEmptyString: false,
IgnoreZeroNumber: false,
IgnoreNil: false,
CustomDiffFuncTable: make(map[string]CustomDiffFunc),
}
}
// CustomDiffFunc 自定义字段对比方法
//
// 输入分别如下:
//
// field : 要对比的字段
//
// inputVal : 输入的原始数据
//
// storageVal : 当前存储的数据
//
// option : 对比时的额外选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:06 2024/3/8
type CustomDiffFunc func(field string, inputVal wrapper.Map, storageVal wrapper.Map, option *DiffOption) *DiffResult
// DiffResult 对比结果
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:10 2024/3/8
type DiffResult struct {
Field string `json:"field"` // 字段名
OldVal any `json:"old_val"` // 当前field在storageVal中的值
NewVal any `json:"new_val"` // 当前field在inputVal中的值
IsSame bool `json:"is_same"` // 两个值是否相同
DiffReason string `json:"diff_reason"` // 两个值不同的原因
Err error `json:"err"` // 对比过程中是否出现异常
}
const (
DiffReasonTypeNotMatch = "TYPE_NOT_MATCH" // 类型不匹配
DiffReasonValueNotMatch = "VALUE_NOT_MATCH" // 数据值不匹配
DiffReasonInputFieldNotFound = "INPUT_FIELD_NOT_FOUND" // 输入数据中不存在相关字段
DiffReasonStorageFieldNotFound = "STORAGE_FIELD_NOT_FOUND" // 存储数据中不存在相关字段
)
var (
// 当前仅支持基础类型的比较不支持slice/map/struct等复杂类型的比较
supportValueTypeTable = map[reflect.Kind]any{
reflect.Bool: true,
reflect.Int: true,
reflect.Int8: true,
reflect.Int16: true,
reflect.Int32: true,
reflect.Int64: true,
reflect.Uint: true,
reflect.Uint8: true,
reflect.Uint16: true,
reflect.Uint32: true,
reflect.Uint64: true,
reflect.Float32: true,
reflect.Float64: true,
reflect.String: true,
// reflect.Ptr: true,
}
)
// IsSupportValueType ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:23 2024/3/8
func IsSupportValueType(kind reflect.Kind) bool {
if _, exist := supportValueTypeTable[kind]; exist {
return true
}
return false
}
// DefaultDiffFunc 默认的diff函数
//
// Author : zhangdeman001@ke.com<张德满>
//
// Date : 12:05 2024/3/8
func DefaultDiffFunc(field string, inputVal wrapper.Map, storageVal wrapper.Map, option *DiffOption) *DiffResult {
if nil == option {
option = NewDiffOption()
}
result := &DiffResult{
Field: field,
OldVal: nil,
NewVal: nil,
IsSame: true,
DiffReason: "",
Err: nil,
}
var (
inputFieldVal any
inputFieldValExist error
storageFieldVal any
storageFieldValExist error
)
inputFieldVal, inputFieldValExist = inputVal.Get(field)
storageFieldVal, storageFieldValExist = storageVal.Get(field)
// 字段在输入数据和存储数据中均不存在
if nil != inputFieldValExist && nil != storageFieldValExist {
// 输入和存储都没这个字段
return result
}
// 判断输入字段是否存在
if nil != inputFieldValExist {
if option.IgnoreNotFoundField {
// 忽略不存在的字段
return result
}
// 由于前置逻辑保证了, 输入不存在相关字段, 则现存数据一定存在相关字段
result.IsSame = false
result.DiffReason = DiffReasonInputFieldNotFound
result.OldVal = storageFieldVal
return result
}
// 判断存储字段是否存在
if nil != storageFieldValExist {
result.IsSame = false
result.DiffReason = DiffReasonStorageFieldNotFound
result.NewVal = inputFieldVal
return result
}
// 校验类型
inputFieldValType := reflect.TypeOf(inputFieldVal)
storageFieldValType := reflect.TypeOf(storageFieldVal)
if inputFieldValType.Kind() == reflect.Ptr {
inputReflect := reflect.ValueOf(inputFieldVal)
inputFieldValType = inputReflect.Type()
inputFieldVal = inputReflect.Interface()
}
if storageFieldValType.Kind() == reflect.Ptr {
storageReflect := reflect.ValueOf(storageFieldValType)
storageFieldValType = storageReflect.Type()
storageFieldVal = storageReflect.Interface()
}
result.NewVal = inputFieldVal
result.OldVal = storageFieldVal
if inputFieldValType.Kind() != storageFieldValType.Kind() && option.StrictMode {
// 严格模式下, 类型不相同
result.IsSame = false
result.DiffReason = DiffReasonTypeNotMatch
return result
}
// 类型相同, 或者非严格模式下不校验类型
if option.StrictMode {
// 严格模式
if inputFieldVal != storageFieldVal {
result.IsSame = false
result.DiffReason = DiffReasonValueNotMatch
return result
}
// return result
}
// 非严格模式
// 存储值尝试转 float64
inputValStr := fmt.Sprintf("%v", inputFieldVal)
storageValStr := fmt.Sprintf("%v", storageFieldVal)
if inputValStr == storageValStr {
return result
}
if option.AllowStringNumber {
// 允许字符串数字
var (
storageFloat64 float64
inputFloat64 float64
)
if err := util.ConvertAssign(&storageFloat64, storageValStr); nil == err {
if err := util.ConvertAssign(&inputFloat64, inputValStr); nil == err {
if storageFloat64 == inputFloat64 {
return result
}
}
}
}
// 浮点型数字. 去小数部分最右侧的0
if inputFieldValType.Kind() == reflect.Float64 || inputFieldValType.Kind() == reflect.Float32 {
inputValStrArr := strings.Split(inputValStr, ".")
if len(inputValStrArr) == 2 {
inputValStrArr[1] = strings.TrimRight(inputValStrArr[1], "0")
inputValStr = strings.Join(inputValStrArr, ".")
}
}
if storageFieldValType.Kind() == reflect.Float64 || storageFieldValType.Kind() == reflect.Float32 {
storageValStrArr := strings.Split(storageValStr, ".")
if len(storageValStrArr) == 2 {
storageValStrArr[1] = strings.TrimRight(storageValStrArr[1], "0")
storageValStr = strings.Join(storageValStrArr, ".")
}
}
if inputValStr != storageValStr {
result.IsSame = false
result.DiffReason = DiffReasonValueNotMatch
return result
}
return result
}