github.com/unionj-cloud/go-doudou/v2@v2.3.5/toolkit/gormgen/internal/generate/export.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  	"gorm.io/gorm/utils/tests"
    12  
    13  	"github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
    14  	"github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/helper"
    15  	"github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
    16  	"github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
    17  )
    18  
    19  // GetQueryStructMeta generate db model by table name
    20  func GetQueryStructMeta(db *gorm.DB, conf *model.Config) (*QueryStructMeta, error) {
    21  	if _, ok := db.Config.Dialector.(tests.DummyDialector); ok {
    22  		return nil, fmt.Errorf("UseDB() is necessary to generate model struct [%s] from database table [%s]", conf.ModelName, conf.TableName)
    23  	}
    24  
    25  	conf = conf.Preprocess()
    26  	tableName, structName, fileName := conf.GetNames()
    27  	if tableName == "" {
    28  		return nil, nil
    29  	}
    30  	if err := checkStructName(structName); err != nil {
    31  		return nil, fmt.Errorf("model name %q is invalid: %w", structName, err)
    32  	}
    33  
    34  	columns, err := getTableColumns(db, conf.GetSchemaName(db), tableName, conf.FieldWithIndexTag)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	fields := getFields(db, conf, columns)
    40  	priKeyType := "string"
    41  	for _, field := range fields {
    42  		if field.PriKey {
    43  			priKeyType = field.Type
    44  			break
    45  		}
    46  	}
    47  
    48  	return (&QueryStructMeta{
    49  		db:              db,
    50  		Source:          model.Table,
    51  		Generated:       true,
    52  		FileName:        fileName,
    53  		TableName:       tableName,
    54  		ModelStructName: structName,
    55  		QueryStructName: uncaptialize(structName),
    56  		S:               strings.ToLower(structName[0:1]),
    57  		StructInfo:      parser.Param{Type: structName, Package: conf.ModelPkg},
    58  		ImportPkgPaths:  conf.ImportPkgPaths,
    59  		Fields:          fields,
    60  		PriKeyType:      priKeyType,
    61  	}).addMethodFromAddMethodOpt(conf.GetModelMethods()...), nil
    62  }
    63  
    64  // GetQueryStructMetaFromObject generate base struct from object
    65  func GetQueryStructMetaFromObject(obj helper.Object, conf *model.Config) (*QueryStructMeta, error) {
    66  	err := helper.CheckObject(obj)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	conf = conf.Preprocess()
    72  
    73  	tableName := obj.TableName()
    74  	if conf.TableNameNS != nil {
    75  		tableName = conf.TableNameNS(tableName)
    76  	}
    77  
    78  	structName := obj.StructName()
    79  	if conf.ModelNameNS != nil {
    80  		structName = conf.ModelNameNS(structName)
    81  	}
    82  
    83  	fileName := obj.FileName()
    84  	if fileName == "" {
    85  		fileName = tableName
    86  	}
    87  	if fileName == "" {
    88  		fileName = structName
    89  	}
    90  	if conf.FileNameNS != nil {
    91  		fileName = conf.FileNameNS(fileName)
    92  	} else {
    93  		fileName = schema.NamingStrategy{SingularTable: true}.TableName(fileName)
    94  	}
    95  
    96  	fields := make([]*model.Field, 0, 16)
    97  	for _, fl := range obj.Fields() {
    98  		tag := fl.Tag()
    99  		if tag == nil {
   100  			tag = field.Tag{}
   101  		}
   102  		if gt := fl.GORMTag(); gt != "" {
   103  			tag.Set(field.TagKeyGorm, gt)
   104  		}
   105  		if jt := fl.JSONTag(); jt != "" {
   106  			tag.Set(field.TagKeyJson, jt)
   107  		}
   108  
   109  		fields = append(fields, &model.Field{
   110  			Name:             fl.Name(),
   111  			Type:             fl.Type(),
   112  			ColumnName:       fl.ColumnName(),
   113  			Tag:              tag,
   114  			ColumnComment:    fl.Comment(),
   115  			MultilineComment: strings.Contains(fl.Comment(), "\n"),
   116  		})
   117  	}
   118  
   119  	return &QueryStructMeta{
   120  		Source:          model.Object,
   121  		Generated:       true,
   122  		FileName:        fileName,
   123  		TableName:       tableName,
   124  		ModelStructName: structName,
   125  		QueryStructName: uncaptialize(structName),
   126  		S:               strings.ToLower(structName[0:1]),
   127  		StructInfo:      parser.Param{Type: structName, Package: conf.ModelPkg},
   128  		ImportPkgPaths:  append(conf.ImportPkgPaths, obj.ImportPkgPaths()...),
   129  		Fields:          fields,
   130  	}, nil
   131  }
   132  
   133  // ConvertStructs convert to base structures
   134  func ConvertStructs(db *gorm.DB, structs ...interface{}) (metas []*QueryStructMeta, err error) {
   135  	for _, st := range structs {
   136  		if isNil(st) {
   137  			continue
   138  		}
   139  		if base, ok := st.(*QueryStructMeta); ok {
   140  			metas = append(metas, base)
   141  			continue
   142  		}
   143  		if !isStructType(reflect.ValueOf(st)) {
   144  			return nil, fmt.Errorf("%s is not a struct", reflect.TypeOf(st).String())
   145  		}
   146  
   147  		structType := reflect.TypeOf(st)
   148  		name := getStructName(structType.String())
   149  		newStructName := name
   150  		if st, ok := st.(interface{ GenInternalDoName() string }); ok {
   151  			newStructName = st.GenInternalDoName()
   152  		}
   153  
   154  		meta := &QueryStructMeta{
   155  			S:               getPureName(name),
   156  			ModelStructName: name,
   157  			QueryStructName: uncaptialize(newStructName),
   158  			StructInfo:      parser.Param{PkgPath: structType.PkgPath(), Type: name, Package: getPackageName(structType.String())},
   159  			Source:          model.Struct,
   160  			db:              db,
   161  		}
   162  		if err := meta.parseStruct(st); err != nil {
   163  			return nil, fmt.Errorf("transform struct [%s.%s] error:%s", meta.StructInfo.Package, name, err)
   164  		}
   165  		if err := meta.check(); err != nil {
   166  			db.Logger.Warn(context.Background(), err.Error())
   167  			continue
   168  		}
   169  
   170  		metas = append(metas, meta)
   171  	}
   172  	return
   173  }
   174  
   175  func isNil(i interface{}) bool {
   176  	if i == nil {
   177  		return true
   178  	}
   179  
   180  	// if v is not ptr, return false(i is not nil)
   181  	// if v is ptr, return v.IsNil()
   182  	v := reflect.ValueOf(i)
   183  	return v.Kind() == reflect.Ptr && v.IsNil()
   184  }
   185  
   186  // BuildDIYMethod check the legitimacy of interfaces
   187  func BuildDIYMethod(f *parser.InterfaceSet, s *QueryStructMeta, data []*InterfaceMethod) (checkResults []*InterfaceMethod, err error) {
   188  	for _, interfaceInfo := range f.Interfaces {
   189  		if interfaceInfo.MatchStruct(s.ModelStructName) {
   190  			for _, method := range interfaceInfo.Methods {
   191  				t := &InterfaceMethod{
   192  					S:             s.S,
   193  					TargetStruct:  s.QueryStructName,
   194  					OriginStruct:  s.StructInfo,
   195  					MethodName:    method.MethodName,
   196  					Params:        method.Params,
   197  					Doc:           method.Doc,
   198  					Table:         s.TableName,
   199  					InterfaceName: interfaceInfo.Name,
   200  					Package:       getPackageName(interfaceInfo.Package),
   201  				}
   202  				if err = t.checkMethod(data, s); err != nil {
   203  					return nil, err
   204  				}
   205  				if err = t.checkParams(method.Params); err != nil {
   206  					return
   207  				}
   208  				if err = t.checkResult(method.Result); err != nil {
   209  					return
   210  				}
   211  				if err = t.checkSQL(); err != nil {
   212  					return
   213  				}
   214  				_, err = t.Section.BuildSQL()
   215  				if err != nil {
   216  					err = fmt.Errorf("sql [%s] build err:%w", t.SQLString, err)
   217  					return
   218  				}
   219  				checkResults = append(checkResults, t)
   220  			}
   221  		}
   222  	}
   223  	return
   224  }
   225  
   226  // ParseStructRelationShip parse struct's relationship
   227  // No one should use it directly in project
   228  func ParseStructRelationShip(relationship *schema.Relationships) []field.Relation {
   229  	cache := make(map[string]bool)
   230  	return append(append(append(append(
   231  		make([]field.Relation, 0, 4),
   232  		pullRelationShip(cache, relationship.HasOne)...),
   233  		pullRelationShip(cache, relationship.HasMany)...),
   234  		pullRelationShip(cache, relationship.BelongsTo)...),
   235  		pullRelationShip(cache, relationship.Many2Many)...,
   236  	)
   237  }
   238  
   239  // GetStructNames get struct names from base structs
   240  func GetStructNames(bases []*QueryStructMeta) (names []string) {
   241  	for _, base := range bases {
   242  		names = append(names, base.ModelStructName)
   243  	}
   244  	return names
   245  }