Browse Source

完成关联实体

yjp 1 năm trước cách đây
mục cha
commit
c84dda0b39
2 tập tin đã thay đổi với 455 bổ sung15 xóa
  1. 336 10
      convenient/relation/remote/service.go
  2. 119 5
      convenient/relation/remote/simple.go

+ 336 - 10
convenient/relation/remote/service.go

@@ -3,33 +3,359 @@ package remote
 import (
 	"git.sxidc.com/go-framework/baize/convenient/binding"
 	"git.sxidc.com/go-framework/baize/convenient/binding/request"
+	"git.sxidc.com/go-framework/baize/convenient/binding/response"
 	"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 UpdateFromExist(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
 	}
 }
 
-func Query[TI any](tableName string) binding.ServiceFunc[TI] {
-	return func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) (TI, error) {
-		var info TI
-		return info, nil
+func QueryFromExist[TI any](middleTableName string,
+	fromTableName string, fromRelationColumnName string,
+	toTableName string, toRelationColumnName string) binding.ServiceFunc[response.InfosData[TI]] {
+	return func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) (response.InfosData[TI], error) {
+		errResponse := response.InfosData[TI]{
+			Infos: make([]TI, 0),
+		}
+
+		if params == nil {
+			return errResponse, fserr.New("请求参数为空")
+		}
+
+		object := objects[0]
+		if object == nil {
+			return errResponse, fserr.New("领域实体为空")
+		}
+
+		dbExecutor := i.DBExecutor()
+
+		queryParams, ok := params.(request.Query)
+		if !ok {
+			return errResponse, fserr.New("请求参数不是Query接口")
+		}
+
+		fromEntity, ok := object.(entity.Entity)
+		if !ok {
+			return errResponse, fserr.New("领域对象不是实体")
+		}
+
+		// from存在性校验
+		fromExist, err := database.CheckExist(dbExecutor, &sql.CheckExistExecuteParams{
+			TableName:  fromTableName,
+			Conditions: sql.NewConditions().Equal(entity.ColumnID, fromEntity.GetID()),
+		})
+		if err != nil {
+			return errResponse, err
+		}
+
+		if !fromExist {
+			return errResponse, fserr.New(fromEntity.DomainCNName() + "不存在")
+		}
+
+		toIDResults, totalCount, err := database.Query(dbExecutor, &sql.QueryExecuteParams{
+			TableName:     middleTableName,
+			SelectColumns: []string{toRelationColumnName},
+			Conditions:    sql.NewConditions().Equal(fromRelationColumnName, fromEntity.GetID()),
+			PageNo:        queryParams.GetPageNo(),
+			PageSize:      queryParams.GetPageSize(),
+		})
+		if err != nil {
+			return errResponse, err
+		}
+
+		if toIDResults == nil || len(toIDResults) == 0 {
+			return response.InfosData[TI]{
+				Infos:      make([]TI, 0),
+				TotalCount: 0,
+				PageNo:     queryParams.GetPageNo(),
+			}, nil
+		}
+
+		toIDs := make([]string, 0)
+		for _, toIDResult := range toIDResults {
+			toIDs = append(toIDs, toIDResult.ColumnValueString(toRelationColumnName))
+		}
+
+		toResults, _, err := database.Query(dbExecutor, &sql.QueryExecuteParams{
+			TableName:  toTableName,
+			Conditions: sql.NewConditions().In(entity.ColumnID, toIDs),
+		})
+		if err != nil {
+			return errResponse, err
+		}
+
+		infos := make([]TI, 0)
+		err = sql.ParseSqlResult(toResults, &infos)
+		if err != nil {
+			return errResponse, err
+		}
+
+		output := response.InfosData[TI]{
+			Infos:      infos,
+			TotalCount: totalCount,
+			PageNo:     queryParams.GetPageNo(),
+		}
+
+		return output, nil
 	}
 }
 
-func UpdateRemote(tableName string) binding.ServiceFunc[any] {
+func UpdateFromRemote(middleTableName string,
+	fromRelationFieldName string, fromDomainCNName 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
+		}
+
+		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
 	}
 }
 
