Hello 第一次写文章,大家将就看。
本次分享的是一个结构体处理的小技巧。
需求背景
产品需求要修改用户信息,还得取到修改前内容和修改后内容。
实现
type User struct {
UserId string `json:"userId"`
UserName string `json:"userName"`
UserClass string `json:"userClass"`
}
笨方法
刚开始没仔细想,就几个字段随手就写了以下的实现。
func DiffMap(old, new *User) (before, after map[string]interface{}) {
before, after = make(map[string]interface{}), make(map[string]interface{})
if old.UserName != new.UserName {
before["userName"] = old.UserName
after["userName"] = new.UserName
}
if old.UserId != new.UserId {
before["userId"] = old.UserId
after["userId"] = new.UserId
}
if old.UserClass != new.UserClass {
before["userClass"] = old.UserId
after["userClass"] = new.UserId
}
return
}
功能实现了,然后结构体越来越大,字段越来越多,if 越来越多,这代码看着就很扯了。
随后还有很多信息修改,都要这样的结构。。。然后就考虑优化了。
优化
这种结构体操作肯定就开始考虑反射了,然后就有以下实现:
func DiffMap(old, new interface{}, excludes ...string) (before, after map[string]interface{}) {
before, after = make(map[string]interface{}), make(map[string]interface{})
valOld := reflect.ValueOf(old).Elem()
valNew := reflect.ValueOf(new).Elem()
for i := 0; i < valOld.NumField(); i++ {
key := valOld.Type().Field(i).Name
if valOld.Field(i).Type() != valNew.FieldByName(key).Type() {
continue
}
if valOld.Field(i).Type().Name() == "Decimal" {
vo, oOk := valOld.Field(i).Interface().(decimal.Decimal)
vn, nOk := valNew.FieldByName(key).Interface().(decimal.Decimal)
if oOk && nOk && vo.Equal(vn) {
continue
}
} else {
if valOld.Field(i).Interface() == valNew.FieldByName(key).Interface() {
continue
}
if valOld.Field(i).IsZero() && valNew.FieldByName(key).IsZero() {
continue
}
}
isExclude := false
for _, col := range excludes {
if key == col {
isExclude = true
break
}
}
if isExclude {
continue
}
before[key] = valOld.Field(i)
after[key] = valNew.FieldByName(key)
}
return before, after
}
优化之后有再多的这种需求都不怕了,直接就可以取到结果了,代码看着舒服多了。
总结
代码重复的东西,能封装就尽量封装,不要怕问题多,方法总比问题多的。