diff --git a/common/serialization/serialization.go b/common/serialization/serialization.go index 06e4875..c3def37 100644 --- a/common/serialization/serialization.go +++ b/common/serialization/serialization.go @@ -2,14 +2,36 @@ package serialization import ( "github.com/fox/fox/log" - "github.com/mitchellh/mapstructure" + "github.com/fox/fox/mapstruct" "reflect" "time" ) +const ( + timeFormat = "2006-01-02 15:04:05" +) + +func time2stringHook(v reflect.Value, _ reflect.StructField) (any, error) { + if v.Type() == reflect.TypeOf(time.Time{}) { + if t, ok := v.Interface().(time.Time); ok { + return t.Format(timeFormat), nil + } + } + return v, nil +} + +type resultT[T any] struct { + ret T +} + +func string2timeHook(s string, _ reflect.StructField) (any, error) { + return time.Parse(timeFormat, s) +} + func StructToMap(_struct interface{}) map[string]interface{} { - var result map[string]interface{} - err := mapstructure.Decode(_struct, &result) + result, err := mapstruct.ToMap(_struct, + mapstruct.WithTypeHook(reflect.TypeOf(time.Time{}), time2stringHook), + ) if err != nil { log.ErrorF("struct:%v to map error:%v", _struct, err) return make(map[string]interface{}) @@ -17,133 +39,10 @@ func StructToMap(_struct interface{}) map[string]interface{} { return result } -type resultT[T any] struct { - ret T -} - -func stringToTimeHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { - if t == reflect.TypeOf(time.Time{}) && f == reflect.TypeOf("") { - return time.Parse(time.RFC3339, data.(string)) - } - return data, nil -} - func MapToStruct[T any](maps map[string]string) (*T, error) { result := &resultT[T]{} - config := &mapstructure.DecoderConfig{ - WeaklyTypedInput: true, // 允许弱类型转换(如 "1" → 1) - DecodeHook: stringToTimeHook, // 自定义钩子 - Result: &result.ret, - } - - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - log.ErrorF("map:%v to struct error:%v", maps, err) - return nil, err - } - - err = decoder.Decode(maps) - if err != nil { - log.ErrorF("map:%v to struct error:%v", maps, err) - return nil, err - } - return &result.ret, nil + err := mapstruct.ToStruct(maps, &result.ret, + mapstruct.WithStringTypeHook(reflect.TypeOf(time.Time{}), string2timeHook), + ) + return &result.ret, err } - -// func StructToMap(obj interface{}) map[string]interface{} { -// out := make(map[string]interface{}) -// v := reflect.ValueOf(obj) -// if v.Kind() == reflect.Ptr { -// v = v.Elem() -// } -// t := v.Type() -// for i := 0; i < v.NumField(); i++ { -// field := t.Field(i) -// key := field.Tag.Get("json") // 使用 json tag 作为字段名 -// if key == "" { -// key = field.Name -// } -// out[key] = v.Field(i).Interface() -// } -// return out -// } - -// func keyValueToField(key, value string, fieldValue *reflect.Value) error { -// switch fieldValue.Kind() { -// case reflect.Int8, reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64: -// if v, err := strconv.ParseInt(value, 10, 0); err == nil { -// fieldValue.SetInt(v) -// } else { -// return err -// } -// case reflect.Uint8, reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: -// if v, err := strconv.ParseUint(value, 10, 0); err == nil { -// fieldValue.SetUint(v) -// } else { -// return err -// } -// case reflect.Float32, reflect.Float64: -// if v, err := strconv.ParseFloat(value, 0); err == nil { -// fieldValue.SetFloat(v) -// } else { -// return err -// } -// case reflect.String: -// fieldValue.SetString(value) -// case reflect.Bool: -// if v, err := strconv.ParseBool(value); err == nil { -// fieldValue.SetBool(v) -// } else { -// return err -// } -// default: -// nv := reflect.New(fieldValue.Type()) -// if err := json.Unmarshal([]byte(value), nv.Interface()); err == nil { -// if fieldValue.Kind() == nv.Elem().Kind() { -// fieldValue.Set(nv.Elem()) -// } else { -// return fmt.Errorf("field:%v should have same type.original type:%v, reflect type:%v", key, fieldValue.Kind().String(), nv.Elem().Kind().String()) -// } -// } else { -// return err -// } -// } -// return nil -// } -// -// func deserializeMapString(kvs map[string]string, structObj interface{}) error { -// val := reflect.ValueOf(structObj).Elem() -// typ := val.Type() -// -// for i := 0; i < typ.NumField(); i++ { -// field := typ.Field(i) -// // 检查字段是否可设置(即导出) -// if !field.IsExported() { -// continue -// } -// fieldName := field.Tag.Get("json") -// if fieldName == "-" { -// continue -// } -// if value, ok := kvs[fieldName]; ok { -// // 获取字段的反射值 -// fieldValue := val.FieldByName(field.Name) -// if err := keyValueToField(field.Tag.Get("json"), value, &fieldValue); err != nil { -// return err -// } -// } else { -// return fmt.Errorf("redis:%v not has field:%v ", typ.Name(), field.Tag.Get("json")) -// } -// } -// return nil -// } -// -// type resultT[T any] struct { -// ret T -// } -// -// func MapStringToStruct[T any](maps map[string]string) (*T, error) { -// result := &resultT[T]{} -// err := deserializeMapString(maps, &result.ret) -// return &result.ret, err -// } diff --git a/common/serialization/serialization_test.go b/common/serialization/serialization_test.go index 7d1a962..5e3a365 100644 --- a/common/serialization/serialization_test.go +++ b/common/serialization/serialization_test.go @@ -1,21 +1,35 @@ package serialization import ( - "encoding/json" + "fmt" "game/common/model/user" "testing" + "time" ) func Test_zz(t *testing.T) { - zz := `{\"device_id\":\"\",\"email\":\"\",\"id\":\"17\",\"last_login_ip\":\"114.132.124.145:49486\",\"last_login_time\":\"2025-06-04T02:01:49.72898394+08:00\",\"password\":\"$2a$10$p4GJtWzmhaUcT0i/SNpsWOE6uNq8JzymuB6Lv2Qf0Bpg32YthnWgK\",\"phone\":\"\",\"register_ip\":\"114.132.124.145:49486\",\"register_time\":\"0001-01-01T00:00:00Z\",\"status\":\"1\",\"username\":\"test0001\"}` - us := &user.UserAccount{} - _ = json.Unmarshal([]byte(zz), us) + us := &user.UserAccount{ + ID: 17, + Username: "test001", + Password: "123456", + Email: "", + Phone: "", + DeviceID: "", + LastLoginIP: "127.0.0.1", + LastLoginTime: time.Now(), + Status: 0, + RegisterIP: "", + RegisterTime: time.Now(), + } + maps := StructToMap(us) + t.Log(maps) + mapString := make(map[string]string) + for key, value := range maps { + mapString[key] = fmt.Sprintf("%v", value) + } var err error - us, err = MapToStruct[user.UserAccount](map[string]string{ - "id": "17", - "last_login_time": "2025-06-04T02:01:49.72898394+08:00", - }) + us, err = MapToStruct[user.UserAccount](mapString) t.Log(err) t.Log(us) }