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 }