-func QueryRemote(tableName string) binding.ServiceFunc[[]string] {
-	return func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) ([]string, error) {
-		ids := make([]string, 0)
-		return ids, nil
+func QueryFromRemote(middleTableName string, fromRelationColumnName string, toRelationColumnName string) binding.ServiceFunc[response.InfosData[string]] {
+	return func(c *api.Context, params request.Params, objects []domain.Object, i *infrastructure.Infrastructure) (response.InfosData[string], error) {
+		errResponse := response.InfosData[string]{
+			Infos: make([]string, 0),
+		}
+
+		if params == nil {
+			return errResponse, fserr.New("请求参数为空")
+		}
+
+		object := objects[0]
+		if object == nil {
+			return errResponse, fserr.New("领域实体为空")
+		}
+
+		dbExecutor := i.DBExecutor()
+
+		queryParams, ok := params.(request.Query)
+		if !ok {
+			return errResponse, fserr.New("请求参数不是Query接口")
+		}
+
+		fromEntity, ok := object.(entity.Entity)
+		if !ok {
+			return errResponse, fserr.New("领域对象不是实体")
+		}
+
+		toIDResults, totalCount, err := database.Query(dbExecutor, &sql.QueryExecuteParams{
+			TableName:     middleTableName,
+			SelectColumns: []string{toRelationColumnName},
+			Conditions:    sql.NewConditions().Equal(fromRelationColumnName, fromEntity.GetID()),
+			PageNo:        queryParams.GetPageNo(),
+			PageSize:      queryParams.GetPageSize(),
+		})
+		if err != nil {
+			return errResponse, err
+		}
+
+		if toIDResults == nil || len(toIDResults) == 0 {
+			return response.InfosData[string]{
+				Infos:      make([]string, 0),
+				TotalCount: 0,
+				PageNo:     queryParams.GetPageNo(),
+			}, nil
+		}
+
+		toIDs := make([]string, 0)
+		for _, toIDResult := range toIDResults {
+			toIDs = append(toIDs, toIDResult.ColumnValueString(toRelationColumnName))
+		}
+
+		return response.InfosData[string]{
+			Infos:      toIDs,
+			TotalCount: totalCount,
+			PageNo:     queryParams.GetPageNo(),
+		}, nil
 	}
 }

+ 119 - 5
convenient/relation/remote/simple.go

@@ -1,15 +1,18 @@
 package remote
 
 import (
+	"fmt"
 	"git.sxidc.com/go-framework/baize/convenient/binding"
 	"git.sxidc.com/go-framework/baize/convenient/binding/request"
+	"git.sxidc.com/go-framework/baize/convenient/binding/response"
+	"git.sxidc.com/go-framework/baize/framwork/domain"
 	"git.sxidc.com/go-framework/baize/framwork/domain/entity"
+	"github.com/iancoleman/strcase"
 )
 
 // Simple 关联的Bind参数
-// LI 为左边实体的Info类型
-// RI 为右边实体的Info类型
-type Simple[LI any, RI any] struct {
+// I 为本地实体的Info类型
+type Simple[I any] struct {
 	// 左领域实体,注意是Entity类型
 	Left entity.Entity
 
@@ -50,9 +53,120 @@ type Simple[LI any, RI any] struct {
 	options *Options
 }
 
-func (simple *Simple[LI, RI]) bind(binder *binding.Binder) {
-	//options := simple.options
+func (simple *Simple[I]) bind(binder *binding.Binder) {
+	options := simple.options
+
+	leftDomainPath := entity.RelativeDomainPath(simple.Left)
+	rightDomainPath := entity.RelativeDomainPath(simple.Right)
+
+	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.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 {
+		if !options.disableLeftUpdate {
+			// 左到右更新
+			if !simple.LeftRemote {
+				binding.PostBind(binder, &binding.SimpleBindItem[any]{
+					Path:          leftDomainPath + rightDomainPath + "/update",
+					ResponseFunc:  response.SendMsgResponse,
+					RequestParams: simple.LeftUpdateJsonBody,
+					Objects:       []domain.Object{simple.Left},
+					ServiceFunc: UpdateFromExist(middleTableName,
+						leftTableName, simple.Left.DomainCNName(), leftRelationFieldName, leftRelationColumnName,
+						rightTableName, rightRelationColumnName),
+				})
+			} else {
+				binding.PostBind(binder, &binding.SimpleBindItem[any]{
+					Path:          leftDomainPath + rightDomainPath + "/update",
+					ResponseFunc:  response.SendMsgResponse,
+					RequestParams: simple.LeftUpdateJsonBody,
+					Objects:       []domain.Object{simple.Left},
+					ServiceFunc: UpdateFromRemote(middleTableName,
+						simple.Left.DomainCNName(), leftRelationFieldName, leftRelationColumnName,
+						rightTableName, rightRelationColumnName),
+				})
+			}
+		}
+
+		if !options.disableLeftQuery {
+			// 左到右查询
+			if !simple.LeftRemote {
+				binding.GetBind(binder, &binding.SimpleBindItem[response.InfosData[I]]{
+					Path:          leftDomainPath + rightDomainPath + "/query",
+					ResponseFunc:  response.SendInfosResponse[I],
+					RequestParams: simple.LeftQueryQueryParams,
+					Objects:       []domain.Object{simple.Left},
+					ServiceFunc: QueryFromExist[I](middleTableName,
+						leftTableName, leftRelationColumnName,
+						rightTableName, rightRelationColumnName),
+				})
+			} else {
+				binding.GetBind(binder, &binding.SimpleBindItem[response.InfosData[string]]{
+					Path:          leftDomainPath + rightDomainPath + "/query",
+					ResponseFunc:  response.SendInfosResponse[string],
+					RequestParams: simple.LeftQueryQueryParams,
+					Objects:       []domain.Object{simple.Left},
+					ServiceFunc:   QueryFromRemote(middleTableName, leftRelationColumnName, rightRelationColumnName),
+				})
+			}
+		}
+	}
 
+	if !options.disableRight {
+		if !options.disableRightUpdate {
+			// 右到左更新
+			if !simple.RightRemote {
+				binding.PostBind(binder, &binding.SimpleBindItem[any]{
+					Path:          rightDomainPath + leftDomainPath + "/update",
+					ResponseFunc:  response.SendMsgResponse,
+					RequestParams: simple.RightUpdateJsonBody,
+					Objects:       []domain.Object{simple.Right},
+					ServiceFunc: UpdateFromExist(middleTableName,
+						rightTableName, simple.Right.DomainCNName(), rightRelationFieldName, rightRelationColumnName,
+						leftTableName, leftRelationColumnName),
+				})
+			} else {
+				binding.PostBind(binder, &binding.SimpleBindItem[any]{
+					Path:          rightDomainPath + leftDomainPath + "/update",
+					ResponseFunc:  response.SendMsgResponse,
+					RequestParams: simple.RightUpdateJsonBody,
+					Objects:       []domain.Object{simple.Right},
+					ServiceFunc: UpdateFromRemote(middleTableName,
+						simple.Right.DomainCNName(), rightRelationFieldName, rightRelationColumnName,
+						leftTableName, leftRelationColumnName),
+				})
+			}
+		}
+
+		if !options.disableRightQuery {
+			// 右到左查询
+			if !simple.RightRemote {
+				binding.GetBind(binder, &binding.SimpleBindItem[response.InfosData[I]]{
+					Path:          rightDomainPath + leftDomainPath + "/query",
+					ResponseFunc:  response.SendInfosResponse[I],
+					RequestParams: simple.RightQueryQueryParams,
+					Objects:       []domain.Object{simple.Right},
+					ServiceFunc: QueryFromExist[I](middleTableName,
+						rightTableName, rightRelationColumnName,
+						leftTableName, leftRelationColumnName),
+				})
+			} else {
+				binding.GetBind(binder, &binding.SimpleBindItem[response.InfosData[string]]{
+					Path:          rightDomainPath + leftDomainPath + "/query",
+					ResponseFunc:  response.SendInfosResponse[string],
+					RequestParams: simple.RightQueryQueryParams,
+					Objects:       []domain.Object{simple.Right},
+					ServiceFunc:   QueryFromRemote(middleTableName, rightRelationColumnName, leftRelationColumnName),
+				})
+			}
+		}
+	}
 }
 
 func BindSimple[LI any, RI any](binder *binding.Binder, simple *Simple[LI, RI], opts ...Option) {