github.com/unionj-cloud/go-doudou/v2@v2.3.5/toolkit/gormgen/internal/generate/query.go (about)

     1  package generate
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  
     9  	"gorm.io/gorm"
    10  	"gorm.io/gorm/schema"
    11  
    12  	"github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
    13  	"github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
    14  	"github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
    15  )
    16  
    17  type FieldParser interface {
    18  	GetFieldGenType(f *schema.Field) string
    19  }
    20  
    21  type dummyFieldParser struct{}
    22  
    23  func (dummyFieldParser) GetFieldGenType(*schema.Field) string { return "" }
    24  
    25  // QueryStructMeta struct info in generated code
    26  type QueryStructMeta struct {
    27  	db *gorm.DB
    28  
    29  	Generated       bool   // whether to generate db model
    30  	FileName        string // generated file name
    31  	S               string // the first letter(lower case)of simple Name (receiver)
    32  	QueryStructName string // internal query struct name
    33  	ModelStructName string // origin/model struct name
    34  	TableName       string // table name in db server
    35  	StructInfo      parser.Param
    36  	Fields          []*model.Field
    37  	Source          model.SourceCode
    38  	ImportPkgPaths  []string
    39  	ModelMethods    []*parser.Method // user custom method bind to db base struct
    40  
    41  	interfaceMode bool
    42  
    43  	PriKeyType string
    44  }
    45  
    46  // parseStruct get all elements of struct with gorm's Parse, ignore unexported elements
    47  func (b *QueryStructMeta) parseStruct(st interface{}) error {
    48  	stmt := gorm.Statement{DB: b.db}
    49  
    50  	err := stmt.Parse(st)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	b.TableName = stmt.Table
    55  	b.FileName = strings.ToLower(stmt.Table)
    56  
    57  	var fp FieldParser = dummyFieldParser{}
    58  	if fps, ok := st.(FieldParser); ok && fps != nil {
    59  		fp = fps
    60  	}
    61  	for _, f := range stmt.Schema.Fields {
    62  		b.appendOrUpdateField(&model.Field{
    63  			Name:          f.Name,
    64  			Type:          b.getFieldRealType(f.FieldType),
    65  			ColumnName:    f.DBName,
    66  			CustomGenType: fp.GetFieldGenType(f),
    67  		})
    68  	}
    69  	for _, r := range ParseStructRelationShip(&stmt.Schema.Relationships) {
    70  		r := r
    71  		b.appendOrUpdateField(&model.Field{Relation: &r})
    72  	}
    73  	return nil
    74  }
    75  
    76  // getFieldRealType  get basic type of field
    77  func (b *QueryStructMeta) getFieldRealType(f reflect.Type) string {
    78  	serializerInterface := reflect.TypeOf((*schema.SerializerInterface)(nil)).Elem()
    79  	if f.Implements(serializerInterface) || reflect.New(f).Type().Implements(serializerInterface) {
    80  		return "serializer"
    81  	}
    82  	scanValuer := reflect.TypeOf((*field.ScanValuer)(nil)).Elem()
    83  	if f.Implements(scanValuer) || reflect.New(f).Type().Implements(scanValuer) {
    84  		return "field"
    85  	}
    86  
    87  	if f.Kind() == reflect.Ptr {
    88  		f = f.Elem()
    89  	}
    90  	if f.String() == "time.Time" {
    91  		return "time.Time"
    92  	}
    93  	if f.String() == "[]uint8" || f.String() == "json.RawMessage" {
    94  		return "bytes"
    95  	}
    96  	return f.Kind().String()
    97  }
    98  
    99  // ReviseFieldName revise field name
   100  func (b *QueryStructMeta) ReviseFieldName() {
   101  	b.ReviseFieldNameFor(model.GormKeywords)
   102  }
   103  
   104  // ReviseFieldNameFor revise field name for keywords
   105  func (b *QueryStructMeta) ReviseFieldNameFor(keywords model.KeyWord) {
   106  	for _, m := range b.Fields {
   107  		m.EscapeKeywordFor(keywords)
   108  	}
   109  }
   110  
   111  // check field if in BaseStruct update else append
   112  func (b *QueryStructMeta) appendOrUpdateField(f *model.Field) {
   113  	if f.IsRelation() {
   114  		b.appendField(f)
   115  	}
   116  	if f.ColumnName == "" {
   117  		return
   118  	}
   119  	for i, m := range b.Fields {
   120  		if m.Name == f.Name {
   121  			b.Fields[i] = f
   122  			return
   123  		}
   124  	}
   125  	b.appendField(f)
   126  }
   127  
   128  func (b *QueryStructMeta) appendField(f *model.Field) { b.Fields = append(b.Fields, f) }
   129  
   130  // HasField check if BaseStruct has fields
   131  func (b *QueryStructMeta) HasField() bool { return len(b.Fields) > 0 }
   132  
   133  // check if struct is exportable and if struct in main package and if field's type is regular
   134  func (b *QueryStructMeta) check() (err error) {
   135  	if b.StructInfo.InMainPkg() {
   136  		return fmt.Errorf("can't generated data object for struct in main package, ignore:%s", b.ModelStructName)
   137  	}
   138  	if !isCapitalize(b.ModelStructName) {
   139  		return fmt.Errorf("can't generated data object for non-exportable struct, ignore:%s", b.QueryStructName)
   140  	}
   141  	return nil
   142  }
   143  
   144  // Relations related field
   145  func (b *QueryStructMeta) Relations() (result []field.Relation) {
   146  	for _, f := range b.Fields {
   147  		if f.IsRelation() {
   148  			result = append(result, *f.Relation)
   149  		}
   150  	}
   151  	return result
   152  }
   153  
   154  // StructComment struct comment
   155  func (b *QueryStructMeta) StructComment() string {
   156  	if b.TableName != "" {
   157  		return fmt.Sprintf(`mapped from table <%s>`, b.TableName)
   158  	}
   159  	return `mapped from object`
   160  }
   161  
   162  // ReviseDIYMethod check diy method duplication name
   163  func (b *QueryStructMeta) ReviseDIYMethod() error {
   164  	var duplicateMethodName []string
   165  	var tableName *parser.Method
   166  	methods := make([]*parser.Method, 0, len(b.ModelMethods))
   167  	methodMap := make(map[string]bool, len(b.ModelMethods))
   168  	for _, method := range b.ModelMethods {
   169  		if methodMap[method.MethodName] {
   170  			duplicateMethodName = append(duplicateMethodName, method.MethodName)
   171  			continue
   172  		}
   173  		if method.MethodName == "TableName" {
   174  			tableName = method
   175  		}
   176  		method.Receiver.Package = ""
   177  		method.Receiver.Type = b.ModelStructName
   178  		methods = append(methods, method)
   179  		methodMap[method.MethodName] = true
   180  	}
   181  	if tableName == nil {
   182  		methods = append(methods, parser.DefaultMethodTableName(b.ModelStructName))
   183  	} else {
   184  		//e.g. return "@@table" => return TableNameUser
   185  		tableName.Body = strings.ReplaceAll(tableName.Body, "\"@@table\"", "TableName"+b.ModelStructName)
   186  		//e.g. return "t_@@table" => return "t_user"
   187  		tableName.Body = strings.ReplaceAll(tableName.Body, "@@table", b.TableName)
   188  	}
   189  	b.ModelMethods = methods
   190  
   191  	if len(duplicateMethodName) > 0 {
   192  		return fmt.Errorf("can't generate struct with duplicated method, please check method name: %s", strings.Join(duplicateMethodName, ","))
   193  	}
   194  	return nil
   195  }
   196  
   197  func (b *QueryStructMeta) addMethodFromAddMethodOpt(methods ...interface{}) *QueryStructMeta {
   198  	for _, method := range methods {
   199  		modelMethods, err := parser.GetModelMethod(method, 5)
   200  		if err != nil {
   201  			panic("add diy method err:" + err.Error())
   202  		}
   203  		b.ModelMethods = append(b.ModelMethods, modelMethods.Methods...)
   204  	}
   205  
   206  	err := b.ReviseDIYMethod()
   207  	if err != nil {
   208  		b.db.Logger.Warn(context.Background(), err.Error())
   209  	}
   210  	return b
   211  }
   212  
   213  // IfaceMode object mode
   214  func (b QueryStructMeta) IfaceMode(on bool) *QueryStructMeta {
   215  	b.interfaceMode = on
   216  	return &b
   217  }
   218  
   219  // ReturnObject return object in generated code
   220  func (b *QueryStructMeta) ReturnObject() string {
   221  	if b.interfaceMode {
   222  		return fmt.Sprint("I", b.ModelStructName, "Do")
   223  	}
   224  	return fmt.Sprint("*", b.QueryStructName, "Do")
   225  }
   226  
   227  func isStructType(data reflect.Value) bool {
   228  	return data.Kind() == reflect.Struct ||
   229  		(data.Kind() == reflect.Ptr && data.Elem().Kind() == reflect.Struct)
   230  }
   231  
   232  func pullRelationShip(cache map[string]bool, relationships []*schema.Relationship) []field.Relation {
   233  	if len(relationships) == 0 {
   234  		return nil
   235  	}
   236  	result := make([]field.Relation, len(relationships))
   237  	for i, relationship := range relationships {
   238  		var childRelations []field.Relation
   239  		varType := strings.TrimLeft(relationship.Field.FieldType.String(), "[]*")
   240  		if !cache[varType] {
   241  			cache[varType] = true
   242  			childRelations = pullRelationShip(cache, append(append(append(append(
   243  				make([]*schema.Relationship, 0, 4),
   244  				relationship.FieldSchema.Relationships.BelongsTo...),
   245  				relationship.FieldSchema.Relationships.HasOne...),
   246  				relationship.FieldSchema.Relationships.HasMany...),
   247  				relationship.FieldSchema.Relationships.Many2Many...),
   248  			)
   249  		}
   250  		result[i] = *field.NewRelationWithType(field.RelationshipType(relationship.Type), relationship.Name, varType, childRelations...)
   251  	}
   252  	return result
   253  }