Browse Source

完成通用实体crud的编写

yjp 1 year ago
parent
commit
7d02b378b7

+ 14 - 13
binding/binding.go

@@ -2,6 +2,7 @@ package binding
 
 import (
 	"git.sxidc.com/go-framework/baize/api"
+	"git.sxidc.com/go-framework/baize/binding/request"
 	"git.sxidc.com/go-framework/baize/domain"
 	"git.sxidc.com/go-framework/baize/infrastructure"
 	"git.sxidc.com/go-tools/utils/reflectutils"
@@ -52,9 +53,9 @@ func StaticFile(binder *Binder, item *StaticFileBindItem) {
 	item.bind(binder)
 }
 
-type DTOBindFunc func(c *api.Context, dto DTO) error
-type FormDomainObjectsFunc func(c *api.Context, dto DTO) ([]domain.Object, error)
-type ServiceFunc[O any] func(c *api.Context, dto DTO, objects []domain.Object, i *infrastructure.Infrastructure) (O, error)
+type DTOBindFunc func(c *api.Context, dto request.DTO) error
+type FormDomainObjectsFunc func(c *api.Context, dto request.DTO) ([]domain.Object, error)
+type ServiceFunc[O any] func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (O, error)
 type ResponseFunc[O any] func(c *api.Context, statusCode int, data O, err error)
 
 // SimpleBindItem  路由条目
@@ -63,7 +64,7 @@ type SimpleBindItem[O any] struct {
 	Path string
 
 	// 使用的dto,非必传,当dto为nil时,说明该接口没有参数
-	DTO DTO
+	DTO request.DTO
 
 	// 可选的dto绑定函数
 	// 非必传,POST和PUT请求默认为JsonBody,DELETE默认为PathParams,GET默认为QueryParams
@@ -128,13 +129,13 @@ func (item *BindItem[O]) bind(binder *Binder, middlewares ...api.Handler) {
 	// 给单个路由增加中间件
 	handlers := []api.Handler{
 		func(c *api.Context) {
-			dto := item.DTO
+			itemDTO := item.DTO
 
 			// 有请求数据
-			if dto != nil {
+			if itemDTO != nil {
 				// 将请求数据解析到dto中
 				if item.DTOBindFunc != nil {
-					err := item.DTOBindFunc(c, dto)
+					err := item.DTOBindFunc(c, itemDTO)
 					if err != nil {
 						var outputZero O
 						item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
@@ -145,21 +146,21 @@ func (item *BindItem[O]) bind(binder *Binder, middlewares ...api.Handler) {
 					case http.MethodPost:
 						fallthrough
 					case http.MethodPut:
-						err := JsonBody(c, dto)
+						err := request.JsonBody(c, itemDTO)
 						if err != nil {
 							var outputZero O
 							item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
 							return
 						}
 					case http.MethodGet:
-						err := QueryParams(c, dto)
+						err := request.QueryParams(c, itemDTO)
 						if err != nil {
 							var outputZero O
 							item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
 							return
 						}
 					case http.MethodDelete:
-						err := PathParams(c, dto)
+						err := request.PathParams(c, itemDTO)
 						if err != nil {
 							var outputZero O
 							item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
@@ -172,7 +173,7 @@ func (item *BindItem[O]) bind(binder *Binder, middlewares ...api.Handler) {
 			// 进行领域对象转化
 			var domainObjects []domain.Object
 			if item.FormDomainObjectsFunc != nil {
-				innerDomainObjects, err := item.FormDomainObjectsFunc(c, dto)
+				innerDomainObjects, err := item.FormDomainObjectsFunc(c, itemDTO)
 				if err != nil {
 					var outputZero O
 					item.ResponseFunc(c, http.StatusOK, outputZero, err)
@@ -195,7 +196,7 @@ func (item *BindItem[O]) bind(binder *Binder, middlewares ...api.Handler) {
 						}
 
 						obj := reflect.New(reflectutils.PointerTypeElem(objectType)).Interface()
-						err := AssignDTOToDomainObject(dto, obj)
+						err := request.AssignDTOToDomainObject(itemDTO, obj)
 						if err != nil {
 							var outputZero O
 							item.ResponseFunc(c, http.StatusOK, outputZero, err)
@@ -215,7 +216,7 @@ func (item *BindItem[O]) bind(binder *Binder, middlewares ...api.Handler) {
 				i = item.Infrastructure
 			}
 
-			outputModel, err := item.ServiceFunc(c, dto, domainObjects, i)
+			outputModel, err := item.ServiceFunc(c, itemDTO, domainObjects, i)
 			if err != nil {
 				statusCode = fserr.ParseCode(err).HttpCode
 			}

+ 76 - 0
binding/request/common.go

@@ -0,0 +1,76 @@
+package request
+
+type ID interface {
+	GetID() string
+}
+
+type IDJsonBody struct {
+	ID string `json:"id" binding:"required"`
+}
+
+func (id *IDJsonBody) GetID() string {
+	return id.ID
+}
+
+type IDPath struct {
+	ID string `uri:"id" binding:"required"`
+}
+
+func (id *IDPath) GetID() string {
+	return id.ID
+}
+
+type IDQuery struct {
+	ID string `form:"id" binding:"required"`
+}
+
+func (id *IDQuery) GetID() string {
+	return id.ID
+}
+
+type TenantID interface {
+	GetTenantID() string
+}
+
+type TenantIDJsonBody struct {
+	TenantID string `json:"tenant_id" binding:"required"`
+}
+
+func (id *IDJsonBody) GetTenantID() string {
+	return id.ID
+}
+
+type TenantIDPath struct {
+	TenantID string `uri:"tenant_id" binding:"required"`
+}
+
+func (id *IDPath) GetTenantID() string {
+	return id.ID
+}
+
+type TenantIDQuery struct {
+	TenantID string `form:"tenant_id" binding:"required"`
+}
+
+func (id *IDQuery) GetTenantID() string {
+	return id.ID
+}
+
+type Query interface {
+	DTO
+	GetPageNo() int
+	GetPageSize() int
+}
+
+type BaseQuery struct {
+	PageNo   int `form:"pageNo"`
+	PageSize int `form:"pageSize"`
+}
+
+func (q *BaseQuery) GetPageNo() int {
+	return q.PageNo
+}
+
+func (q *BaseQuery) GetPageSize() int {
+	return q.PageSize
+}

+ 21 - 20
binding/dto.go → binding/request/dto.go

@@ -1,8 +1,7 @@
-package binding
+package request
 
 import (
 	"git.sxidc.com/go-framework/baize/domain"
-	"git.sxidc.com/go-framework/baize/infrastructure/logger"
 	"git.sxidc.com/go-framework/baize/tag/assign"
 	"git.sxidc.com/go-tools/utils/reflectutils"
 	"git.sxidc.com/service-supports/fserr"
@@ -15,46 +14,48 @@ func AssignDTOToDomainObject(dto DTO, domainObject domain.Object) error {
 	return assign.DefaultUsage(dto, domainObject)
 }
 
-func Field[T any](dto DTO, fieldName string) T {
+func Field[T any](dto DTO, fieldName string) (T, error) {
 	var zero T
 
+	if dto == nil {
+		return zero, fserr.New("DTO为nil")
+	}
+
 	fieldValue, err := getDTOFieldValue(dto, fieldName)
 	if err != nil {
-		logger.GetInstance().Error(err)
-		return zero
+		return zero, err
 	}
 
 	if !fieldValue.IsValid() {
-		err := fserr.New("dto字段" + fieldName + "无法赋值")
-		logger.GetInstance().Error(err)
-		return zero
+		return zero, fserr.New("dto" + fieldValue.Type().String() + "的字段" + fieldName + "无法赋值")
 	}
 
 	retValue, ok := fieldValue.Interface().(T)
 	if !ok {
-		err := fserr.New("dto字段" + fieldName + "无法转换类型")
-		logger.GetInstance().Error(err)
-		return zero
+		return zero, fserr.New("dto" + fieldValue.Type().String() + "的字段" + fieldName + "无法转换类型")
 	}
 
-	return retValue
+	return retValue, nil
 }
 
-func ToConcrete[T DTO](object DTO) T {
-	concrete, ok := object.(T)
-	if !ok {
-		logger.GetInstance().Error(fserr.New("DTO转化失败"))
+func ToConcrete[T DTO](dto DTO) (T, error) {
+	var zero T
 
-		var zero T
-		return zero
+	if dto == nil {
+		return zero, fserr.New("DTO为nil")
+	}
+
+	concrete, ok := dto.(T)
+	if !ok {
+		return zero, fserr.New("DTO转化失败")
 	}
 
-	return concrete
+	return concrete, nil
 }
 
 func getDTOFieldValue(dto DTO, fieldName string) (*reflect.Value, error) {
 	if dto == nil {
-		return nil, fserr.New("dto为nil")
+		return nil, fserr.New("DTO为nil")
 	}
 
 	dtoValue := reflect.ValueOf(dto)

+ 1 - 1
binding/dto_func.go → binding/request/dto_func.go

@@ -1,4 +1,4 @@
-package binding
+package request
 
 import (
 	"git.sxidc.com/go-framework/baize/api"

+ 1 - 1
binding/response.go → binding/response/response.go

@@ -1,4 +1,4 @@
-package binding
+package response
 
 import (
 	"encoding/json"

+ 1 - 1
binding/response_data.go → binding/response/response_data.go

@@ -1,4 +1,4 @@
-package binding
+package response
 
 type InfosData[T any] struct {
 	Infos      []T   `json:"infos"`

+ 1 - 1
binding/response_type.go → binding/response/response_type.go

@@ -1,4 +1,4 @@
-package binding
+package response
 
 type IDType interface {
 	~string | ~uint64

+ 0 - 9
binding/service/common.go

@@ -1,9 +0,0 @@
-package service
-
-import "git.sxidc.com/go-framework/baize/domain"
-
-type EntityCRUDCallbacks struct {
-	Before  func(object domain.Object) error
-	Success func(object domain.Object) error
-	Error   func(object domain.Object, err error) error
-}

+ 0 - 128
binding/service/entity_crud.go

@@ -1,128 +0,0 @@
-package service
-
-import (
-	"git.sxidc.com/go-framework/baize/api"
-	"git.sxidc.com/go-framework/baize/binding"
-	"git.sxidc.com/go-framework/baize/domain"
-	"git.sxidc.com/go-framework/baize/infrastructure"
-	"git.sxidc.com/go-framework/baize/infrastructure/database"
-	"git.sxidc.com/go-tools/utils/strutils"
-	"git.sxidc.com/service-supports/fserr"
-)
-
-const (
-	DatabaseExecutorOperations  = "operations"
-	DatabaseExecutorDataService = "data_service"
-)
-
-func CommonEntityCreate(tableName string, databaseExecutorType string, callbacks EntityCRUDCallbacks) binding.ServiceFunc[string] {
-	return func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (string, error) {
-		e, ok := objects[0].(domain.Entity)
-		if !ok {
-			return "", fserr.New("需要传递领域对象应该为实体")
-		}
-
-		err := e.GenerateID()
-		if err != nil {
-			return "", err
-		}
-
-		if callbacks.Before != nil {
-			err := callbacks.Before(e)
-			if err != nil {
-				return "", err
-			}
-		}
-
-		err = database.InsertEntity(chooseDatabaseExecutor(databaseExecutorType, i), tableName, e)
-		if err != nil {
-			if callbacks.Error != nil {
-				err := callbacks.Error(e, err)
-				if err != nil {
-					return "", err
-				}
-			}
-
-			return "", err
-		}
-
-		if callbacks.Success != nil {
-			err := callbacks.Success(e)
-			if err != nil {
-				return "", err
-			}
-		}
-
-		return e.ID(), nil
-	}
-}
-
-func CommonEntityDelete(tableName string, databaseExecutorType string, callbacks EntityCRUDCallbacks) binding.ServiceFunc[any] {
-	return func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
-		e, ok := objects[0].(domain.Entity)
-		if !ok {
-			return nil, fserr.New("需要传递领域对象应该为实体")
-		}
-
-		if strutils.IsStringEmpty(e.ID()) {
-			return nil, fserr.New("领域实体ID为空")
-		}
-
-		if callbacks.Before != nil {
-			err := callbacks.Before(e)
-			if err != nil {
-				return nil, err
-			}
-		}
-
-		err := database.DeleteEntity(chooseDatabaseExecutor(databaseExecutorType, i), tableName, e)
-		if err != nil {
-			if callbacks.Error != nil {
-				err := callbacks.Error(e, err)
-				if err != nil {
-					return nil, err
-				}
-			}
-
-			return nil, err
-		}
-
-		if callbacks.Success != nil {
-			err := callbacks.Success(e)
-			if err != nil {
-				return nil, err
-			}
-		}
-
-		return nil, nil
-	}
-}
-
-func CommonEntityUpdate() binding.ServiceFunc[any] {
-	return func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
-
-	}
-}
-
-func CommonEntityQuery() binding.ServiceFunc[binding.InfosData[any]] {
-	return func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (binding.InfosData[any], error) {
-
-	}
-}
-
-func CommonEntityQueryByID() binding.ServiceFunc[any] {
-	return func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
-
-	}
-}
-
-func chooseDatabaseExecutor(dataExecutorType string, i *infrastructure.Infrastructure) database.Executor {
-	switch dataExecutorType {
-	case DatabaseExecutorOperations:
-		return i.DBOperations()
-	case DatabaseExecutorDataService:
-		return i.DataService()
-	default:
-		return i.DBOperations()
-	}
-}

+ 48 - 0
binding/service/entity_crud/callbacks.go

@@ -0,0 +1,48 @@
+package entity_crud
+
+import (
+	"git.sxidc.com/go-framework/baize/domain"
+	"git.sxidc.com/go-framework/baize/infrastructure/database"
+)
+
+type Callbacks[O any] struct {
+	BeforeDBOperate func(e domain.Entity, dbExecutor database.Executor) error
+	OnReturn        func(e domain.Entity, dbExecutor database.Executor, output O) (O, error)
+	OnError         func(e domain.Entity, err error, dbExecutor database.Executor, defaultErrOutput O) (O, error)
+}
+
+func callbackBeforeDBOperate[O any](callbacks *Callbacks[O], e domain.Entity, dbExecutor database.Executor) error {
+	if callbacks == nil {
+		return nil
+	}
+
+	if callbacks.BeforeDBOperate == nil {
+		return callbacks.BeforeDBOperate(e, dbExecutor)
+	}
+
+	return nil
+}
+
+func callbackOnReturn[O any](callbacks *Callbacks[O], e domain.Entity, dbExecutor database.Executor, output O) (O, error) {
+	if callbacks == nil {
+		return output, nil
+	}
+
+	if callbacks.OnReturn == nil {
+		return callbacks.OnReturn(e, dbExecutor, output)
+	}
+
+	return output, nil
+}
+
+func callbackOnError[O any](callbacks *Callbacks[O], e domain.Entity, err error, dbExecutor database.Executor, defaultErrOutput O) (O, error) {
+	if callbacks == nil {
+		return defaultErrOutput, err
+	}
+
+	if callbacks.OnError == nil {
+		return callbacks.OnError(e, err, dbExecutor, defaultErrOutput)
+	}
+
+	return defaultErrOutput, err
+}

+ 232 - 0
binding/service/entity_crud/service.go

@@ -0,0 +1,232 @@
+package entity_crud
+
+import (
+	"git.sxidc.com/go-framework/baize/api"
+	"git.sxidc.com/go-framework/baize/binding"
+	"git.sxidc.com/go-framework/baize/binding/request"
+	"git.sxidc.com/go-framework/baize/binding/response"
+	"git.sxidc.com/go-framework/baize/domain"
+	"git.sxidc.com/go-framework/baize/infrastructure"
+	"git.sxidc.com/go-framework/baize/infrastructure/database"
+	"git.sxidc.com/go-framework/baize/infrastructure/database/sql"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"git.sxidc.com/service-supports/fserr"
+	"git.sxidc.com/service-supports/fslog"
+	"reflect"
+)
+
+func CommonEntityCreate(tableName string, dbExecutorType string, callbacks *Callbacks[string]) binding.ServiceFunc[string] {
+	return func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (string, error) {
+		dbExecutor := i.ChooseDBExecutor(dbExecutorType)
+
+		e, ok := objects[0].(domain.Entity)
+		if !ok {
+			return "", fserr.New("需要传递领域对象应该为实体")
+		}
+
+		err := e.GenerateID()
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecutor, "")
+		}
+
+		err = callbackBeforeDBOperate(callbacks, e, dbExecutor)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecutor, "")
+		}
+
+		err = database.InsertEntity(dbExecutor, tableName, e)
+		if err != nil {
+			if database.IsErrorDBRecordHasExist(err) {
+				err = fserr.New(e.DomainCNName() + "已存在")
+			}
+
+			return callbackOnError(callbacks, e, err, dbExecutor, "")
+		}
+
+		return callbackOnReturn(callbacks, e, dbExecutor, e.GetID())
+	}
+}
+
+func CommonEntityDelete(tableName string, databaseExecutorType string, callbacks *Callbacks[any]) binding.ServiceFunc[any] {
+	return func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+		dbExecute := i.ChooseDBExecutor(databaseExecutorType)
+
+		e, ok := objects[0].(domain.Entity)
+		if !ok {
+			return nil, fserr.New("需要传递领域对象应该为实体")
+		}
+
+		if strutils.IsStringEmpty(e.GetID()) {
+			err := fserr.New("领域实体ID为空")
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		exist, err := database.CheckExist(dbExecute, &sql.CheckExistExecuteParams{
+			TableName:  tableName,
+			Conditions: sql.NewConditions().Equal(e.IDColumnName(), e.GetID()),
+		})
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		if !exist {
+			err := fserr.New(e.DomainCNName() + "不存在")
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		err = callbackBeforeDBOperate(callbacks, e, dbExecute)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		err = database.DeleteEntity(dbExecute, tableName, e)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		return callbackOnReturn(callbacks, e, dbExecute, nil)
+	}
+}
+
+func CommonEntityUpdate(tableName string, databaseExecutorType string, callbacks *Callbacks[any]) binding.ServiceFunc[any] {
+	return func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+		dbExecute := i.ChooseDBExecutor(databaseExecutorType)
+
+		e, ok := objects[0].(domain.Entity)
+		if !ok {
+			return nil, fserr.New("需要传递领域对象应该为实体")
+		}
+
+		if strutils.IsStringEmpty(e.GetID()) {
+			err := fserr.New("领域实体ID为空")
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		exist, err := database.CheckExist(dbExecute, &sql.CheckExistExecuteParams{
+			TableName:  tableName,
+			Conditions: sql.NewConditions().Equal(e.IDColumnName(), e.GetID()),
+		})
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		if !exist {
+			err := fserr.New(e.DomainCNName() + "不存在")
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		err = callbackBeforeDBOperate(callbacks, e, dbExecute)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		err = database.UpdateEntity(dbExecute, tableName, e)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		return callbackOnReturn(callbacks, e, dbExecute, nil)
+	}
+}
+
+func CommonEntityQuery[O any](tableName string, databaseExecutorType string, callbacks *Callbacks[*response.InfosData[O]], conditionFieldCallback domain.ConditionFieldCallback) binding.ServiceFunc[*response.InfosData[O]] {
+	return func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (*response.InfosData[O], error) {
+		dbExecute := i.ChooseDBExecutor(databaseExecutorType)
+
+		queryDTO, ok := dto.(request.Query)
+		if !ok {
+			return &response.InfosData[O]{}, fserr.New("DTO不是Query")
+		}
+
+		e, ok := objects[0].(domain.Entity)
+		if !ok {
+			return &response.InfosData[O]{}, fserr.New("需要传递领域对象应该为实体")
+		}
+
+		conditions, err := e.QueryDBConditions(conditionFieldCallback)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, &response.InfosData[O]{})
+		}
+
+		err = callbackBeforeDBOperate(callbacks, e, dbExecute)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, &response.InfosData[O]{})
+		}
+
+		results, totalCount, err := database.Query(dbExecute, &sql.QueryExecuteParams{
+			TableName:  tableName,
+			Conditions: conditions,
+			PageNo:     queryDTO.GetPageNo(),
+			PageSize:   queryDTO.GetPageSize(),
+		})
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, &response.InfosData[O]{})
+		}
+
+		infos := make([]O, 0)
+		err = sql.ParseSqlResult(results, &infos)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, &response.InfosData[O]{})
+		}
+
+		output := &response.InfosData[O]{
+			Infos:      infos,
+			TotalCount: totalCount,
+			PageNo:     queryDTO.GetPageNo(),
+		}
+
+		return callbackOnReturn(callbacks, e, dbExecute, output)
+	}
+}
+
+func CommonEntityQueryByID[O any](tableName string, databaseExecutorType string, callbacks *Callbacks[O]) binding.ServiceFunc[O] {
+	return func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (O, error) {
+		var outputZero O
+		outputZeroValue := reflect.Zero(reflect.TypeOf(outputZero))
+		if outputZeroValue.Kind() != reflect.Pointer {
+			fslog.Error("建议在QueryByID时使用指针类型作为输出,以方便对查询结果进行后处理")
+		}
+
+		outputZeroValue.Set(reflect.New(outputZeroValue.Type().Elem()))
+
+		dbExecute := i.ChooseDBExecutor(databaseExecutorType)
+
+		e, ok := objects[0].(domain.Entity)
+		if !ok {
+			return outputZero, fserr.New("需要传递领域对象应该为实体")
+		}
+
+		if strutils.IsStringEmpty(e.GetID()) {
+			err := fserr.New("领域实体ID为空")
+			return callbackOnError(callbacks, e, err, dbExecute, nil)
+		}
+
+		err := callbackBeforeDBOperate(callbacks, e, dbExecute)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, outputZero)
+		}
+
+		result, err := database.QueryOne(dbExecute, &sql.QueryOneExecuteParams{
+			TableName:  tableName,
+			Conditions: sql.NewConditions().Equal(e.IDColumnName(), e.GetID()),
+		})
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, outputZero)
+		}
+
+		var info O
+		var infoPointer any
+
+		infoPointer = &info
+		if outputZeroValue.Kind() == reflect.Pointer {
+			infoPointer = info
+		}
+
+		err = sql.ParseSqlResult(result, infoPointer)
+		if err != nil {
+			return callbackOnError(callbacks, e, err, dbExecute, outputZero)
+		}
+
+		return callbackOnReturn(callbacks, e, dbExecute, info)
+	}
+}

+ 9 - 0
domain/common_fields.go → domain/common.go

@@ -6,6 +6,15 @@ import (
 	"time"
 )
 
+const (
+	ColumnID               = "id"
+	ColumnTenantID         = "tenant_id"
+	ColumnCreateUserID     = "create_user_id"
+	ColumnLastUpdateUserID = "last_update_user_id"
+	ColumnCreatedTime      = "created_time"
+	ColumnLastUpdatedTime  = "last_updated_time"
+)
+
 const (
 	FieldIDLen = 32
 )

+ 11 - 4
domain/entity.go

@@ -10,11 +10,13 @@ import (
 type ConditionFieldCallback func(conditions *sql.Conditions, fieldName string, columnName string, value any) (hasDeal bool)
 
 type Entity interface {
+	Object
 	DomainCNName() string
 	GenerateID() error
-	ID() string
-	CheckID(domainCNName string, errCode int) error
-	QueryDBConditions(callback ConditionFieldCallback) sql.Conditions
+	GetID() string
+	CheckID(errCode int) error
+	IDColumnName() string
+	QueryDBConditions(callback ConditionFieldCallback) (*sql.Conditions, error)
 }
 
 type BaseEntity struct {
@@ -25,8 +27,9 @@ func (e *BaseEntity) DomainCNName() string {
 	return "基础实体"
 }
 
-func (e *BaseEntity) GenerateID() {
+func (e *BaseEntity) GenerateID() error {
 	e.ID = strutils.SimpleUUID()
+	return nil
 }
 
 func (e *BaseEntity) GetID() string {
@@ -45,6 +48,10 @@ func (e *BaseEntity) CheckID(errCode int) error {
 	return nil
 }
 
+func (e *BaseEntity) IDColumnName() string {
+	return ColumnID
+}
+
 func (e *BaseEntity) QueryDBConditions(callback ConditionFieldCallback) (*sql.Conditions, error) {
 	fields, err := sql_mapping.DefaultUsage(e)
 	if err != nil {

+ 25 - 17
domain/object.go

@@ -1,7 +1,6 @@
 package domain
 
 import (
-	"git.sxidc.com/go-framework/baize/infrastructure/logger"
 	"git.sxidc.com/go-tools/utils/reflectutils"
 	"git.sxidc.com/service-supports/fserr"
 	"reflect"
@@ -13,33 +12,39 @@ func HasField(object Object, fieldName string) bool {
 	return hasField(object, fieldName)
 }
 
-func SetField[T any](object Object, fieldName string, value T) {
+func SetField[T any](object Object, fieldName string, value T) error {
+	if object == nil {
+		return fserr.New("领域对象为nil")
+	}
+
 	fieldValue, err := getFieldValue(object, fieldName)
 	if err != nil {
-		logger.GetInstance().Error(err)
-		return
+		return err
 	}
 
 	if !fieldValue.IsValid() || !fieldValue.CanSet() {
-		err := fserr.New("object字段" + fieldName + "无法赋值")
-		logger.GetInstance().Error(err)
-		return
+		return fserr.New("领域对象" + fieldValue.Type().String() + "的字段" + fieldName + "无法赋值")
 	}
 
 	fieldValue.Set(reflect.ValueOf(value))
+
+	return nil
 }
 
 func Field[T any](object Object, fieldName string) (T, error) {
 	var zero T
 
+	if object == nil {
+		return zero, fserr.New("领域对象为nil")
+	}
+
 	fieldValue, err := getFieldValue(object, fieldName)
 	if err != nil {
 		return zero, err
 	}
 
 	if !fieldValue.IsValid() {
-		err := fserr.New("object字段" + fieldName + "无法赋值")
-		return zero, err
+		return zero, fserr.New("领域对象" + fieldValue.Type().String() + "的字段" + fieldName + "无法赋值")
 	}
 
 	retValue, ok := fieldValue.Interface().(T)
@@ -50,16 +55,19 @@ func Field[T any](object Object, fieldName string) (T, error) {
 	return retValue, nil
 }
 
-func ToConcrete[T Object](object Object) T {
+func ToConcrete[T Object](object Object) (T, error) {
+	var zero T
+
+	if object == nil {
+		return zero, fserr.New("领域对象为nil")
+	}
+
 	concrete, ok := object.(T)
 	if !ok {
-		logger.GetInstance().Error(fserr.New("领域对象转化失败"))
-
-		var zero T
-		return zero
+		return zero, fserr.New("领域对象转化失败")
 	}
 
-	return concrete
+	return concrete, nil
 }
 
 func hasField(object Object, fieldName string) bool {
@@ -82,13 +90,13 @@ func hasField(object Object, fieldName string) bool {
 
 func getFieldValue(object Object, fieldName string) (*reflect.Value, error) {
 	if object == nil {
-		return nil, fserr.New("object为nil")
+		return nil, fserr.New("领域对象为nil")
 	}
 
 	objectValue := reflect.ValueOf(object)
 
 	if reflectutils.IsValueStructOrStructPointer(objectValue) {
-		return nil, fserr.New("object必须是结构或结构指针")
+		return nil, fserr.New("领域对象必须是结构或结构指针")
 	}
 
 	fieldValue := reflectutils.PointerValueElem(objectValue).FieldByName(fieldName)

+ 18 - 16
examples/binding/main.go

@@ -5,6 +5,8 @@ import (
 	"git.sxidc.com/go-framework/baize/api"
 	"git.sxidc.com/go-framework/baize/application"
 	"git.sxidc.com/go-framework/baize/binding"
+	"git.sxidc.com/go-framework/baize/binding/request"
+	"git.sxidc.com/go-framework/baize/binding/response"
 	"git.sxidc.com/go-framework/baize/domain"
 	"git.sxidc.com/go-framework/baize/infrastructure"
 	"git.sxidc.com/go-framework/baize/infrastructure/database"
@@ -142,10 +144,10 @@ func main() {
 	// 创建班级
 	binding.PostBind(binder, &binding.SimpleBindItem[string]{
 		Path:         "/class/create",
-		ResponseFunc: binding.SendIDResponse[string],
+		ResponseFunc: response.SendIDResponse[string],
 		DTO:          &CreateClassJsonBody{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (string, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (string, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 			e.ID = strutils.SimpleUUID()
 
@@ -161,10 +163,10 @@ func main() {
 	// 删除班级
 	binding.DeleteBind(binder, &binding.SimpleBindItem[any]{
 		Path:         "/class/:id/delete",
-		ResponseFunc: binding.SendMsgResponse,
+		ResponseFunc: response.SendMsgResponse,
 		DTO:          &DeleteClassPathParams{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 
 			err := database.DeleteEntity(i.DBOperations(), tableName, e)
@@ -179,10 +181,10 @@ func main() {
 	// 修改班级
 	binding.PutBind(binder, &binding.SimpleBindItem[any]{
 		Path:         "/class/update",
-		ResponseFunc: binding.SendMsgResponse,
+		ResponseFunc: response.SendMsgResponse,
 		DTO:          &UpdateClassJsonBody{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 
 			result, err := database.QueryOne(i.DBOperations(), &sql.QueryOneExecuteParams{
@@ -217,15 +219,15 @@ func main() {
 	})
 
 	// 查询班级
-	binding.GetBind(binder, &binding.SimpleBindItem[binding.InfosData[ClassInfo]]{
+	binding.GetBind(binder, &binding.SimpleBindItem[response.InfosData[ClassInfo]]{
 		Path:         "/class/query",
-		ResponseFunc: binding.SendInfosResponse[ClassInfo],
+		ResponseFunc: response.SendInfosResponse[ClassInfo],
 		DTO:          &QueryClassesQueryParams{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (binding.InfosData[ClassInfo], error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (response.InfosData[ClassInfo], error) {
 			e := domain.ToConcrete[*Class](objects[0])
-			pageNo := binding.Field[int](dto, "PageNo")
-			pageSize := binding.Field[int](dto, "PageSize")
+			pageNo := dto.Field[int](dto, "PageNo")
+			pageSize := dto.Field[int](dto, "PageSize")
 
 			conditions := sql.NewConditions()
 
@@ -240,7 +242,7 @@ func main() {
 				PageSize:   pageSize,
 			})
 			if err != nil {
-				return binding.InfosData[ClassInfo]{
+				return response.InfosData[ClassInfo]{
 					Infos: make([]ClassInfo, 0),
 				}, nil
 			}
@@ -248,12 +250,12 @@ func main() {
 			classInfos := make([]ClassInfo, 0)
 			err = sql.ParseSqlResult(results, &classInfos)
 			if err != nil {
-				return binding.InfosData[ClassInfo]{
+				return response.InfosData[ClassInfo]{
 					Infos: make([]ClassInfo, 0),
 				}, nil
 			}
 
-			return binding.InfosData[ClassInfo]{
+			return response.InfosData[ClassInfo]{
 				Infos:      classInfos,
 				TotalCount: totalCount,
 				PageNo:     pageNo,
@@ -264,10 +266,10 @@ func main() {
 	// 通过ID获取班级
 	binding.GetBind(binder, &binding.SimpleBindItem[*ClassInfo]{
 		Path:         "/class/get",
-		ResponseFunc: binding.SendInfoResponse[*ClassInfo],
+		ResponseFunc: response.SendInfoResponse[*ClassInfo],
 		DTO:          &GetClassQueryParams{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (*ClassInfo, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (*ClassInfo, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 
 			result, err := database.QueryOne(i.DBOperations(), &sql.QueryOneExecuteParams{

+ 18 - 16
examples/binding_ds/main.go

@@ -5,6 +5,8 @@ import (
 	"git.sxidc.com/go-framework/baize/api"
 	"git.sxidc.com/go-framework/baize/application"
 	"git.sxidc.com/go-framework/baize/binding"
+	"git.sxidc.com/go-framework/baize/binding/request"
+	"git.sxidc.com/go-framework/baize/binding/response"
 	"git.sxidc.com/go-framework/baize/domain"
 	"git.sxidc.com/go-framework/baize/infrastructure"
 	"git.sxidc.com/go-framework/baize/infrastructure/database"
@@ -104,10 +106,10 @@ func main() {
 	// 创建班级
 	binding.PostBind(binder, &binding.SimpleBindItem[string]{
 		Path:         "/class/create",
-		ResponseFunc: binding.SendIDResponse[string],
+		ResponseFunc: response.SendIDResponse[string],
 		DTO:          &CreateClassJsonBody{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (string, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (string, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 			e.ID = strutils.SimpleUUID()
 
@@ -123,10 +125,10 @@ func main() {
 	// 删除班级
 	binding.DeleteBind(binder, &binding.SimpleBindItem[any]{
 		Path:         "/class/:id/delete",
-		ResponseFunc: binding.SendMsgResponse,
+		ResponseFunc: response.SendMsgResponse,
 		DTO:          &DeleteClassPathParams{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 
 			err := database.DeleteEntity(i.DataService(), tableName, e)
@@ -141,10 +143,10 @@ func main() {
 	// 修改班级
 	binding.PutBind(binder, &binding.SimpleBindItem[any]{
 		Path:         "/class/update",
-		ResponseFunc: binding.SendMsgResponse,
+		ResponseFunc: response.SendMsgResponse,
 		DTO:          &UpdateClassJsonBody{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 
 			result, err := database.QueryOne(i.DataService(), &sql.QueryOneExecuteParams{
@@ -179,15 +181,15 @@ func main() {
 	})
 
 	// 查询班级
-	binding.GetBind(binder, &binding.SimpleBindItem[binding.InfosData[ClassInfo]]{
+	binding.GetBind(binder, &binding.SimpleBindItem[response.InfosData[ClassInfo]]{
 		Path:         "/class/query",
-		ResponseFunc: binding.SendInfosResponse[ClassInfo],
+		ResponseFunc: response.SendInfosResponse[ClassInfo],
 		DTO:          &QueryClassesQueryParams{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (binding.InfosData[ClassInfo], error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (response.InfosData[ClassInfo], error) {
 			e := domain.ToConcrete[*Class](objects[0])
-			pageNo := binding.Field[int](dto, "PageNo")
-			pageSize := binding.Field[int](dto, "PageSize")
+			pageNo := dto.Field[int](dto, "PageNo")
+			pageSize := dto.Field[int](dto, "PageSize")
 
 			conditions := sql.NewConditions()
 
@@ -202,7 +204,7 @@ func main() {
 				PageSize:   pageSize,
 			})
 			if err != nil {
-				return binding.InfosData[ClassInfo]{
+				return response.InfosData[ClassInfo]{
 					Infos: make([]ClassInfo, 0),
 				}, nil
 			}
@@ -210,12 +212,12 @@ func main() {
 			classInfos := make([]ClassInfo, 0)
 			err = sql.ParseSqlResult(results, &classInfos)
 			if err != nil {
-				return binding.InfosData[ClassInfo]{
+				return response.InfosData[ClassInfo]{
 					Infos: make([]ClassInfo, 0),
 				}, nil
 			}
 
-			return binding.InfosData[ClassInfo]{
+			return response.InfosData[ClassInfo]{
 				Infos:      classInfos,
 				TotalCount: totalCount,
 				PageNo:     pageNo,
@@ -226,10 +228,10 @@ func main() {
 	// 通过ID获取班级
 	binding.GetBind(binder, &binding.SimpleBindItem[*ClassInfo]{
 		Path:         "/class/get",
-		ResponseFunc: binding.SendInfoResponse[*ClassInfo],
+		ResponseFunc: response.SendInfoResponse[*ClassInfo],
 		DTO:          &GetClassQueryParams{},
 		Objects:      []domain.Object{&Class{}},
-		ServiceFunc: func(c *api.Context, dto binding.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (*ClassInfo, error) {
+		ServiceFunc: func(c *api.Context, dto request.DTO, objects []domain.Object, i *infrastructure.Infrastructure) (*ClassInfo, error) {
 			e := domain.ToConcrete[*Class](objects[0])
 
 			result, err := database.QueryOne(i.DataService(), &sql.QueryOneExecuteParams{

+ 4 - 0
infrastructure/database/database.go

@@ -224,6 +224,10 @@ func UpdateEntity(executor Executor, tableName string, e any) error {
 		if field.IsKey {
 			conditions.Equal(field.ColumnName, field.Value)
 		} else {
+			if reflect.ValueOf(field.Value).IsZero() && !field.CanUpdateClear {
+				continue
+			}
+
 			tableRow.Add(field.ColumnName, field.Value)
 		}
 	}

+ 17 - 0
infrastructure/infrastructure.go

@@ -1,6 +1,7 @@
 package infrastructure
 
 import (
+	"git.sxidc.com/go-framework/baize/infrastructure/database"
 	"git.sxidc.com/go-framework/baize/infrastructure/database/data_service"
 	"git.sxidc.com/go-framework/baize/infrastructure/database/operations"
 )
@@ -65,6 +66,22 @@ func DestroyInfrastructure(i *Infrastructure) {
 	return
 }
 
+const (
+	DBExecutorOperations  = "operations"
+	DBExecutorDataService = "data_service"
+)
+
+func (i Infrastructure) ChooseDBExecutor(dataExecutorType string) database.Executor {
+	switch dataExecutorType {
+	case DBExecutorOperations:
+		return i.DBOperations()
+	case DBExecutorDataService:
+		return i.DataService()
+	default:
+		return i.DBOperations()
+	}
+}
+
 func (i Infrastructure) DBOperations() *operations.Operations {
 	return i.dbOperations
 }