change animal Model, wait next change
This commit is contained in:
		
							parent
							
								
									9b24a1f645
								
							
						
					
					
						commit
						89d6ed41e9
					
				
							
								
								
									
										49
									
								
								AutoMigrateMySQL/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								AutoMigrateMySQL/config/config.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Config 包含所有配置部分
 | 
			
		||||
type Config struct {
 | 
			
		||||
	MySQL MySQLConfig `json:"mysql"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MySQLConfig 用于存储 MySQL 数据库的配置
 | 
			
		||||
type MySQLConfig struct {
 | 
			
		||||
	Username string `json:"username"`
 | 
			
		||||
	Password string `json:"password"`
 | 
			
		||||
	Host     string `json:"host"`
 | 
			
		||||
	Database string `json:"database"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadConfig 从文件中加载所有配置信息
 | 
			
		||||
func LoadConfig(filename string) (*Config, error) {
 | 
			
		||||
	file, err := os.Open(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not open config file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	var config Config
 | 
			
		||||
	decoder := json.NewDecoder(file)
 | 
			
		||||
	if err := decoder.Decode(&config); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not decode config file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &config, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	config, err := LoadConfig("config.json")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Error loading config: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 使用 MySQL 配置信息
 | 
			
		||||
	fmt.Printf("Connecting to MySQL database at %s\n", config.MySQL.Host)
 | 
			
		||||
	// 使用 config.MySQL.Username, config.MySQL.Password, config.MySQL.Database 来连接数据库
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										186
									
								
								AutoMigrateMySQL/history/main-v1.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								AutoMigrateMySQL/history/main-v1.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,186 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"log"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/datatypes"
 | 
			
		||||
	"gorm.io/driver/mysql"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 从 AST 中提取结构体字段类型
 | 
			
		||||
func getFieldType(expr ast.Expr) reflect.Type {
 | 
			
		||||
	switch t := expr.(type) {
 | 
			
		||||
	case *ast.Ident:
 | 
			
		||||
		// fmt.Println("t.Name:", t.Name)
 | 
			
		||||
		switch t.Name {
 | 
			
		||||
		case "string":
 | 
			
		||||
			return reflect.TypeOf("")
 | 
			
		||||
		case "int":
 | 
			
		||||
			return reflect.TypeOf(0)
 | 
			
		||||
		case "bool":
 | 
			
		||||
			return reflect.TypeOf(true)
 | 
			
		||||
		case "uint8":
 | 
			
		||||
			return reflect.TypeOf(uint8(0))
 | 
			
		||||
		case "uint16":
 | 
			
		||||
			return reflect.TypeOf(uint16(0))
 | 
			
		||||
		case "uint32":
 | 
			
		||||
			return reflect.TypeOf(uint32(0))
 | 
			
		||||
		case "uint64":
 | 
			
		||||
			return reflect.TypeOf(uint64(0))
 | 
			
		||||
		case "float64":
 | 
			
		||||
			return reflect.TypeOf(float64(0))
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.ArrayType:
 | 
			
		||||
		elemType := getFieldType(t.Elt)
 | 
			
		||||
		if elemType != nil {
 | 
			
		||||
			return reflect.SliceOf(elemType)
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.SelectorExpr: // info time.Time 的特化识别
 | 
			
		||||
		if pkgIdent, ok := t.X.(*ast.Ident); ok {
 | 
			
		||||
			if pkgIdent.Name == "time" && t.Sel.Name == "Time" {
 | 
			
		||||
				return reflect.TypeOf(time.Time{})
 | 
			
		||||
			}
 | 
			
		||||
			if pkgIdent.Name == "datatypes" && t.Sel.Name == "JSON" {
 | 
			
		||||
				return reflect.TypeOf(datatypes.JSON{})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.StarExpr:
 | 
			
		||||
		// Handle pointer to a type
 | 
			
		||||
		return reflect.PtrTo(getFieldType(t.X))
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertToSnakeCase 将大写字符转换为小写并用下划线隔开
 | 
			
		||||
func convertToSnakeCase(name string) string {
 | 
			
		||||
	// 使用正则表达式找到大写字符并在前面加上下划线,然后转换为小写
 | 
			
		||||
	re := regexp.MustCompile("([a-z0-9])([A-Z])")
 | 
			
		||||
	snake := re.ReplaceAllString(name, "${1}_${2}")
 | 
			
		||||
	return strings.ToLower(snake)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	filePath := "./table_defs/table_defs.go" // 指定Go源文件
 | 
			
		||||
	fset := token.NewFileSet()               // 创建文件集,用于记录位置
 | 
			
		||||
 | 
			
		||||
	// 解析文件,得到*ast.File结构
 | 
			
		||||
	f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 用于保存结构体信息的map
 | 
			
		||||
	structs := make(map[string]reflect.Type)
 | 
			
		||||
 | 
			
		||||
	// 遍历文件中的所有声明
 | 
			
		||||
	for _, decl := range f.Decls {
 | 
			
		||||
		// 检查声明是否为类型声明(type T struct {...})
 | 
			
		||||
		genDecl, ok := decl.(*ast.GenDecl)
 | 
			
		||||
		if !ok || genDecl.Tok != token.TYPE {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 遍历类型声明中的所有规格(可能有多个类型在一个声明中,例如:type (A struct{}; B struct{}))
 | 
			
		||||
		for _, spec := range genDecl.Specs {
 | 
			
		||||
			typeSpec, ok := spec.(*ast.TypeSpec)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 检查类型是否为结构体
 | 
			
		||||
			structType, ok := typeSpec.Type.(*ast.StructType)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// info 过滤空表
 | 
			
		||||
			if len(structType.Fields.List) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 构建反射类型
 | 
			
		||||
			fields := make([]reflect.StructField, 0)
 | 
			
		||||
			// fmt.Println(typeSpec.Name.Name, len(structType.Fields.List))
 | 
			
		||||
 | 
			
		||||
			for _, field := range structType.Fields.List {
 | 
			
		||||
				if len(field.Names) == 0 {
 | 
			
		||||
					// 处理嵌入结构体
 | 
			
		||||
					ident, ok := field.Type.(*ast.Ident)
 | 
			
		||||
					if !ok {
 | 
			
		||||
						log.Printf("Unsupported embedded type for field %v\n", field.Type)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					embedType, ok := structs[ident.Name]
 | 
			
		||||
					if !ok {
 | 
			
		||||
						log.Printf("Embedded type %s not found\n", ident.Name)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					// 获取嵌入结构体的所有字段
 | 
			
		||||
					for i := 0; i < embedType.NumField(); i++ {
 | 
			
		||||
						fields = append(fields, embedType.Field(i))
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					for _, fieldName := range field.Names {
 | 
			
		||||
						fieldType := getFieldType(field.Type)
 | 
			
		||||
						if fieldType == nil {
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						// 处理标签
 | 
			
		||||
						tag := ""
 | 
			
		||||
						if field.Tag != nil {
 | 
			
		||||
							tag = field.Tag.Value
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						fields = append(fields, reflect.StructField{
 | 
			
		||||
							Name: fieldName.Name,
 | 
			
		||||
							Type: fieldType,
 | 
			
		||||
							Tag:  reflect.StructTag(tag),
 | 
			
		||||
						})
 | 
			
		||||
						// fmt.Println(fieldName.Name, field.Type, fieldType, tag)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 创建结构体类型
 | 
			
		||||
			structName := typeSpec.Name.Name
 | 
			
		||||
			structReflectType := reflect.StructOf(fields)
 | 
			
		||||
			structs[structName] = structReflectType
 | 
			
		||||
			fmt.Println(fmt.Sprintf("get struct: %s\n", structName))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 初始化数据库
 | 
			
		||||
	dsn := "root:havocantelope412@tcp(127.0.0.1:3306)/pawwander_dev?charset=utf8mb4&parseTime=True&loc=Local"
 | 
			
		||||
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) // 打开 DB 连接
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 通过反射创建结构体实例并迁移数据库
 | 
			
		||||
	for name, typ := range structs {
 | 
			
		||||
		if name == "General" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		instance := reflect.New(typ).Interface()
 | 
			
		||||
 | 
			
		||||
		// 手动设定 表名
 | 
			
		||||
		tableName := convertToSnakeCase(name) // 你可以根据实际情况生成表名
 | 
			
		||||
		db = db.Table(tableName)
 | 
			
		||||
 | 
			
		||||
		fmt.Printf("Created instance of %s: %+v\n", name, instance)
 | 
			
		||||
		if err := db.AutoMigrate(instance); err != nil {
 | 
			
		||||
			log.Fatalf("Failed to migrate %s: %v", name, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										248
									
								
								AutoMigrateMySQL/main-v2.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								AutoMigrateMySQL/main-v2.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,248 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	. "catface/AutoMigrateMySQL/config"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/datatypes"
 | 
			
		||||
	"gorm.io/driver/mysql"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 仿照 AutoMigrate 原本的效果
 | 
			
		||||
func convertToSnakeCase(name string) string {
 | 
			
		||||
	// 使用正则表达式找到大写字符并在前面加上下划线,然后转换为小写
 | 
			
		||||
	re := regexp.MustCompile("([a-z0-9])([A-Z])")
 | 
			
		||||
	snake := re.ReplaceAllString(name, "${1}_${2}")
 | 
			
		||||
	return strings.ToLower(snake)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 从 AST 中提取结构体字段类型
 | 
			
		||||
func getFieldType(expr ast.Expr) reflect.Type {
 | 
			
		||||
	switch t := expr.(type) {
 | 
			
		||||
	case *ast.Ident:
 | 
			
		||||
		// fmt.Println("t.Name:", t.Name)
 | 
			
		||||
		switch t.Name {
 | 
			
		||||
		case "string":
 | 
			
		||||
			return reflect.TypeOf("")
 | 
			
		||||
		case "int":
 | 
			
		||||
			return reflect.TypeOf(0)
 | 
			
		||||
		case "bool":
 | 
			
		||||
			return reflect.TypeOf(true)
 | 
			
		||||
		case "uint8":
 | 
			
		||||
			return reflect.TypeOf(uint8(0))
 | 
			
		||||
		case "uint16":
 | 
			
		||||
			return reflect.TypeOf(uint16(0))
 | 
			
		||||
		case "uint32":
 | 
			
		||||
			return reflect.TypeOf(uint32(0))
 | 
			
		||||
		case "uint64":
 | 
			
		||||
			return reflect.TypeOf(uint64(0))
 | 
			
		||||
		case "float64":
 | 
			
		||||
			return reflect.TypeOf(float64(0))
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.ArrayType:
 | 
			
		||||
		elemType := getFieldType(t.Elt)
 | 
			
		||||
		if elemType != nil {
 | 
			
		||||
			return reflect.SliceOf(elemType)
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.SelectorExpr: // info time.Time 的特化识别
 | 
			
		||||
		if pkgIdent, ok := t.X.(*ast.Ident); ok {
 | 
			
		||||
			if pkgIdent.Name == "time" && t.Sel.Name == "Time" {
 | 
			
		||||
				return reflect.TypeOf(time.Time{})
 | 
			
		||||
			}
 | 
			
		||||
			if pkgIdent.Name == "datatypes" && t.Sel.Name == "JSON" {
 | 
			
		||||
				return reflect.TypeOf(datatypes.JSON{})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.StarExpr: // question 暂时好像不影响。
 | 
			
		||||
		// Handle pointer to a type
 | 
			
		||||
		return reflect.PtrTo(getFieldType(t.X))
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 用于保存结构体信息的map
 | 
			
		||||
var structs = make(map[string]reflect.Type)
 | 
			
		||||
 | 
			
		||||
// 遍历文件中的所有声明
 | 
			
		||||
func getStruct(fDecls []ast.Decl) {
 | 
			
		||||
	for _, decl := range fDecls {
 | 
			
		||||
		// 检查声明是否为类型声明(type T struct {...})
 | 
			
		||||
		genDecl, ok := decl.(*ast.GenDecl)
 | 
			
		||||
		if !ok || genDecl.Tok != token.TYPE {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 遍历类型声明中的所有规格(可能有多个类型在一个声明中,例如:type (A struct{}; B struct{}))
 | 
			
		||||
		for _, spec := range genDecl.Specs {
 | 
			
		||||
			typeSpec, ok := spec.(*ast.TypeSpec)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 检查类型是否为结构体
 | 
			
		||||
			structType, ok := typeSpec.Type.(*ast.StructType)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 过滤空表
 | 
			
		||||
			if len(structType.Fields.List) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 构建反射类型
 | 
			
		||||
			fields := make([]reflect.StructField, 0)
 | 
			
		||||
			// fmt.Println(typeSpec.Name.Name, len(structType.Fields.List))
 | 
			
		||||
 | 
			
		||||
			for _, field := range structType.Fields.List {
 | 
			
		||||
				if len(field.Names) == 0 {
 | 
			
		||||
					// 处理嵌入结构体
 | 
			
		||||
					ident, ok := field.Type.(*ast.Ident)
 | 
			
		||||
					if !ok {
 | 
			
		||||
						log.Printf("Unsupported embedded type for field %v\n", field.Type)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					embedType, ok := structs[ident.Name]
 | 
			
		||||
					if !ok {
 | 
			
		||||
						log.Printf("Embedded type %s not found\n", ident.Name)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					// 获取嵌入结构体的所有字段
 | 
			
		||||
					for i := 0; i < embedType.NumField(); i++ {
 | 
			
		||||
						fields = append(fields, embedType.Field(i))
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					for _, fieldName := range field.Names {
 | 
			
		||||
						fieldType := getFieldType(field.Type)
 | 
			
		||||
						if fieldType == nil {
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						// 处理标签
 | 
			
		||||
						tag := ""
 | 
			
		||||
						if field.Tag != nil {
 | 
			
		||||
							tag = field.Tag.Value
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						fields = append(fields, reflect.StructField{
 | 
			
		||||
							Name: fieldName.Name,
 | 
			
		||||
							Type: fieldType,
 | 
			
		||||
							Tag:  reflect.StructTag(tag),
 | 
			
		||||
						})
 | 
			
		||||
						// fmt.Println(fieldName.Name, field.Type, fieldType, tag)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 创建结构体类型
 | 
			
		||||
			structName := typeSpec.Name.Name
 | 
			
		||||
			structReflectType := reflect.StructOf(fields)
 | 
			
		||||
			structs[structName] = structReflectType
 | 
			
		||||
			fmt.Println("get struct: ", structName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func autoMigrate() {
 | 
			
		||||
	config, err := LoadConfig("config.json")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalln("Error loading config: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	// info 初始化数据库
 | 
			
		||||
	dsn := fmt.Sprintf(
 | 
			
		||||
		"%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
 | 
			
		||||
		config.MySQL.Username,
 | 
			
		||||
		config.MySQL.Password,
 | 
			
		||||
		config.MySQL.Host,
 | 
			
		||||
		config.MySQL.Database,
 | 
			
		||||
	)
 | 
			
		||||
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) // 打开 DB 连接
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 通过反射创建结构体实例并迁移数据库
 | 
			
		||||
	for name, typ := range structs {
 | 
			
		||||
		if name == "General" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		instance := reflect.New(typ).Interface()
 | 
			
		||||
 | 
			
		||||
		// 手动设定表名
 | 
			
		||||
		tableName := convertToSnakeCase(name)
 | 
			
		||||
		db = db.Table(tableName)
 | 
			
		||||
 | 
			
		||||
		fmt.Printf("Created instance of %s: %+v\n", name, instance)
 | 
			
		||||
		if err := db.AutoMigrate(instance); err != nil {
 | 
			
		||||
			log.Fatalf("Failed to migrate %s: %v", name, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	const dirPath = "./table_defs"       // 指定目录路径
 | 
			
		||||
	const rootFileName = "table_defs.go" // info 根结构体所在的文件
 | 
			
		||||
	fset := token.NewFileSet()           // 创建文件集,用于记录位置
 | 
			
		||||
 | 
			
		||||
	// mark stage-1
 | 
			
		||||
	// 列出指定目录下的所有文件和子目录
 | 
			
		||||
	entries, err := os.ReadDir(dirPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 前置根文件
 | 
			
		||||
	var targetIndex int
 | 
			
		||||
	var found bool
 | 
			
		||||
	for i, entry := range entries {
 | 
			
		||||
		if entry.Name() == rootFileName {
 | 
			
		||||
			targetIndex = i
 | 
			
		||||
			found = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if found {
 | 
			
		||||
		targetEntry := entries[targetIndex]
 | 
			
		||||
		entries = append(entries[:targetIndex], entries[targetIndex+1:]...)
 | 
			
		||||
		entries = append([]os.DirEntry{targetEntry}, entries...)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Fatalf("File %s not found in directory %s", rootFileName, dirPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 正常遍历
 | 
			
		||||
	for _, entry := range entries {
 | 
			
		||||
		// 构建完整路径
 | 
			
		||||
		path := filepath.Join(dirPath, entry.Name())
 | 
			
		||||
 | 
			
		||||
		// 检查文件后缀是否为 .go
 | 
			
		||||
		if !entry.IsDir() && filepath.Ext(path) == ".go" {
 | 
			
		||||
			// 解析文件,得到 *ast.File 结构
 | 
			
		||||
			f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Error parsing file %s: %s", path, err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// mark stage-2
 | 
			
		||||
			getStruct(f.Decls)
 | 
			
		||||
			log.Printf("Parsed file: %s", path)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// mark stage-3
 | 
			
		||||
	autoMigrate()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								AutoMigrateMySQL/test/test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								AutoMigrateMySQL/test/test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	// . "pawwander/table_defs"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func convertToSnakeCase(name string) string {
 | 
			
		||||
	// 使用正则表达式找到大写字符并在前面加上下划线,然后转换为小写
 | 
			
		||||
	re := regexp.MustCompile("([a-z0-9])([A-Z])")
 | 
			
		||||
	snake := re.ReplaceAllString(name, "${1}_${2}")
 | 
			
		||||
	return strings.ToLower(snake)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	fmt.Println(convertToSnakeCase("UserActivity"))
 | 
			
		||||
}
 | 
			
		||||
@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"catface/app/utils/response"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Animals struct { // INFO 起到一个标记的作用,这样 web.xxx 的时候不同模块就不会命名冲突了。
 | 
			
		||||
@ -30,11 +31,10 @@ func (a *Animals) List(context *gin.Context) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// v1
 | 
			
		||||
// v0.1
 | 
			
		||||
// func (a *Animals) Detail(context *gin.Context) {
 | 
			
		||||
// 	// 1. Get Params
 | 
			
		||||
// 	anmId, err := strconv.Atoi(context.Param("anm_id"))
 | 
			
		||||
 | 
			
		||||
// 	// 2. Select & Filter
 | 
			
		||||
// 	var animal model.Animal
 | 
			
		||||
// 	err = variable.GormDbMysql.Table("animals").Model(&animal).Where("id = ?", anmId).Scan(&animal).Error // TIP GORM.First 采取默认的
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,11 @@ package model
 | 
			
		||||
 | 
			
		||||
// INFO 一些基础表单的整合
 | 
			
		||||
 | 
			
		||||
type Breed struct {
 | 
			
		||||
type AnmBreed struct {
 | 
			
		||||
	BriefModel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Sterilzation struct { // TEST How to use BriefModel, the dif between Common
 | 
			
		||||
type AnmSterilzation struct { // TEST How to use BriefModel, the dif between Common
 | 
			
		||||
	Id     int64  `json:"id"`
 | 
			
		||||
	NameZh string `json:"name_zh"`
 | 
			
		||||
	NameEn string `json:"name_en"`
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,13 @@
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
// INFO 写在前面:实际上这个模块是 CatFace 子模块在维护,所以对应的 curd 都交给 python 了。
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @description: 保留 Top 3, 辅助 catface - breed 子模型判断; 单独建表,因为只会被 CatFace 模块使用。
 | 
			
		||||
 * @return {*}
 | 
			
		||||
 */
 | 
			
		||||
type AnmFaceBreed struct { // TODO 迁移 python 的时候再考虑一下细节
 | 
			
		||||
	BriefModel
 | 
			
		||||
	BaseModel
 | 
			
		||||
	Top1  uint8
 | 
			
		||||
	Prob1 float64
 | 
			
		||||
	Top2  uint8
 | 
			
		||||
@ -13,6 +15,6 @@ type AnmFaceBreed struct { // TODO 迁移 python 的时候再考虑一下细节
 | 
			
		||||
	Top3  uint8
 | 
			
		||||
	Prob3 float64
 | 
			
		||||
 | 
			
		||||
	AnimalId int64 // INFO 外键设定?
 | 
			
		||||
	AnimalId int64 `gorm:"index;column:animal_id"` // INFO 外键设定?
 | 
			
		||||
	Animal   Animal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								app/model/animal_notice.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/model/animal_notice.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
// TODO Notice 模块,从 Python 迁移
 | 
			
		||||
@ -14,7 +14,7 @@ var DB *gorm.DB // 这种写法是方柏包外使用
 | 
			
		||||
 | 
			
		||||
// 自动迁移表
 | 
			
		||||
func autoMigrateTable() {
 | 
			
		||||
	err := DB.AutoMigrate(&model.Animal{}, &model.Breed{}, &model.Sterilzation{}, &model.AnmStatus{}, &model.AnmGender{})
 | 
			
		||||
	err := DB.AutoMigrate(&model.Animal{}, &model.AnmBreed{}, &model.AnmSterilzation{}, &model.AnmStatus{}, &model.AnmGender{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("autoMigrateTable error:", err)
 | 
			
		||||
	}
 | 
			
		||||
@ -26,7 +26,7 @@ func testInsertSterilzation() {
 | 
			
		||||
	statusesEN := []string{"unknown", "unsterilized", "sterilized"}
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(statusesZH); i++ {
 | 
			
		||||
		sterilzation := model.Sterilzation{
 | 
			
		||||
		sterilzation := model.AnmSterilzation{
 | 
			
		||||
			NameZh: statusesZH[i],
 | 
			
		||||
			NameEn: statusesEN[i],
 | 
			
		||||
		}
 | 
			
		||||
@ -43,7 +43,7 @@ func testInsertBreed() {
 | 
			
		||||
	colorsZH := []string{"不明", "橘白", "奶牛", "白猫", "黑猫", "橘猫", "狸花", "狸白", "简州", "三花", "彩狸"}
 | 
			
		||||
	colorsEN := []string{"unknown", "orange", "cow", "white", "black", "orangeCat", "tabby", "tabbyWhite", "jianzhong", "threeColor", "colorCat"}
 | 
			
		||||
	for i := 0; i < len(colorsZH); i++ {
 | 
			
		||||
		breed := model.Breed{
 | 
			
		||||
		breed := model.AnmBreed{
 | 
			
		||||
			BriefModel: model.BriefModel{
 | 
			
		||||
				NameZh: colorsZH[i],
 | 
			
		||||
				NameEn: colorsEN[i],
 | 
			
		||||
@ -102,11 +102,11 @@ func insertData() {
 | 
			
		||||
	testInsertBreed()
 | 
			
		||||
	fmt.Println("testInsertBreed success.")
 | 
			
		||||
 | 
			
		||||
	testInsertStatus()
 | 
			
		||||
	fmt.Println("testInsertStatus success.")
 | 
			
		||||
	// testInsertStatus()
 | 
			
		||||
	// fmt.Println("testInsertStatus success.")
 | 
			
		||||
 | 
			
		||||
	testInsertAnmGender()
 | 
			
		||||
	fmt.Println("testInsertAnmGender success.")
 | 
			
		||||
	// testInsertAnmGender()
 | 
			
		||||
	// fmt.Println("testInsertAnmGender success.")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,9 @@ import "gorm.io/gorm"
 | 
			
		||||
 * @param {map[string][]uint8} conditions
 | 
			
		||||
 * @return {*}
 | 
			
		||||
 */
 | 
			
		||||
// INFO 特性,源于 MySQL 键值 index from 1,
 | 
			
		||||
// 同时 go 在解析参数之时,对于 Query 为空的情况会得到 [0] 的结果,
 | 
			
		||||
// 所以就可以用这种方式简单的过滤掉。
 | 
			
		||||
func BuildWhere(db *gorm.DB, conditions map[string][]uint8) *gorm.DB {
 | 
			
		||||
	for field, values := range conditions {
 | 
			
		||||
		if len(values) == 0 || len(values) == 1 && values[0] == 0 {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										47
									
								
								test/animal_face_breed_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								test/animal_face_breed_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
package test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"catface/app/model"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestAnmFaceBreed(t *testing.T) {
 | 
			
		||||
	Init()
 | 
			
		||||
 | 
			
		||||
	err := DB.AutoMigrate(&model.AnmFaceBreed{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// // INFO 查询表上的所有索引
 | 
			
		||||
	// var indexes []struct {
 | 
			
		||||
	// 	IndexName  string
 | 
			
		||||
	// 	ColumnName string
 | 
			
		||||
	// }
 | 
			
		||||
	// DB.Raw(`SELECT INDEX_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, "Hav'sCats", "anm_face_breeds").Scan(&indexes)
 | 
			
		||||
	// fmt.Println("All Indexes:", len(indexes)) // QUESTION 输出 0 ?
 | 
			
		||||
	// for _, index := range indexes {
 | 
			
		||||
	// 	fmt.Printf("Index Name: %s, Column Name: %s\n", index.IndexName, index.ColumnName)
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	animalFaceBreed := model.AnmFaceBreed{
 | 
			
		||||
		AnimalId: 1,
 | 
			
		||||
		Top1:     3,
 | 
			
		||||
		Prob1:    0.9,
 | 
			
		||||
		Top2:     4,
 | 
			
		||||
		Prob2:    0.05,
 | 
			
		||||
		Top3:     5,
 | 
			
		||||
		Prob3:    0.05,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// res := DB.Create(&animalFaceBreed)
 | 
			
		||||
	// assert.Nil(t, res.Error)
 | 
			
		||||
 | 
			
		||||
	// 可以进一步检查数据是否正确插入,例如通过查询数据库来验证
 | 
			
		||||
	var temp model.AnmFaceBreed
 | 
			
		||||
	result := DB.First(&temp, 1) //animalFaceBreed.BaseModel.Id)  // ATT 这里用 Id 直接去拿到默认值 0
 | 
			
		||||
	assert.Nil(t, result.Error)
 | 
			
		||||
	assert.Equal(t, animalFaceBreed.Top1, temp.Top1)
 | 
			
		||||
}
 | 
			
		||||
@ -127,7 +127,6 @@ func TestCreateAnimal(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	// 插入数据到数据库
 | 
			
		||||
	result := DB.Create(&animal)
 | 
			
		||||
 | 
			
		||||
	// 检查插入是否成功
 | 
			
		||||
	assert.Nil(t, result.Error)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user