package converter import ( "database/sql" "reflect" "time" ) // StructToStruct 通用结构体转换函数,利用反射将源结构体的字段值复制到目标结构体 // src: 源结构体(通常是 model) // dst: 目标结构体(通常是 pb),必须是指针 // 支持的自动转换: // - time.Time -> int64 (Unix 时间戳) // - sql.NullTime -> int64 (如果有效) // - sql.NullInt64 -> int64 // - sql.NullString -> string // - 相同名称和兼容类型的字段 func StructToStruct(src, dst interface{}) error { if src == nil { return nil } srcVal := reflect.ValueOf(src) dstVal := reflect.ValueOf(dst) // 确保 dst 是指针 if dstVal.Kind() != reflect.Ptr { return newError("destination must be a pointer") } dstVal = dstVal.Elem() // 如果 src 是指针,解引用 if srcVal.Kind() == reflect.Ptr { srcVal = srcVal.Elem() } // 都必须是结构体 if srcVal.Kind() != reflect.Struct || dstVal.Kind() != reflect.Struct { return newError("both source and destination must be structs") } srcType := srcVal.Type() // 遍历源结构体的所有字段 for i := 0; i < srcVal.NumField(); i++ { srcField := srcVal.Field(i) srcFieldName := srcType.Field(i).Name // 在目标结构体中查找同名字段 dstField := dstVal.FieldByName(srcFieldName) if !dstField.IsValid() || !dstField.CanSet() { continue } // 进行类型转换和赋值 if err := assignValue(srcField, dstField); err != nil { continue // 如果单个字段转换失败,继续处理其他字段 } } return nil } // assignValue 尝试将源字段值赋给目标字段 func assignValue(srcField, dstField reflect.Value) error { // 如果是可直接赋值的类型 if srcField.Type() == dstField.Type() { dstField.Set(srcField) return nil } srcType := srcField.Type() dstType := dstField.Type() // 处理 time.Time -> int64 的转换 if srcType == reflect.TypeOf(time.Time{}) && dstType.Kind() == reflect.Int64 { t := srcField.Interface().(time.Time) dstField.SetInt(t.Unix()) return nil } // 处理 sql.NullTime -> int64 的转换 if srcType == reflect.TypeOf(sql.NullTime{}) && dstType.Kind() == reflect.Int64 { nt := srcField.Interface().(sql.NullTime) if nt.Valid { dstField.SetInt(nt.Time.Unix()) } return nil } // 处理 sql.NullTime -> time.Time 的转换 if srcType == reflect.TypeOf(sql.NullTime{}) && dstType == reflect.TypeOf(time.Time{}) { nt := srcField.Interface().(sql.NullTime) if nt.Valid { dstField.Set(reflect.ValueOf(nt.Time)) } return nil } // 处理 sql.NullInt64 -> int64 的转换 if srcType == reflect.TypeOf(sql.NullInt64{}) && dstType.Kind() == reflect.Int64 { ni := srcField.Interface().(sql.NullInt64) if ni.Valid { dstField.SetInt(ni.Int64) } return nil } // 处理 sql.NullString -> string 的转换 if srcType == reflect.TypeOf(sql.NullString{}) && dstType.Kind() == reflect.String { ns := srcField.Interface().(sql.NullString) if ns.Valid { dstField.SetString(ns.String) } return nil } // 处理 sql.NullBool -> bool 的转换 if srcType == reflect.TypeOf(sql.NullBool{}) && dstType.Kind() == reflect.Bool { nb := srcField.Interface().(sql.NullBool) if nb.Valid { dstField.SetBool(nb.Bool) } return nil } // 处理 int -> int64 的转换 if srcType.Kind() == reflect.Int && dstType.Kind() == reflect.Int64 { dstField.SetInt(int64(srcField.Int())) return nil } // 处理 int64 -> int 的转换 if srcType.Kind() == reflect.Int64 && dstType.Kind() == reflect.Int { dstField.SetInt(srcField.Int()) return nil } // 处理 string -> string(某些情况下可能存在复制) if srcType.Kind() == reflect.String && dstType.Kind() == reflect.String { dstField.SetString(srcField.String()) return nil } // 处理 bool -> bool if srcType.Kind() == reflect.Bool && dstType.Kind() == reflect.Bool { dstField.SetBool(srcField.Bool()) return nil } return newError("unsupported type conversion from " + srcType.String() + " to " + dstType.String()) } // SliceToSlice 通用切片转换函数,使用 StructToStruct 转换每个元素 func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error) { srcVal := reflect.ValueOf(src) // src 必须是切片 if srcVal.Kind() != reflect.Slice { return nil, newError("source must be a slice") } // 获取原始 dst slice type dstSliceVal := reflect.ValueOf(dstSliceType) if dstSliceVal.Kind() != reflect.Slice { return nil, newError("dstSliceType must be a slice type") } dstSliceElemType := dstSliceVal.Type().Elem() // 创建新的目标切片 dstSlice := reflect.MakeSlice(dstSliceVal.Type(), srcVal.Len(), srcVal.Len()) // 逐个转换元素 for i := 0; i < srcVal.Len(); i++ { srcElem := srcVal.Index(i) dstElem := reflect.New(dstSliceElemType) // 如果 src 元素是指针,需要解引用 if srcElem.Kind() == reflect.Ptr { srcElem = srcElem.Elem() } // 转换单个元素 dstElemIface := dstElem.Interface() if err := StructToStruct(srcElem.Interface(), dstElemIface); err != nil { return nil, err } dstSlice.Index(i).Set(dstElem.Elem()) } return dstSlice.Interface(), nil } type Error struct { msg string } func (e *Error) Error() string { return e.msg } func newError(msg string) error { return &Error{msg: msg} }