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"
|
"catface/app/utils/response"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Animals struct { // INFO 起到一个标记的作用,这样 web.xxx 的时候不同模块就不会命名冲突了。
|
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) {
|
// func (a *Animals) Detail(context *gin.Context) {
|
||||||
// // 1. Get Params
|
// // 1. Get Params
|
||||||
// anmId, err := strconv.Atoi(context.Param("anm_id"))
|
// anmId, err := strconv.Atoi(context.Param("anm_id"))
|
||||||
|
|
||||||
// // 2. Select & Filter
|
// // 2. Select & Filter
|
||||||
// var animal model.Animal
|
// var animal model.Animal
|
||||||
// err = variable.GormDbMysql.Table("animals").Model(&animal).Where("id = ?", anmId).Scan(&animal).Error // TIP GORM.First 采取默认的
|
// err = variable.GormDbMysql.Table("animals").Model(&animal).Where("id = ?", anmId).Scan(&animal).Error // TIP GORM.First 采取默认的
|
||||||
|
@ -2,11 +2,11 @@ package model
|
|||||||
|
|
||||||
// INFO 一些基础表单的整合
|
// INFO 一些基础表单的整合
|
||||||
|
|
||||||
type Breed struct {
|
type AnmBreed struct {
|
||||||
BriefModel
|
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"`
|
Id int64 `json:"id"`
|
||||||
NameZh string `json:"name_zh"`
|
NameZh string `json:"name_zh"`
|
||||||
NameEn string `json:"name_en"`
|
NameEn string `json:"name_en"`
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
// INFO 写在前面:实际上这个模块是 CatFace 子模块在维护,所以对应的 curd 都交给 python 了。
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 保留 Top 3, 辅助 catface - breed 子模型判断; 单独建表,因为只会被 CatFace 模块使用。
|
* @description: 保留 Top 3, 辅助 catface - breed 子模型判断; 单独建表,因为只会被 CatFace 模块使用。
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
type AnmFaceBreed struct { // TODO 迁移 python 的时候再考虑一下细节
|
type AnmFaceBreed struct { // TODO 迁移 python 的时候再考虑一下细节
|
||||||
BriefModel
|
BaseModel
|
||||||
Top1 uint8
|
Top1 uint8
|
||||||
Prob1 float64
|
Prob1 float64
|
||||||
Top2 uint8
|
Top2 uint8
|
||||||
@ -13,6 +15,6 @@ type AnmFaceBreed struct { // TODO 迁移 python 的时候再考虑一下细节
|
|||||||
Top3 uint8
|
Top3 uint8
|
||||||
Prob3 float64
|
Prob3 float64
|
||||||
|
|
||||||
AnimalId int64 // INFO 外键设定?
|
AnimalId int64 `gorm:"index;column:animal_id"` // INFO 外键设定?
|
||||||
Animal Animal
|
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() {
|
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 {
|
if err != nil {
|
||||||
fmt.Println("autoMigrateTable error:", err)
|
fmt.Println("autoMigrateTable error:", err)
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ func testInsertSterilzation() {
|
|||||||
statusesEN := []string{"unknown", "unsterilized", "sterilized"}
|
statusesEN := []string{"unknown", "unsterilized", "sterilized"}
|
||||||
|
|
||||||
for i := 0; i < len(statusesZH); i++ {
|
for i := 0; i < len(statusesZH); i++ {
|
||||||
sterilzation := model.Sterilzation{
|
sterilzation := model.AnmSterilzation{
|
||||||
NameZh: statusesZH[i],
|
NameZh: statusesZH[i],
|
||||||
NameEn: statusesEN[i],
|
NameEn: statusesEN[i],
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ func testInsertBreed() {
|
|||||||
colorsZH := []string{"不明", "橘白", "奶牛", "白猫", "黑猫", "橘猫", "狸花", "狸白", "简州", "三花", "彩狸"}
|
colorsZH := []string{"不明", "橘白", "奶牛", "白猫", "黑猫", "橘猫", "狸花", "狸白", "简州", "三花", "彩狸"}
|
||||||
colorsEN := []string{"unknown", "orange", "cow", "white", "black", "orangeCat", "tabby", "tabbyWhite", "jianzhong", "threeColor", "colorCat"}
|
colorsEN := []string{"unknown", "orange", "cow", "white", "black", "orangeCat", "tabby", "tabbyWhite", "jianzhong", "threeColor", "colorCat"}
|
||||||
for i := 0; i < len(colorsZH); i++ {
|
for i := 0; i < len(colorsZH); i++ {
|
||||||
breed := model.Breed{
|
breed := model.AnmBreed{
|
||||||
BriefModel: model.BriefModel{
|
BriefModel: model.BriefModel{
|
||||||
NameZh: colorsZH[i],
|
NameZh: colorsZH[i],
|
||||||
NameEn: colorsEN[i],
|
NameEn: colorsEN[i],
|
||||||
@ -102,11 +102,11 @@ func insertData() {
|
|||||||
testInsertBreed()
|
testInsertBreed()
|
||||||
fmt.Println("testInsertBreed success.")
|
fmt.Println("testInsertBreed success.")
|
||||||
|
|
||||||
testInsertStatus()
|
// testInsertStatus()
|
||||||
fmt.Println("testInsertStatus success.")
|
// fmt.Println("testInsertStatus success.")
|
||||||
|
|
||||||
testInsertAnmGender()
|
// testInsertAnmGender()
|
||||||
fmt.Println("testInsertAnmGender success.")
|
// fmt.Println("testInsertAnmGender success.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -8,6 +8,9 @@ import "gorm.io/gorm"
|
|||||||
* @param {map[string][]uint8} conditions
|
* @param {map[string][]uint8} conditions
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
|
// INFO 特性,源于 MySQL 键值 index from 1,
|
||||||
|
// 同时 go 在解析参数之时,对于 Query 为空的情况会得到 [0] 的结果,
|
||||||
|
// 所以就可以用这种方式简单的过滤掉。
|
||||||
func BuildWhere(db *gorm.DB, conditions map[string][]uint8) *gorm.DB {
|
func BuildWhere(db *gorm.DB, conditions map[string][]uint8) *gorm.DB {
|
||||||
for field, values := range conditions {
|
for field, values := range conditions {
|
||||||
if len(values) == 0 || len(values) == 1 && values[0] == 0 {
|
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)
|
result := DB.Create(&animal)
|
||||||
|
|
||||||
// 检查插入是否成功
|
// 检查插入是否成功
|
||||||
assert.Nil(t, result.Error)
|
assert.Nil(t, result.Error)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user