2 Commits 10aea8caf1 ... c3ffd20d2b

Tác giả SHA1 Thông báo Ngày
  runningwater c3ffd20d2b fix: fix automigrate charset for mysql 4 tháng trước cách đây
  runningwater 732356f3c2 feat: 模式模板添加分页功能 4 tháng trước cách đây

+ 4 - 5
app/cmd/make/tpls/migration.tpl

@@ -1,7 +1,6 @@
 package migrations
 
 import (
-	"database/sql"
 	"gorm.io/gorm"
 
 	"github.com/runningwater/gohub/app/models"
@@ -21,12 +20,12 @@ func init() {
 		models.CommonTimestampsField
 	}
 
-	up := func(migrator gorm.Migrator, DB *sql.DB) {
-		migrator.AutoMigrate(&User{})
+	up := func(migrator gorm.Migrator, DB *gorm.DB) {
+		_ = DB.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci").AutoMigrate(&User{})
 	}
 
-	down := func(migrator gorm.Migrator, DB *sql.DB) {
-		migrator.DropTable(&User{})
+	down := func(migrator gorm.Migrator, DB *gorm.DB) {
+		_ = migrator.DropTable(&User{})
 	}
 
 	migrate.Add(up, down, "{{FileName}}")

+ 1 - 1
app/cmd/make/tpls/model/model.tpl

@@ -1,4 +1,4 @@
-//Package {{PackageName}} 模型
+// Package {{PackageName}} 模型
 package {{PackageName}}
 
 import (

+ 18 - 2
app/cmd/make/tpls/model/model_util.tpl

@@ -1,11 +1,15 @@
 package {{PackageName}}
 
 import (
+	"github.com/gin-gonic/gin"
+
+	"github.com/runningwater/gohub/pkg/app"
 	"github.com/runningwater/gohub/pkg/database"
+	"github.com/runningwater/gohub/pkg/paginator"
 )
 
-func Get(idstr string) ({{VariableName}} {{StructName}}) {
-    database.DB.Where("id", idstr).First(&{{VariableName}})
+func Get(idStr string) ({{VariableName}} {{StructName}}) {
+    database.DB.Where("id", idStr).First(&{{VariableName}})
     return
 }
 
@@ -24,3 +28,15 @@ func IsExist(field, value string) bool {
     database.DB.Model({{StructName}}{}).Where("? = ?", field, value).Count(&count)
     return count > 0
 }
+
+// Paginate 分页内容
+func Paginate(c *gin.Context, pageSize int) ({{VariableNamePlural}} []{{StructName}}, paging paginator.Paging) {
+	paging = paginator.Paginate(
+		c,
+		database.DB.Model({{StructName}}{}),
+		&{{VariableNamePlural}},
+		app.V1URL(database.TableName(&{{StructName}}{})),
+		pageSize,
+	)
+	return
+}

+ 19 - 19
app/http/controllers/api/v1/users_controller.go

@@ -1,35 +1,35 @@
 package v1
 
 import (
-    "github.com/runningwater/gohub/app/models/user"
-    "github.com/runningwater/gohub/app/requests"
-    "github.com/runningwater/gohub/pkg/auth"
-    "github.com/runningwater/gohub/pkg/response"
+	"github.com/runningwater/gohub/app/models/user"
+	"github.com/runningwater/gohub/app/requests"
+	"github.com/runningwater/gohub/pkg/auth"
+	"github.com/runningwater/gohub/pkg/response"
 
-    "github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin"
 )
 
 type UsersController struct {
-    BaseApiController
+	BaseApiController
 }
 
 // CurrentUser 当前登录用户信息
 func (ctrl *UsersController) CurrentUser(c *gin.Context) {
-    users := auth.CurrentUser(c)
-    response.Data(c, users)
+	users := auth.CurrentUser(c)
+	response.Data(c, users)
 }
 
 // Index 所有用户
 func (ctrl *UsersController) Index(c *gin.Context) {
-    // data := user.All()
-    // 输入参数校验
-    request := requests.PaginationRequest{}
-    if ok := requests.Validate(c, &request, requests.Pagination); !ok {
-        return
-    }
-    data, pager := user.Paginate(c, 2)
-    response.Data(c, gin.H{
-        "data":  data,
-        "pager": pager,
-    })
+	// data := user.All()
+	// 输入参数校验
+	request := requests.PaginationRequest{}
+	if ok := requests.Validate(c, &request, requests.Pagination); !ok {
+		return
+	}
+	data, pager := user.Paginate(c, 2)
+	response.Data(c, gin.H{
+		"data":  data,
+		"pager": pager,
+	})
 }

+ 11 - 0
app/models/category/category_hooks.go

@@ -0,0 +1,11 @@
+package category
+
+// func (category *Category) BeforeSave(tx *gorm.DB) (err error) {}
+// func (category *Category) BeforeCreate(tx *gorm.DB) (err error) {}
+// func (category *Category) AfterCreate(tx *gorm.DB) (err error) {}
+// func (category *Category) BeforeUpdate(tx *gorm.DB) (err error) {}
+// func (category *Category) AfterUpdate(tx *gorm.DB) (err error) {}
+// func (category *Category) AfterSave(tx *gorm.DB) (err error) {}
+// func (category *Category) BeforeDelete(tx *gorm.DB) (err error) {}
+// func (category *Category) AfterDelete(tx *gorm.DB) (err error) {}
+// func (category *Category) AfterFind(tx *gorm.DB) (err error) {}

+ 30 - 0
app/models/category/category_model.go

@@ -0,0 +1,30 @@
+// Package category 模型
+package category
+
+import (
+	"github.com/runningwater/gohub/app/models"
+	"github.com/runningwater/gohub/pkg/database"
+)
+
+type Category struct {
+	models.BaseModel
+
+	Name        string `json:"name,omitempty"`
+	Description string `json:"description,omitempty"`
+
+	models.CommonTimestampsField
+}
+
+func (category *Category) Create() {
+	database.DB.Create(&category)
+}
+
+func (category *Category) Save() (rowsAffected int64) {
+	result := database.DB.Save(&category)
+	return result.RowsAffected
+}
+
+func (category *Category) Delete() (rowsAffected int64) {
+	result := database.DB.Delete(&category)
+	return result.RowsAffected
+}

+ 42 - 0
app/models/category/category_util.go

@@ -0,0 +1,42 @@
+package category
+
+import (
+	"github.com/gin-gonic/gin"
+
+	"github.com/runningwater/gohub/pkg/app"
+	"github.com/runningwater/gohub/pkg/database"
+	"github.com/runningwater/gohub/pkg/paginator"
+)
+
+func Get(idStr string) (category Category) {
+	database.DB.Where("id", idStr).First(&category)
+	return
+}
+
+func GetBy(field, value string) (category Category) {
+	database.DB.Where("? = ?", field, value).First(&category)
+	return
+}
+
+func All() (categories []Category) {
+	database.DB.Find(&categories)
+	return
+}
+
+func IsExist(field, value string) bool {
+	var count int64
+	database.DB.Model(Category{}).Where("? = ?", field, value).Count(&count)
+	return count > 0
+}
+
+// Paginate 分页内容
+func Paginate(c *gin.Context, pageSize int) (categories []Category, paging paginator.Paging) {
+	paging = paginator.Paginate(
+		c,
+		database.DB.Model(Category{}),
+		&categories,
+		app.V1URL(database.TableName(&Category{})),
+		pageSize,
+	)
+	return
+}

+ 22 - 22
app/requests/pagination_request.go

@@ -5,33 +5,33 @@
 package requests
 
 import (
-    "github.com/gin-gonic/gin"
-    "github.com/thedevsaddam/govalidator"
+	"github.com/gin-gonic/gin"
+	"github.com/thedevsaddam/govalidator"
 )
 
 type PaginationRequest struct {
-    Sort    string `valid:"sort" form:"sort"`
-    Order   string `valid:"order" form:"order"`
-    PerPage string `valid:"per_page" form:"per_page"`
+	Sort    string `valid:"sort" form:"sort"`
+	Order   string `valid:"order" form:"order"`
+	PerPage string `valid:"per_page" form:"per_page"`
 }
 
 func Pagination(data any, c *gin.Context) map[string][]string {
-    rules := govalidator.MapData{
-        "sort":     []string{"in:id,created_at,updated_at"},
-        "order":    []string{"in:asc,desc"},
-        "per_page": []string{"numeric_between:2,100"},
-    }
-    messages := govalidator.MapData{
-        "sort": []string{
-            "in:排序字段仅支持 id,created_at,updated_at",
-        },
-        "order": []string{
-            "in:排序规则仅支持 asc,desc",
-        },
-        "per_page": []string{
-            "numeric_between:每页条数的值介于 2~100 之间",
-        },
-    }
+	rules := govalidator.MapData{
+		"sort":     []string{"in:id,created_at,updated_at"},
+		"order":    []string{"in:asc,desc"},
+		"per_page": []string{"numeric_between:2,100"},
+	}
+	messages := govalidator.MapData{
+		"sort": []string{
+			"in:排序字段仅支持 id,created_at,updated_at",
+		},
+		"order": []string{
+			"in:排序规则仅支持 asc,desc",
+		},
+		"per_page": []string{
+			"numeric_between:每页条数的值介于 2~100 之间",
+		},
+	}
 
-    return validate(data, rules, messages)
+	return validate(data, rules, messages)
 }

+ 2 - 2
bootstrap/database.go

@@ -32,8 +32,8 @@ func SetupDB() {
 		})
 	case "sqlite":
 		// 初始化 SQLite 数据库
-		database := config.GetString("database.sqlite.database")
-		dbConfig = sqlite.Open(database)
+		_database := config.GetString("database.sqlite.database")
+		dbConfig = sqlite.Open(_database)
 	default:
 		panic(errors.New("不支持的数据库连接"))
 	}

+ 3 - 5
database/migrations/2025_06_03_101326_add_users_table.go

@@ -1,8 +1,6 @@
 package migrations
 
 import (
-	"database/sql"
-
 	"gorm.io/gorm"
 
 	"github.com/runningwater/gohub/app/models"
@@ -22,11 +20,11 @@ func init() {
 		models.CommonTimestampsField
 	}
 
-	up := func(migrator gorm.Migrator, DB *sql.DB) {
-		_ = migrator.AutoMigrate(&User{})
+	up := func(migrator gorm.Migrator, DB *gorm.DB) {
+		_ = DB.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci").AutoMigrate(&User{})
 	}
 
-	down := func(migrator gorm.Migrator, DB *sql.DB) {
+	down := func(migrator gorm.Migrator, DB *gorm.DB) {
 		_ = migrator.DropTable(&User{})
 	}
 

+ 32 - 0
database/migrations/2025_07_15_154850_add_categories_table.go

@@ -0,0 +1,32 @@
+package migrations
+
+import (
+	"gorm.io/gorm"
+
+	"github.com/runningwater/gohub/app/models"
+	"github.com/runningwater/gohub/pkg/migrate"
+)
+
+func init() {
+
+	type Category struct {
+		models.BaseModel
+
+		Name        string `gorm:"type:varchar(255);not null;index"`
+		Description string `gorm:"type:varchar(255);index;default:null"`
+
+		models.CommonTimestampsField
+	}
+
+	up := func(migrator gorm.Migrator, DB *gorm.DB) {
+		// 创建表
+		_ = DB.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci").AutoMigrate(&Category{})
+		// _ = migrator.AutoMigrate(&Category{})
+	}
+
+	down := func(migrator gorm.Migrator, DB *gorm.DB) {
+		_ = migrator.DropTable(&Category{})
+	}
+
+	migrate.Add(up, down, "2025_07_15_154850_add_categories_table")
+}

+ 1 - 0
pkg/database/database.go

@@ -27,6 +27,7 @@ func Connect(dbConfig gorm.Dialector, _logger gormlgger.Interface) {
 	}
 	// 获取底层的 sql.DB 实例
 	SQLDB, err = DB.DB()
+
 	if err != nil {
 		fmt.Println("数据库连接失败", err.Error())
 	}

+ 6 - 8
pkg/migrate/migration_file.go

@@ -1,13 +1,11 @@
 package migrate
 
 import (
-	"database/sql"
-
 	"gorm.io/gorm"
 )
 
 // migrationFunc 定义一个函数类型,用于执行数据库迁移操作。
-type migrationFunc func(gorm.Migrator, *sql.DB)
+type migrationFunc func(gorm.Migrator, *gorm.DB)
 
 // migrationFiles 是一个切片,用于存储所有的迁移文件。
 var migrationFiles []MigrationFile
@@ -32,17 +30,17 @@ func Add(up, down migrationFunc, fileName string) {
 
 // getMigrationFile 用于根据迁移文件的名称获取对应的 MigrationFile 结构体。
 func getMigrationFile(name string) MigrationFile {
-	for _, mfile := range migrationFiles {
-		if mfile.FileName == name {
-			return mfile
+	for _, file := range migrationFiles {
+		if file.FileName == name {
+			return file
 		}
 	}
 	return MigrationFile{}
 }
 
-func (mfile MigrationFile) isNotMigrated(migrations []Migration) bool {
+func (file *MigrationFile) isNotMigrated(migrations []Migration) bool {
 	for _, migration := range migrations {
-		if migration.Migration == mfile.FileName {
+		if migration.Migration == file.FileName {
 			return false
 		}
 	}

+ 11 - 11
pkg/migrate/migrator.go

@@ -24,7 +24,7 @@ func (m *Migrator) createMigrationsTable() {
 
 	if !m.Migrator.HasTable(&migration) {
 		// 如果表不存在,则创建表
-		if err := m.Migrator.CreateTable(&migration); err != nil {
+		if err := m.DB.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci").AutoMigrate(&migration); err != nil {
 			console.ExitIf(err)
 			return
 		}
@@ -58,7 +58,7 @@ func (m *Migrator) Up() {
 	batch := m.getBatch()
 
 	// 获取所有迁移数据
-	migrations := []Migration{}
+	var migrations []Migration
 	m.DB.Find(&migrations)
 
 	// 可以通过此值来判断数据库是否已是最新
@@ -84,7 +84,7 @@ func (m *Migrator) Rollback() {
 	// 获取最后一批次的迁移数据
 	lastMigration := Migration{}
 	m.DB.Order("id desc").First(&lastMigration)
-	migrations := []Migration{}
+	var migrations []Migration
 	m.DB.Where("batch = ?", lastMigration.Batch).Order("id desc").Find(&migrations)
 
 	// 回滚迁移操作
@@ -97,7 +97,7 @@ func (m *Migrator) Rollback() {
 func (m *Migrator) Reset() {
 
 	// 获取所有的迁移数据
-	migrations := []Migration{}
+	var migrations []Migration
 	m.DB.Order("id desc").Find(&migrations)
 
 	// 回滚迁移操作
@@ -146,7 +146,7 @@ func (m *Migrator) rollbackMigrations(migrations []Migration) bool {
 		mfile := getMigrationFile(migration.Migration)
 		if mfile.Down != nil {
 			// 执行迁移回退操作
-			mfile.Down(database.DB.Migrator(), database.SQLDB)
+			mfile.Down(database.DB.Migrator(), database.DB)
 		}
 		runed = true
 
@@ -200,17 +200,17 @@ func (m *Migrator) readAllMigrationFiles() []MigrationFile {
 // mfile 是迁移文件的结构体,batch 是批次号,用于区分不同的迁移批次。
 // 此方法会执行迁移操作,同时插入一条记录到数据库。
 // 如果迁移操作成功,会输出一条成功信息;如果迁移操作失败,会输出一条错误信息,并退出程序。
-func (m *Migrator) runUpMigration(mfile MigrationFile, batch int) {
+func (m *Migrator) runUpMigration(file MigrationFile, batch int) {
 
-	if mfile.Up != nil {
-		console.Warning("migrating " + mfile.FileName)
+	if file.Up != nil {
+		console.Warning("migrating " + file.FileName)
 		// 执行迁移操作
-		mfile.Up(database.DB.Migrator(), database.SQLDB)
-		console.Success("migrated " + mfile.FileName)
+		file.Up(database.DB.Migrator(), database.DB)
+		console.Success("migrated " + file.FileName)
 	}
 	// 插入一条记录到数据库
 	err := m.DB.Create(&Migration{
-		Migration: mfile.FileName,
+		Migration: file.FileName,
 		Batch:     batch,
 	}).Error
 	console.ExitIf(err)