Bladeren bron

完成多对多更新

yjp 1 jaar geleden
bovenliggende
commit
6e6a087d03

+ 24 - 18
convenient/domain/configuration/api.go

@@ -7,6 +7,7 @@ import (
 	"git.sxidc.com/go-framework/baize/convenient/value_object"
 	"git.sxidc.com/go-framework/baize/framwork/api"
 	"git.sxidc.com/go-framework/baize/framwork/domain"
+	domainValueObject "git.sxidc.com/go-framework/baize/framwork/domain/value_object"
 	"git.sxidc.com/go-framework/baize/framwork/infrastructure"
 	"git.sxidc.com/go-framework/baize/framwork/infrastructure/database"
 	"git.sxidc.com/go-framework/baize/framwork/infrastructure/database/sql"
@@ -14,18 +15,19 @@ import (
 	"git.sxidc.com/service-supports/fserr"
 )
 
-func BindConfiguration(binder *binding.Binder, opts ...Option) {
-	options := new(Options)
+// Simple Bind参数
+type Simple struct {
+	// schema
+	Schema string
 
-	for _, opt := range opts {
-		opt(options)
-	}
+	// 可选配置项,通过WithXXX配置
+	options *Options
+}
 
-	configurationTableName := tableName
-	if strutils.IsStringNotEmpty(options.schema) {
-		configurationTableName = options.schema + "." + tableName
-	}
+func (simple *Simple) bind(binder *binding.Binder) {
+	options := simple.options
 
+	configurationTableName := domainValueObject.TableName(simple.Schema, &Entity{})
 	valueObjectOptions := []value_object.Option[any]{value_object.WithDisableQuery[any]()}
 
 	if options.disableCreate {
@@ -38,6 +40,7 @@ func BindConfiguration(binder *binding.Binder, opts ...Option) {
 
 	value_object.BindSimple(binder, &value_object.Simple[any]{
 		ValueObject:       &Entity{},
+		Schema:            simple.Schema,
 		CreateJsonBody:    &AddConfigurationJsonBody{},
 		DeleteQueryParams: &RemoveConfigurationJsonBody{},
 	}, valueObjectOptions...)
@@ -92,12 +95,21 @@ func BindConfiguration(binder *binding.Binder, opts ...Option) {
 	}
 }
 
+func BindConfiguration(binder *binding.Binder, simple *Simple, opts ...Option) {
+	options := new(Options)
+
+	for _, opt := range opts {
+		opt(options)
+	}
+
+	simple.options = options
+
+	simple.bind(binder)
+}
+
 type Option func(options *Options)
 
 type Options struct {
-	// 表schema
-	schema string
-
 	// 创建删除
 	disableCreate bool
 
@@ -108,12 +120,6 @@ type Options struct {
 	disableQuery bool
 }
 
-func WithSchema(schema string) Option {
-	return func(options *Options) {
-		options.schema = schema
-	}
-}
-
 func WithDisableCreate() Option {
 	return func(options *Options) {
 		options.disableCreate = true

+ 2 - 4
convenient/domain/configuration/entity.go

@@ -1,6 +1,7 @@
 package configuration
 
 import (
+	"git.sxidc.com/go-framework/baize/framwork/domain/value_object"
 	"git.sxidc.com/go-tools/utils/strutils"
 	"git.sxidc.com/service-supports/fserr"
 )
@@ -11,10 +12,6 @@ const (
 	ColumnValue = "value"
 )
 
-const (
-	tableName = "configurations"
-)
-
 const (
 	fieldScopeMaxLen = 256
 	fieldGroupMaxLen = 256
@@ -22,6 +19,7 @@ const (
 )
 
 type Entity struct {
+	value_object.Base
 	Scope string `sqlmapping:"column:scope;key;notUpdate;" sqlresult:"column:group;"`
 	Group string `sqlmapping:"column:group;key;notUpdate;" sqlresult:"column:group;"`
 	Value string `sqlmapping:"column:value;notUpdate;" sqlresult:"column:value;"`

+ 4 - 1
convenient/entity/simple.go

@@ -15,6 +15,9 @@ type Simple[I any] struct {
 	// 使用的领域实体,注意是Entity类型
 	Entity entity.Entity
 
+	// 数据库Schema
+	Schema string
+
 	// 创建使用的请求参数
 	CreateJsonBody request.Params
 
@@ -37,7 +40,7 @@ type Simple[I any] struct {
 func (simple *Simple[I]) bind(binder *binding.Binder) {
 	options := simple.options
 
-	tableName := entity.TableName(simple.Entity)
+	tableName := entity.TableName(simple.Schema, simple.Entity)
 	domainPath := entity.RelativeDomainPath(simple.Entity)
 
 	// 创建

+ 104 - 1
convenient/relation/many2many/service.go

@@ -5,11 +5,114 @@ import (
 	"git.sxidc.com/go-framework/baize/convenient/binding/request"
 	"git.sxidc.com/go-framework/baize/framwork/api"
 	"git.sxidc.com/go-framework/baize/framwork/domain"
+	"git.sxidc.com/go-framework/baize/framwork/domain/entity"
 	"git.sxidc.com/go-framework/baize/framwork/infrastructure"
+	"git.sxidc.com/go-framework/baize/framwork/infrastructure/database"
+	"git.sxidc.com/go-framework/baize/framwork/infrastructure/database/sql"
+	"git.sxidc.com/go-tools/utils/slice"
+	"git.sxidc.com/service-supports/fserr"
 )
 
-func Update(tableName string) binding.ServiceFunc[any] {
+func Update(middleTableName string, fromTableName string, fromDomainCNName string, fromRelationFieldName string, fromRelationColumnName string,
+	toTableName string, toRelationColumnName string) binding.ServiceFunc[any] {
 	return func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) (any, error) {
+		object := objects[0]
+		if object == nil {
+			return nil, fserr.New("领域实体为空")
+		}
+
+		dbExecutor := i.DBExecutor()
+
+		fromEntity, ok := object.(entity.Entity)
+		if !ok {
+			return nil, fserr.New("领域对象不是实体")
+		}
+
+		// 字段校验
+		err := fromEntity.CheckFieldID(fromEntity.DomainCNName())
+		if err != nil {
+			return nil, err
+		}
+
+		// from存在性校验
+		fromExist, err := database.CheckExist(dbExecutor, &sql.CheckExistExecuteParams{
+			TableName:  fromTableName,
+			Conditions: sql.NewConditions().Equal(entity.ColumnID, fromEntity.GetID()),
+		})
+		if err != nil {
+			return nil, err
+		}
+
+		if !fromExist {
+			return nil, fserr.New(fromEntity.DomainCNName() + "不存在")
+		}
+
+		if !domain.HasField(object, fromRelationFieldName) {
+			return nil, fserr.New("关联字段" + fromRelationFieldName + "不存在")
+		}
+
+		toIDs, err := domain.Field[[]string](object, fromRelationFieldName)
+		if err != nil {
+			return nil, err
+		}
+
+		if toIDs != nil && len(toIDs) != 0 {
+			for _, toID := range toIDs {
+				err := entity.CheckID(fromDomainCNName, fromRelationFieldName, toID)
+				if err != nil {
+					return nil, err
+				}
+			}
+
+			toIDs = slice.RemoveRepeatElement(toIDs)
+		}
+
+		err = database.Transaction(dbExecutor, func(tx database.Executor) error {
+			err := database.Delete(tx, &sql.DeleteExecuteParams{
+				TableName:  middleTableName,
+				Conditions: sql.NewConditions().Equal(fromRelationColumnName, fromEntity.GetID()),
+			})
+			if err != nil {
+				return err
+			}
+
+			if toIDs == nil || len(toIDs) == 0 {
+				return nil
+			}
+
+			toCount, err := database.Count(dbExecutor, &sql.CountExecuteParams{
+				TableName:  toTableName,
+				Conditions: sql.NewConditions().In(entity.ColumnID, toIDs),
+			})
+			if err != nil {
+				return err
+			}
+
+			if int(toCount) != len(toIDs) {
+				return fserr.New("部分{{ $toCNName }}不存在")
+			}
+
+			tableRows := make([]sql.TableRow, len(toIDs))
+			for index, toID := range toIDs {
+				tableRows[index] = *(sql.NewTableRow().
+					Add(fromRelationColumnName, fromEntity.GetID()).
+					Add(toRelationColumnName, toID))
+			}
+
+			err = database.InsertBatch(tx, &sql.InsertBatchExecuteParams{
+				TableName:     middleTableName,
+				TableRowBatch: tableRows,
+			})
+			if err != nil {
+				return err
+			}
+
+			return nil
+		})
+		if err != nil {
+			return nil, err
+		}
+
 		return nil, nil
 	}
 }

+ 6 - 4
convenient/relation/many2many/simple.go

@@ -42,12 +42,14 @@ func (simple *Simple[LI, RI]) bind(binder *binding.Binder) {
 	leftDomainPath := entity.RelativeDomainPath(simple.Left)
 	rightDomainPath := entity.RelativeDomainPath(simple.Right)
 
-	leftRelationFieldName := fmt.Sprintf("%sID", simple.Right.DomainCamelName())
-	rightRelationFieldName := fmt.Sprintf("%sID", simple.Left.DomainCamelName())
-	leftRelationColumnName := fmt.Sprintf("%s_id", strcase.ToSnake(simple.Right.DomainCamelName()))
-	rightRelationColumnName := fmt.Sprintf("%s_id", strcase.ToSnake(simple.Left.DomainCamelName()))
+	leftRelationFieldName := fmt.Sprintf("%sIDs", simple.Right.DomainCamelName())
+	rightRelationFieldName := fmt.Sprintf("%sIDs", simple.Left.DomainCamelName())
 
 	middleTableName := entity.SnakeDomainName(simple.Left) + "_and_" + entity.SnakeDomainName(simple.Right)
+	leftTableName := entity.TableName(simple.Left)
+	leftRelationColumnName := fmt.Sprintf("%s_id", strcase.ToSnake(simple.Right.DomainCamelName()))
+	rightTableName := entity.TableName(simple.Right)
+	rightRelationColumnName := fmt.Sprintf("%s_id", strcase.ToSnake(simple.Left.DomainCamelName()))
 
 	if !options.disableLeft {
 		if !options.disableLeftUpdate {

+ 4 - 4
convenient/relation/one2one/service.go

@@ -35,10 +35,6 @@ func Update(fromTableName string, fromRelationFieldName string, fromRelationColu
 			return nil, err
 		}
 
-		if !domain.HasField(object, fromRelationFieldName) {
-			return nil, fserr.New("关联字段" + fromRelationFieldName + "不存在")
-		}
-
 		// from存在性校验
 		fromResult, err := database.QueryOne(dbExecutor, &sql.QueryOneExecuteParams{
 			TableName:  fromTableName,
@@ -52,6 +48,10 @@ func Update(fromTableName string, fromRelationFieldName string, fromRelationColu
 			return nil, err
 		}
 
+		if !domain.HasField(object, fromRelationFieldName) {
+			return nil, fserr.New("关联字段" + fromRelationFieldName + "不存在")
+		}
+
 		existFrom := reflect.New(reflect.TypeOf(object).Elem()).Interface()
 		err = sql.ParseSqlResult(fromResult, existFrom)
 		if err != nil {

+ 6 - 2
convenient/relation/one2one/simple.go

@@ -20,6 +20,9 @@ type Simple[LI any, RI any] struct {
 	// 右领域实体,注意是Entity类型
 	Right entity.Entity
 
+	// 数据库Schema
+	Schema string
+
 	// 更新左实体关联使用的请求参数
 	LeftUpdateJsonBody request.WithID
 
@@ -45,14 +48,15 @@ type Simple[LI any, RI any] struct {
 func (simple *Simple[LI, RI]) bind(binder *binding.Binder) {
 	options := simple.options
 
-	leftTableName := entity.TableName(simple.Left)
 	leftDomainPath := entity.RelativeDomainPath(simple.Left)
-	rightTableName := entity.TableName(simple.Right)
 	rightDomainPath := entity.RelativeDomainPath(simple.Right)
 
 	leftRelationFieldName := fmt.Sprintf("%sID", simple.Right.DomainCamelName())
 	rightRelationFieldName := fmt.Sprintf("%sID", simple.Left.DomainCamelName())
+
+	leftTableName := entity.TableName(simple.Schema, simple.Left)
 	leftRelationColumnName := fmt.Sprintf("%s_id", strcase.ToSnake(simple.Right.DomainCamelName()))
+	rightTableName := entity.TableName(simple.Schema, simple.Right)
 	rightRelationColumnName := fmt.Sprintf("%s_id", strcase.ToSnake(simple.Left.DomainCamelName()))
 
 	if !options.disableLeft {

+ 2 - 2
convenient/value_object/service.go

@@ -196,7 +196,7 @@ func CreateTx(tableName string, callbacks *Callbacks[any]) binding.ServiceFunc[a
 				return err
 			}
 
-			err = database.InsertEntity(dbExecutor, tableName, valueObject)
+			err = database.InsertEntity(tx, tableName, valueObject)
 			if err != nil {
 				if database.IsErrorDBRecordHasExist(err) {
 					err = fserr.New(valueObject.DomainCNName() + "已存在")
@@ -240,7 +240,7 @@ func DeleteTx(tableName string, callbacks *Callbacks[any]) binding.ServiceFunc[a
 				return err
 			}
 
-			err = database.DeleteEntity(dbExecutor, tableName, valueObject)
+			err = database.DeleteEntity(tx, tableName, valueObject)
 			if err != nil {
 				return err
 			}

+ 4 - 1
convenient/value_object/simple.go

@@ -15,6 +15,9 @@ type Simple[I any] struct {
 	// 使用的领域实体,注意是ValueObject类型
 	ValueObject value_object.ValueObject
 
+	// 数据库schema
+	Schema string
+
 	// 创建使用的请求参数
 	CreateJsonBody request.Params
 
@@ -31,7 +34,7 @@ type Simple[I any] struct {
 func (simple *Simple[I]) bind(binder *binding.Binder) {
 	options := simple.options
 
-	tableName := value_object.TableName(simple.ValueObject)
+	tableName := value_object.TableName(simple.Schema, simple.ValueObject)
 	domainPath := value_object.RelativeDomainPath(simple.ValueObject)
 
 	// 创建

+ 1 - 0
examples/examples/project/application/service/class.go

@@ -22,6 +22,7 @@ func (app *Class) v1(appInstance *application.App) {
 
 	entity.BindSimple[class.Info](v1Binder, &entity.Simple[class.Info]{
 		Entity:             &class.Entity{},
+		Schema:             dbSchema,
 		CreateJsonBody:     &class.CreateJsonBody{},
 		DeleteQueryParams:  &class.DeletePathParams{},
 		UpdateJsonBody:     &class.UpdateJsonBody{},

+ 3 - 1
examples/examples/project/application/service/configuration.go

@@ -18,5 +18,7 @@ func (app *Configuration) Destroy() error {
 
 func (app *Configuration) prefixRoot(appInstance *application.App) {
 	prefixRootBinder := appInstance.Binder(application.RouterPrefix, "")
-	configuration.BindConfiguration(prefixRootBinder, configuration.WithSchema("test"))
+	configuration.BindConfiguration(prefixRootBinder, &configuration.Simple{
+		Schema: dbSchema,
+	})
 }

+ 1 - 0
examples/examples/project/application/service/family.go

@@ -22,6 +22,7 @@ func (app *Family) v1(appInstance *application.App) {
 
 	entity.BindSimple[family.Info](v1Binder, &entity.Simple[family.Info]{
 		Entity:             &family.Entity{},
+		Schema:             dbSchema,
 		CreateJsonBody:     &family.CreateJsonBody{},
 		DeleteQueryParams:  &family.DeletePathParams{},
 		UpdateJsonBody:     &family.UpdateJsonBody{},

+ 5 - 0
examples/examples/project/application/service/service.go

@@ -0,0 +1,5 @@
+package service
+
+const (
+	dbSchema = "test"
+)

+ 1 - 0
examples/examples/project/application/service/student.go

@@ -22,6 +22,7 @@ func (app *Student) v1(appInstance *application.App) {
 
 	entity.BindSimple[*student.Info](v1Binder, &entity.Simple[*student.Info]{
 		Entity:             &student.Entity{},
+		Schema:             dbSchema,
 		CreateJsonBody:     &student.CreateJsonBody{},
 		DeleteQueryParams:  &student.DeletePathParams{},
 		UpdateJsonBody:     &student.UpdateJsonBody{},

+ 1 - 0
examples/examples/project/application/service/student_and_family.go

@@ -24,6 +24,7 @@ func (app *StudentAndFamily) v1(appInstance *application.App) {
 	one2one.BindSimple(v1Binder, &one2one.Simple[student.Info, *family.Info]{
 		Left:                          &student.Entity{},
 		Right:                         &family.Entity{},
+		Schema:                        dbSchema,
 		LeftUpdateJsonBody:            &student.UpdateFamilyOfStudentJsonBody{},
 		LeftQueryQueryParams:          &student.QueryFamilyOfStudentQueryParams{},
 		LeftQueryWithRightQueryParams: &student.QueryStudentWithFamilyQueryParams{},

+ 20 - 0
framwork/domain/entity/base.go

@@ -6,6 +6,10 @@ type Base struct {
 	ID string `sqlmapping:"column:id;key;" sqlresult:"column:id;"`
 }
 
+func (e *Base) DBSchema() string {
+	return ""
+}
+
 func (e *Base) DomainCNName() string {
 	panic("领域实体没有实现DomainCNName接口")
 }
@@ -22,3 +26,19 @@ func (e *Base) GenerateID() error {
 func (e *Base) GetID() string {
 	return e.ID
 }
+
+func (e *Base) CheckFieldID(domainCNName string) error {
+	return CheckID(domainCNName, "ID", e.ID)
+}
+
+func (e *Base) IDColumnName() string {
+	return ColumnID
+}
+
+func (e *Base) ForCreate() error {
+	panic("领域实体没有实现ForCreate接口")
+}
+
+func (e *Base) ForUpdate() error {
+	panic("领域实体没有实现ForUpdate接口")
+}

+ 1 - 16
framwork/domain/entity/entity.go

@@ -6,6 +6,7 @@ import (
 
 type Entity interface {
 	domain.Object
+	DBSchema() string
 	DomainCNName() string
 	DomainCamelName() string
 	GenerateID() error
@@ -14,19 +15,3 @@ type Entity interface {
 	ForCreate() error
 	ForUpdate() error
 }
-
-func (e *Base) CheckFieldID(domainCNName string) error {
-	return CheckID(domainCNName, "ID", e.ID)
-}
-
-func (e *Base) IDColumnName() string {
-	return ColumnID
-}
-
-func (e *Base) ForCreate() error {
-	panic("领域实体没有实现ForCreate接口")
-}
-
-func (e *Base) ForUpdate() error {
-	panic("领域实体没有实现ForUpdate接口")
-}

+ 10 - 2
framwork/domain/entity/utils.go

@@ -7,8 +7,16 @@ import (
 	"github.com/iancoleman/strcase"
 )
 
-func TableName(e Entity) string {
-	return template.Plural(strcase.ToSnake(template.Id(e.DomainCamelName())))
+func TableName(schema string, e Entity) string {
+	if strutils.IsStringNotEmpty(e.DBSchema()) {
+		schema = e.DBSchema()
+	}
+
+	if strutils.IsStringEmpty(schema) {
+		return template.Plural(strcase.ToSnake(template.Id(e.DomainCamelName())))
+	} else {
+		return schema + "." + template.Plural(strcase.ToSnake(template.Id(e.DomainCamelName())))
+	}
 }
 
 func RelativeDomainPath(e Entity) string {

+ 23 - 0
framwork/domain/value_object/base.go

@@ -0,0 +1,23 @@
+package value_object
+
+type Base struct{}
+
+func (valueObject *Base) DBSchema() string {
+	return ""
+}
+
+func (valueObject *Base) DomainCNName() string {
+	panic("值对象没有实现DomainCNName接口")
+}
+
+func (valueObject *Base) DomainCamelName() string {
+	panic("值对象没有实现DomainCamelName接口")
+}
+
+func (valueObject *Base) CheckKeyFields() error {
+	panic("值对象没有实现CheckKeyFields接口")
+}
+
+func (valueObject *Base) ForCreate() error {
+	panic("值对象没有实现ForCreate接口")
+}

+ 11 - 2
framwork/domain/value_object/utils.go

@@ -1,12 +1,21 @@
 package value_object
 
 import (
+	"git.sxidc.com/go-tools/utils/strutils"
 	"git.sxidc.com/go-tools/utils/template"
 	"github.com/iancoleman/strcase"
 )
 
-func TableName(valueObject ValueObject) string {
-	return template.Plural(strcase.ToSnake(template.Id(valueObject.DomainCamelName())))
+func TableName(schema string, valueObject ValueObject) string {
+	if strutils.IsStringNotEmpty(valueObject.DBSchema()) {
+		schema = valueObject.DBSchema()
+	}
+
+	if strutils.IsStringEmpty(schema) {
+		return template.Plural(strcase.ToSnake(template.Id(valueObject.DomainCamelName())))
+	} else {
+		return schema + "." + template.Plural(strcase.ToSnake(template.Id(valueObject.DomainCamelName())))
+	}
 }
 
 func RelativeDomainPath(valueObject ValueObject) string {

+ 1 - 0
framwork/domain/value_object/value_object.go

@@ -4,6 +4,7 @@ import "git.sxidc.com/go-framework/baize/framwork/domain"
 
 type ValueObject interface {
 	domain.Object
+	DBSchema() string
 	DomainCNName() string
 	DomainCamelName() string
 	CheckKeyFields() error