feat: refactor type mapper.

This commit is contained in:
Bobo
2025-06-16 14:49:21 +08:00
parent a1b8326783
commit de36ab695d
6 changed files with 102 additions and 94 deletions

View File

@@ -17,26 +17,26 @@ func main() {
Age int Age int
} }
type ModelType struct { type EntityType struct {
Name string Name string
Age int Age int
} }
mapper := mapper.NewCopierMapper[DtoType, ModelType]() mapper := mapper.NewCopierMapper[DtoType, EntityType]()
// 测试 ToModel 方法 // 测试 ToEntity 方法
dto := &DtoType{Name: "Alice", Age: 25} dto := &DtoType{Name: "Alice", Age: 25}
model := mapper.ToModel(dto) entity := mapper.ToEntity(dto)
// 测试 ToModel 方法,传入 nil // 测试 ToEntity 方法,传入 nil
modelNil := mapper.ToModel(nil) entityNil := mapper.ToEntity(nil)
// 测试 ToDto 方法 // 测试 ToDTO 方法
model = &ModelType{Name: "Bob", Age: 30} entity = &EntityType{Name: "Bob", Age: 30}
dtoResult := mapper.ToDto(model) dtoResult := mapper.ToDTO(entity)
// 测试 ToDto 方法,传入 nil // 测试 ToDTO 方法,传入 nil
dtoNil := mapper.ToDto(nil) dtoNil := mapper.ToDTO(nil)
} }
``` ```
@@ -50,7 +50,7 @@ import "github.com/tx7do/go-utils/mapper"
func main() { func main() {
type DtoType int32 type DtoType int32
type ModelType string type EntityType string
const ( const (
DtoTypeOne DtoType = 1 DtoTypeOne DtoType = 1
@@ -58,8 +58,8 @@ func main() {
) )
const ( const (
ModelTypeOne ModelType = "One" EntityTypeOne EntityType = "One"
ModelTypeTwo ModelType = "Two" EntityTypeTwo EntityType = "Two"
) )
nameMap := map[int32]string{ nameMap := map[int32]string{
@@ -71,25 +71,25 @@ func main() {
"Two": 2, "Two": 2,
} }
converter := mapper.NewEnumTypeConverter[DtoType, ModelType](nameMap, valueMap) converter := mapper.NewEnumTypeConverter[DtoType, EntityType](nameMap, valueMap)
// 测试 ToModel 方法 // 测试 ToEntity 方法
dto := DtoTypeOne dto := DtoTypeOne
model := converter.ToModel(&dto) entity := converter.ToEntity(&dto)
// 测试 ToModel 方法,传入不存在的值 // 测试 ToEntity 方法,传入不存在的值
dtoInvalid := DtoType(3) dtoInvalid := DtoType(3)
modelInvalid := converter.ToModel(&dtoInvalid) entityInvalid := converter.ToEntity(&dtoInvalid)
// 测试 ToDto 方法 // 测试 ToDTO 方法
tmpModelTwo := ModelTypeTwo tmpEntityTwo := EntityTypeTwo
model = &tmpModelTwo entity = &tmpEntityTwo
dtoResult := converter.ToDto(model) dtoResult := converter.ToDTO(entity)
// 测试 ToDto 方法,传入不存在的值 // 测试 ToDTO 方法,传入不存在的值
tmpModelThree := ModelType("Three") tmpEntityThree := EntityType("Three")
modelInvalid = &tmpModelThree entityInvalid = &tmpEntityThree
dtoInvalidResult := converter.ToDto(modelInvalid) dtoInvalidResult := converter.ToDTO(entityInvalid)
} }
``` ```

View File

@@ -4,19 +4,22 @@ import (
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
) )
type EnumTypeConverter[DTO ~int32, MODEL ~string] struct { type EnumTypeConverter[DTO ~int32, ENTITY ~string] struct {
nameMap map[int32]string nameMap map[int32]string
valueMap map[string]int32 valueMap map[string]int32
} }
func NewEnumTypeConverter[DTO ~int32, MODEL ~string](nameMap map[int32]string, valueMap map[string]int32) *EnumTypeConverter[DTO, MODEL] { func NewEnumTypeConverter[DTO ~int32, ENTITY ~string](
return &EnumTypeConverter[DTO, MODEL]{ nameMap map[int32]string,
valueMap map[string]int32,
) *EnumTypeConverter[DTO, ENTITY] {
return &EnumTypeConverter[DTO, ENTITY]{
valueMap: valueMap, valueMap: valueMap,
nameMap: nameMap, nameMap: nameMap,
} }
} }
func (m *EnumTypeConverter[DTO, MODEL]) ToModel(dto *DTO) *MODEL { func (m *EnumTypeConverter[DTO, ENTITY]) ToEntity(dto *DTO) *ENTITY {
if dto == nil { if dto == nil {
return nil return nil
} }
@@ -26,16 +29,16 @@ func (m *EnumTypeConverter[DTO, MODEL]) ToModel(dto *DTO) *MODEL {
return nil return nil
} }
model := MODEL(find) entity := ENTITY(find)
return &model return &entity
} }
func (m *EnumTypeConverter[DTO, MODEL]) ToDto(model *MODEL) *DTO { func (m *EnumTypeConverter[DTO, ENTITY]) ToDTO(entity *ENTITY) *DTO {
if model == nil { if entity == nil {
return nil return nil
} }
find, ok := m.valueMap[string(*model)] find, ok := m.valueMap[string(*entity)]
if !ok { if !ok {
return nil return nil
} }
@@ -44,17 +47,22 @@ func (m *EnumTypeConverter[DTO, MODEL]) ToDto(model *MODEL) *DTO {
return &dto return &dto
} }
func (m *EnumTypeConverter[DTO, MODEL]) NewConverterPair() []copier.TypeConverter { func (m *EnumTypeConverter[DTO, ENTITY]) NewConverterPair() []copier.TypeConverter {
srcType := MODEL("") srcType := ENTITY("")
dstType := DTO(0) dstType := DTO(0)
fromFn := m.ToDto fromFn := m.ToDTO
toFn := m.ToModel toFn := m.ToEntity
return NewGenericTypeConverterPair(&srcType, &dstType, fromFn, toFn) return NewGenericTypeConverterPair(&srcType, &dstType, fromFn, toFn)
} }
func NewGenericTypeConverterPair[A interface{}, B interface{}](srcType A, dstType B, fromFn func(src A) B, toFn func(src B) A) []copier.TypeConverter { func NewGenericTypeConverterPair[A interface{}, B interface{}](
srcType A,
dstType B,
fromFn func(src A) B,
toFn func(src B) A,
) []copier.TypeConverter {
return []copier.TypeConverter{ return []copier.TypeConverter{
{ {
SrcType: srcType, SrcType: srcType,

View File

@@ -1,10 +1,10 @@
package mapper package mapper
// Mapper defines the interface for converting between DTOs and models. // Mapper defines the interface for converting between Data Transfer Objects (DTOs) and Database Entities.
type Mapper[DTO any, MODEL any] interface { type Mapper[DTO any, ENTITY any] interface {
// ToModel converts a DTO to a MODEL. // ToEntity converts a DTO to a Database Entity.
ToModel(*DTO) *MODEL ToEntity(*DTO) *ENTITY
// ToDto converts a MODEL to a DTO. // ToDTO converts a Database Entity to a DTO.
ToDto(*MODEL) *DTO ToDTO(*ENTITY) *DTO
} }

View File

@@ -4,46 +4,46 @@ import (
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
) )
type CopierMapper[DTO any, MODEL any] struct { type CopierMapper[DTO any, ENTITY any] struct {
copierOption copier.Option copierOption copier.Option
} }
func NewCopierMapper[DTO any, MODEL any]() *CopierMapper[DTO, MODEL] { func NewCopierMapper[DTO any, ENTITY any]() *CopierMapper[DTO, ENTITY] {
return &CopierMapper[DTO, MODEL]{ return &CopierMapper[DTO, ENTITY]{
copierOption: copier.Option{ copierOption: copier.Option{
Converters: []copier.TypeConverter{}, Converters: []copier.TypeConverter{},
}, },
} }
} }
func (m *CopierMapper[DTO, MODEL]) AppendConverter(converter copier.TypeConverter) { func (m *CopierMapper[DTO, ENTITY]) AppendConverter(converter copier.TypeConverter) {
m.copierOption.Converters = append(m.copierOption.Converters, converter) m.copierOption.Converters = append(m.copierOption.Converters, converter)
} }
func (m *CopierMapper[DTO, MODEL]) AppendConverters(converters []copier.TypeConverter) { func (m *CopierMapper[DTO, ENTITY]) AppendConverters(converters []copier.TypeConverter) {
m.copierOption.Converters = append(m.copierOption.Converters, converters...) m.copierOption.Converters = append(m.copierOption.Converters, converters...)
} }
func (m *CopierMapper[DTO, MODEL]) ToModel(dto *DTO) *MODEL { func (m *CopierMapper[DTO, ENTITY]) ToEntity(dto *DTO) *ENTITY {
if dto == nil { if dto == nil {
return nil return nil
} }
var model MODEL var entity ENTITY
if err := copier.CopyWithOption(&model, dto, m.copierOption); err != nil { if err := copier.CopyWithOption(&entity, dto, m.copierOption); err != nil {
panic(err) // Handle error appropriately in production code panic(err) // Handle error appropriately in production code
} }
return &model return &entity
} }
func (m *CopierMapper[DTO, MODEL]) ToDto(model *MODEL) *DTO { func (m *CopierMapper[DTO, ENTITY]) ToDTO(entity *ENTITY) *DTO {
if model == nil { if entity == nil {
return nil return nil
} }
var dto DTO var dto DTO
if err := copier.CopyWithOption(&dto, model, m.copierOption); err != nil { if err := copier.CopyWithOption(&dto, entity, m.copierOption); err != nil {
panic(err) // Handle error appropriately in production code panic(err) // Handle error appropriately in production code
} }

View File

@@ -12,39 +12,39 @@ func TestCopierMapper(t *testing.T) {
Age int Age int
} }
type ModelType struct { type EntityType struct {
Name string Name string
Age int Age int
} }
mapper := NewCopierMapper[DtoType, ModelType]() mapper := NewCopierMapper[DtoType, EntityType]()
// 测试 ToModel 方法 // 测试 ToEntity 方法
dto := &DtoType{Name: "Alice", Age: 25} dto := &DtoType{Name: "Alice", Age: 25}
model := mapper.ToModel(dto) entity := mapper.ToEntity(dto)
assert.NotNil(t, model) assert.NotNil(t, entity)
assert.Equal(t, "Alice", model.Name) assert.Equal(t, "Alice", entity.Name)
assert.Equal(t, 25, model.Age) assert.Equal(t, 25, entity.Age)
// 测试 ToModel 方法,传入 nil // 测试 ToEntity 方法,传入 nil
modelNil := mapper.ToModel(nil) entityNil := mapper.ToEntity(nil)
assert.Nil(t, modelNil) assert.Nil(t, entityNil)
// 测试 ToDto 方法 // 测试 ToDTO 方法
model = &ModelType{Name: "Bob", Age: 30} entity = &EntityType{Name: "Bob", Age: 30}
dtoResult := mapper.ToDto(model) dtoResult := mapper.ToDTO(entity)
assert.NotNil(t, dtoResult) assert.NotNil(t, dtoResult)
assert.Equal(t, "Bob", dtoResult.Name) assert.Equal(t, "Bob", dtoResult.Name)
assert.Equal(t, 30, dtoResult.Age) assert.Equal(t, 30, dtoResult.Age)
// 测试 ToDto 方法,传入 nil // 测试 ToDTO 方法,传入 nil
dtoNil := mapper.ToDto(nil) dtoNil := mapper.ToDTO(nil)
assert.Nil(t, dtoNil) assert.Nil(t, dtoNil)
} }
func TestEnumTypeConverter(t *testing.T) { func TestEnumTypeConverter(t *testing.T) {
type DtoType int32 type DtoType int32
type ModelType string type EntityType string
const ( const (
DtoTypeOne DtoType = 1 DtoTypeOne DtoType = 1
@@ -52,8 +52,8 @@ func TestEnumTypeConverter(t *testing.T) {
) )
const ( const (
ModelTypeOne ModelType = "One" EntityTypeOne EntityType = "One"
ModelTypeTwo ModelType = "Two" EntityTypeTwo EntityType = "Two"
) )
nameMap := map[int32]string{ nameMap := map[int32]string{
@@ -65,29 +65,29 @@ func TestEnumTypeConverter(t *testing.T) {
"Two": 2, "Two": 2,
} }
converter := NewEnumTypeConverter[DtoType, ModelType](nameMap, valueMap) converter := NewEnumTypeConverter[DtoType, EntityType](nameMap, valueMap)
// 测试 ToModel 方法 // 测试 ToEntity 方法
dto := DtoTypeOne dto := DtoTypeOne
model := converter.ToModel(&dto) entity := converter.ToEntity(&dto)
assert.NotNil(t, model) assert.NotNil(t, entity)
assert.Equal(t, "One", string(*model)) assert.Equal(t, "One", string(*entity))
// 测试 ToModel 方法,传入不存在的值 // 测试 ToEntity 方法,传入不存在的值
dtoInvalid := DtoType(3) dtoInvalid := DtoType(3)
modelInvalid := converter.ToModel(&dtoInvalid) entityInvalid := converter.ToEntity(&dtoInvalid)
assert.Nil(t, modelInvalid) assert.Nil(t, entityInvalid)
// 测试 ToDto 方法 // 测试 ToDTO 方法
tmpModelTwo := ModelTypeTwo tmpEntityTwo := EntityTypeTwo
model = &tmpModelTwo entity = &tmpEntityTwo
dtoResult := converter.ToDto(model) dtoResult := converter.ToDTO(entity)
assert.NotNil(t, dtoResult) assert.NotNil(t, dtoResult)
assert.Equal(t, DtoType(2), *dtoResult) assert.Equal(t, DtoType(2), *dtoResult)
// 测试 ToDto 方法,传入不存在的值 // 测试 ToDTO 方法,传入不存在的值
tmpModelThree := ModelType("Three") tmpEntityThree := EntityType("Three")
modelInvalid = &tmpModelThree entityInvalid = &tmpEntityThree
dtoInvalidResult := converter.ToDto(modelInvalid) dtoInvalidResult := converter.ToDTO(entityInvalid)
assert.Nil(t, dtoInvalidResult) assert.Nil(t, dtoInvalidResult)
} }

View File

@@ -8,7 +8,7 @@ git tag jwtutil/v0.0.2
git tag id/v0.0.2 git tag id/v0.0.2
git tag slug/v0.0.1 git tag slug/v0.0.1
git tag name_generator/v0.0.1 git tag name_generator/v0.0.1
git tag mapper/v0.0.2 git tag mapper/v0.0.3
git tag password/v0.0.1 git tag password/v0.0.1
git tag entgo/v1.1.31 git tag entgo/v1.1.31