diff --git a/go.mod b/go.mod index 6ffce81..ab84572 100644 --- a/go.mod +++ b/go.mod @@ -8,16 +8,18 @@ require ( git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240130062251-a87a97b0e8d4 git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240110090803-399e964daa0c github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 + github.com/stretchr/testify v1.8.4 ) require ( git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240104123641-b3f23974e5d6 // indirect git.zhangdeman.cn/zhangdeman/util v0.0.0-20231227095334-7eb5cdbf9253 // indirect github.com/BurntSushi/toml v1.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mozillazg/go-pinyin v0.20.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/stretchr/testify v1.8.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 74f2e60..8f4228b 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,7 @@ -git.zhangdeman.cn/zhangdeman/consts v0.0.0-20230815040024-2b12dd51d19b h1:C7KftnLh7dOqzNRs5dn/9yqMDvuqMn5RCglvV6bY758= -git.zhangdeman.cn/zhangdeman/consts v0.0.0-20230815040024-2b12dd51d19b/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240104123641-b3f23974e5d6 h1:ytpXTP3oxp480BAZQoOzqlBP4XP73NcpMplZ1/fA1lQ= git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240104123641-b3f23974e5d6/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= -git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20231224145141-489e31b07a71 h1:nvVSH+Ju8EmoPiPHTae5lxHo4kDjROYChs19Yayz+NY= -git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20231224145141-489e31b07a71/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U= -git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20231229032122-804bc9822704 h1:y/hXa0Ez+u0ka+QLv1abiCRy+rlJlnNdaH4H/eI6ZJ0= -git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20231229032122-804bc9822704/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U= git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240130062251-a87a97b0e8d4 h1:93JYY8JLbFcrlq37q/uKyxs2r2e3modsjvfSbnZQ/UI= git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240130062251-a87a97b0e8d4/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U= -git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20231224125439-01f39b6ea08d h1:TV0BCQQewBEtLsv3i9gXkxLFd5A5bWBTiNd3D/I5o4Q= -git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20231224125439-01f39b6ea08d/go.mod h1:w7kG4zyTJ1uPFaTWhze+OQuaUBINT2XnDxpyiM6ctc0= git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240110090803-399e964daa0c h1:k7VCn9GfRGTilvdF/TcTFVMDBfKLe3VeGAtMTiDSnS0= git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240110090803-399e964daa0c/go.mod h1:w7kG4zyTJ1uPFaTWhze+OQuaUBINT2XnDxpyiM6ctc0= git.zhangdeman.cn/zhangdeman/util v0.0.0-20231227095334-7eb5cdbf9253 h1:GO3oZa5a2sqwAzGcLDJtQzmshSWRmoP7IDS8bwFqvC4= diff --git a/tool/define/diff.go b/tool/define/diff.go index 6270cc4..5b9d50a 100644 --- a/tool/define/diff.go +++ b/tool/define/diff.go @@ -8,8 +8,11 @@ package define import ( + "fmt" + "git.zhangdeman.cn/zhangdeman/util" "git.zhangdeman.cn/zhangdeman/wrapper" "reflect" + "strings" ) // DiffOption 做数据对比时的选项 @@ -18,11 +21,28 @@ import ( // // Date : 11:12 2024/3/8 type DiffOption struct { - StrictMode bool `json:"strict_mode"` // 采用严格模式: 1 != "1" , 采用非严格模式 1 == "1" - 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:"-"` // 外部自定义的字段是否相同的比较函数, 会优先使用外部输入的比较函数 + StrictMode bool `json:"strict_mode"` // 采用严格模式: 1 != "1", 1 != 1.0 , 采用非严格模式 1 == "1", 1 == 1.0 + 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 自定义字段对比方法 @@ -56,8 +76,10 @@ type DiffResult struct { } const ( - DiffReasonTypeNotMatch = "TYPE_NOT_MATCH" // 类型不匹配 - DiffReasonValueNotMatch = "VALUE_NOT_MATCH" // 数据值不匹配 + DiffReasonTypeNotMatch = "TYPE_NOT_MATCH" // 类型不匹配 + DiffReasonValueNotMatch = "VALUE_NOT_MATCH" // 数据值不匹配 + DiffReasonInputFieldNotFound = "INPUT_FIELD_NOT_FOUND" // 输入数据中不存在相关字段 + DiffReasonStorageFieldNotFound = "STORAGE_FIELD_NOT_FOUND" // 存储数据中不存在相关字段 ) var ( @@ -76,6 +98,7 @@ var ( reflect.Uint64: true, reflect.Float32: true, reflect.Float64: true, + reflect.String: true, // reflect.Ptr: true, } ) @@ -91,3 +114,125 @@ func IsSupportValueType(kind reflect.Kind) bool { } 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{ + OldVal: nil, + NewVal: nil, + IsSame: true, + DiffReason: "", + Err: nil, + } + var ( + inputFieldVal interface{} + inputFieldValExist bool + storageFieldVal interface{} + storageFieldValExist bool + ) + + inputFieldVal, inputFieldValExist = inputVal.Get(field) + storageFieldVal, storageFieldValExist = storageVal.Get(field) + // 字段在输入数据和存储数据中均不存在 + if !inputFieldValExist && !storageFieldValExist { + // 输入和存储都没这个字段 + return result + } + + // 判断输入字段是否存在 + if !inputFieldValExist { + if option.IgnoreNotFoundField { + // 忽略不存在的字段 + return result + } + // 由于前置逻辑保证了, 输入不存在相关字段, 则现存数据一定存在相关字段 + result.IsSame = false + result.DiffReason = DiffReasonInputFieldNotFound + result.OldVal = storageFieldVal + return result + } + // 判断存储字段是否存在 + if !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) + 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 + } + } + } + if inputValStr == storageValStr { + 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 +} diff --git a/tool/define/diff_test.go b/tool/define/diff_test.go new file mode 100644 index 0000000..1f36560 --- /dev/null +++ b/tool/define/diff_test.go @@ -0,0 +1,48 @@ +// Package define ... +// +// Description : define ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-03-08 15:24 +package define + +import ( + "git.zhangdeman.cn/zhangdeman/wrapper" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestDefaultDiffFunc(t *testing.T) { + var ( + num1 float64 = 1.0 + num2 float32 = 1.00 + num3 string = "1" + num4 string = "1.00" + ) + input := wrapper.EasyMap(map[string]interface{}{ + "num": num1, + "num3": num3, + "num4": num4, + "num5": num1, + }) + storage := wrapper.EasyMap(map[string]interface{}{ + "num": num2, + "num3": num2, + "num4": num2, + "num5": num1, + }) + diffOption := NewDiffOption() + diffOption.StrictMode = true + res := DefaultDiffFunc("num", input, storage, diffOption) + assert.EqualValues(t, false, res.IsSame, "严格模式下, float32与float64不相等") + res = DefaultDiffFunc("num5", input, storage, diffOption) + assert.EqualValues(t, true, res.IsSame, "严格模式下, float32与float32相等") + diffOption.StrictMode = false + res = DefaultDiffFunc("num", input, storage, diffOption) + assert.EqualValues(t, true, res.IsSame, "非严格模式下, float32与float64相等") + res = DefaultDiffFunc("num3", input, storage, diffOption) + assert.EqualValues(t, true, res.IsSame, "非严格模式下, float32与string相等") + res = DefaultDiffFunc("num4", input, storage, diffOption) + assert.EqualValues(t, true, res.IsSame, "非严格模式下, float32与string相等") +}