一、业务介绍
- 1、有时候我们希望每次在数据的创建和更新的时候都保留当前操作人信息,最简单暴力的方式就是在每次创建的时候手动插入,在修改的时候手动的修改,这种方式可以实现,但是比较笨和费力
- 2、也可以尝试使用
gorm
的钩子函数来实现这个功能,并且钩子函数要接收上下文参数,将本次接口请求的用户数据插入到对应的字段上
二、使用gorm
自定义全局钩子来实现自动插入创建人、自动修改更新人操作
-
1、创建一个文件实现接收上下文参数来实现自动插入数据,关于
CreatedName
和UpdatedName
自己根据实际数据库字段修改package gormPlus import "gorm.io/gorm" const UserIDKey string = "user_name" func BeforeCreate(db *gorm.DB) { if userName, ok := db.Statement.Context.Value(UserIDKey).(string); ok { // 多加这个判断主要是判断如果表字段有created_name的时候才会给这个字段赋值,没有也不影响 if db.Statement.Schema.LookUpField("CreatedName") != nil { db.Statement.SetColumn("CreatedName", userName) } if db.Statement.Schema.LookUpField("UpdatedName") != nil { db.Statement.SetColumn("UpdatedName", userName) } } } func BeforeUpdate(db *gorm.DB) { if userName, ok := db.Statement.Context.Value(UserIDKey).(string); ok { if db.Statement.Schema.LookUpField("UpdatedName") != nil { db.Statement.SetColumn("UpdatedName", userName) } } } // RegisterAutoHooks 注册全局钩子 func RegisterAutoHooks(db *gorm.DB) { db.Callback().Create().Before("gorm:before_create").Register("audit_hook:before_create", BeforeCreate) db.Callback().Update().Before("gorm:before_update").Register("audit_hook:before_update", BeforeUpdate) }
-
2、全局挂载注册钩子函数
sqlStr := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", "root", "123456", "localhost", "3306", "test2", ) fmt.Println("数据库连接:", sqlStr) db, err := gorm.Open(mysql.Open(sqlStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), DisableForeignKeyConstraintWhenMigrating: true, // 自动创建表的时候不创建外键 NamingStrategy: schema.NamingStrategy{ // 自动创建表时候表名的配置 SingularTable: true, // 全部的表名前面加前缀 //TablePrefix: "mall_", }, }) if err != nil { fmt.Println("打开数据库失败", err) panic("打开数据库失败" + err.Error()) } // 注册创建人、更新人钩子 gormPlus.RegisterAutoHooks(db)
-
3、手动来验证
type User2 struct { ID int64 `gorm:"primaryKey"` Name string `gorm:"size:255"` CreatedName int64 `gorm:"column:created_name"` // 创建人 UpdatedName int64 `gorm:"column:updated_name"` // 更新人 }
ctx := context.WithValue(context.Background(), UserIDKey, "李四") // 插入数据 user := User2{Name: "John Doe"} db.WithContext(ctx).Create(&user) // 自动填充 CreatedName = 李四, UpdatedName = 李四
二、结合gin
项目操作
-
1、在实际项目中操作人主要是从当前请求的
token
解析出操作人 -
2、自定义一个函数来获取上下文的
token
,然后从redis
中读取到数据func GetCtx(ctx *gin.Context) context.Context { token := ctx.GetHeader("token") fmt.Println(token, "当前token") // TODO 根据token获取用户名,这里假设用户名是admin return context.WithValue(context.Background(), gormPlus.UserIDKey, "admin") }
-
3、在接口里面使用当前的上下文操作数据库
package api import ( "github.com/gin-gonic/gin" "gorm.io/gorm" "gorm_plus_gin/model" "gorm_plus_gin/utils" ) type IAccount interface { CreateAccountApi(ctx *gin.Context) // 创建账号 ModifyAccountByIdApi(ctx *gin.Context) // 根据id修改账号 } type Account struct { db *gorm.DB } func (a Account) CreateAccountApi(ctx *gin.Context) { if err := a.db.WithContext(utils.GetCtx(ctx)).Create(&model.AccountEntity{ Username: "test", }).Error; err != nil { utils.Fail(ctx, "创建账号失败") return } utils.Success(ctx, "创建账号成功") return } func (a Account) ModifyAccountByIdApi(ctx *gin.Context) { if err := a.db.WithContext(utils.GetCtx(ctx)).Model(&model.AccountEntity{}).Where("id = ?", 1).Update("Username", "莉莉丝").Error; err != nil { utils.Fail(ctx, "修改账号失败") return } utils.Success(ctx, "修改账号成功") } func NewIAccount(db *gorm.DB) IAccount { return Account{ db: db, } }