Files
juwan-backend/common/converter/generic.go
T
2026-02-27 19:17:01 +08:00

208 lines
5.2 KiB
Go
Raw 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 converter
import (
"database/sql"
"reflect"
"time"
)
// StructToStruct 通用结构体转换函数,利用反射将源结构体的字段值复制到目标结构体
// src: 源结构体(通常是 models
// 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}
}