第十节:使用goctl-gin 搭建gin项目用户注册鉴权(上)
首先下载工具
git clone https://gitee.com/haixb/goctl-gin.git
cd goctl-gin
make mac //我的是mac
mv goctl-darwin /usr/local/bin/goctl-gin
#测试
goctl-gin

目录结构
doc #api文件 - api.api - base.api - user.api pkg #工具 - encrypt #加密解密 - encrypt.go - token #token签名鉴权相关 - ctxdata.go - parse.go - parse_test.go shell.sh 命令脚本
base.api
info ( title:"后台系统admin" author:"phpwyl" ) type ( IdPathReq{ Id string `uri:"id,omitempty"` } )
api.api
syntax = "v1" import "base.api" import "user.api" info( title:"后台系统admin" author:"phpwyl" servuceMod:1 )
user.api
syntax = "v1" info ( title: "后台系统admin" author: "gitee.com/dn-jinmin" ) type ( User { Id string `json:"id,omitempty"` Password string `json:"password,omitempty"` Name string `json:"name,omitempty"` Status int `json:"status,omitempty"` } loginReq { Name string `json:"name,omitempty"` Password string `json:"password,omitempty"` } loginResp { Status int `json:"status,omitempty"` Id string `json:"id,omitempty"` Name string `json:"name,omitempty"` AccessToken string `json:"token,omitempty"` AccessExpire int64 `json:"accessExpire,omitempty"` RefreshAfter int64 `json:"refreshAfter,omitempty"` } userListReq { Ids []string `json:"ids,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` Count int `json:"count,omitempty"` } userListResp { Count int64 `json:"count"` List []*User `json:"data"` } upPasswordReq { Id string `json:"id"` OldPwd string `json:"oldPwd"` NewPwd string `json:"newPwd"` } ) @server( group: v1/user logic: User ) service User { @server ( handler: Login logic: User.Login ) post /login(loginReq) returns (loginResp) } @server( group: /v1/user logic: User middleware: Jwt ) service user { @server ( handler: Info logic: User.Info ) get /:id(IdPathReq) returns(User) @server ( handler: Create logic: User.Create ) post / (User) @server ( handler: Edit logic: User.Edit ) put / (User) @server ( handler: Delete logic: User.Delete ) delete /:id(IdPathReq) @server ( handler: List logic: User.List ) get /list(userListReq) returns (userListResp) @server ( handler: UpPassword logic: User.UpPassword ) post /password(upPasswordReq) }
生成代码文件
goctl-gin api go -api ./doc/api.api -dir ./ -style go-zero
生成后的目录结构
aiwork - doc - api.api - user.api - etc - internal - pkg - main.go
数据模型 :
在goctl-gin中目前只针对mongo提供了相应的生成代码的命令
goctl-gin model mongo --type user --dir ./internal/model/
通过这个命令生成的usermodel中已经提供了最基本的curd的方法,我们只需要完善它的结构即可
aiwork/internal/model/userTypes.go
package model
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
// TODO: Fill your own fields
Name string `bson:"name,omitempty" json:"name,omitempty"`
Password string `bson:"password,omitempty" json:"password,omitempty"`
Status int `bson:"status,omitempty" json:"status,omitempty"`
IsSystem int `bson:"isSystem,omitempty" json:"isSystem,omitempty"`
UpdateAt int64 `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt int64 `bson:"createAt,omitempty" json:"createAt,omitempty"`
}
我们在 usermodel 中添加通过名称查询数据的方法
// Code generated by goctl. DO NOT EDIT. package model import ( "context" "time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" ) type UserModel interface { Insert(ctx context.Context, data *User) error FindOne(ctx context.Context, id string) (*User, error) Update(ctx context.Context, data *User) error Delete(ctx context.Context, id string) error FindByName(ctx context.Context, name string)(*User, error) //新增加的方法 } type defaultUserModel struct { col *mongo.Collection } func NewUserModel(db *mongo.Database) UserModel { col := db.Collection("user") return &defaultUserModel{ col: col, } } func (m *defaultUserModel) Insert(ctx context.Context, data *User) error { if data.ID.IsZero() { data.ID = primitive.NewObjectID() data.CreateAt = time.Now().Unix() data.UpdateAt = time.Now().Unix() } _, err := m.col.InsertOne(ctx, data) return err } func (m *defaultUserModel) FindOne(ctx context.Context, id string) (*User, error) { oid, err := primitive.ObjectIDFromHex(id) if err != nil { return nil, ErrInvalidObjectId } var data User err = m.col.FindOne(ctx, bson.M{"_id": oid}).Decode(&data) switch err { case nil: return &data, nil case mongo.ErrNoDocuments: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) FindByName(ctx context.Context, name string) (*User, error) { //实现该方法 var data User err := m.col.FindOne(ctx, bson.M{"name": name}).Decode(&data) switch err { case nil: return &data, nil case mongo.ErrNoDocuments: return nil, ErrNotUser default: return nil, err } } func (m *defaultUserModel) Update(ctx context.Context, data *User) error { data.UpdateAt = time.Now().Unix() _, err := m.col.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) return err } func (m *defaultUserModel) Delete(ctx context.Context, id string) error { oid, err := primitive.ObjectIDFromHex(id) if err != nil { return ErrInvalidObjectId } _, err = m.col.DeleteOne(ctx, bson.M{"_id": oid}) return err }
设置Mongo数据库链接配置
在aiwork/pkg 我们增加扩展配置工具 mongox
aiwork/pkg/mongox/mongox.go
package mongox import ( "context" "fmt" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) type MongodbConfig struct { User string //用户名 Password string //密码 Hosts []string //host地址 Port int //端口 Database string //数据库名 Params string //其他相关认证 MaxPoolSize uint64 //连接池最大数 } func MongodbDatabase(config *MongodbConfig) (*mongo.Database, error) { client, err := mongo.Connect(context.TODO(), config.GetApplyURI()...) if err != nil { return nil, err } err = client.Ping(context.TODO(), nil) if err != nil { return nil, err } return client.Database(config.Database), nil } func (t *MongodbConfig) GetApplyURI() []*options.ClientOptions { var ops []*options.ClientOptions var uri string uri = "mongodb://" if len(t.User) > 0 && len(t.Password) > 0 { uri = fmt.Sprintf("%v%v:%v@", uri, t.User, t.Password) } for index, v := range t.Hosts { var host string if t.Port != 0 { host += v + fmt.Sprintf(":%d", t.Port) } else { host = v } if index < len(t.Hosts)-1 { host += "," } uri += host } uri += fmt.Sprintf("/%s", t.Database) if len(t.Params) > 0 { uri = fmt.Sprintf("%v?%v", uri, t.Params) } ops = append(ops, options.Client().ApplyURI(uri)) if t.MaxPoolSize > 0 { ops = append(ops, options.Client().SetMaxPoolSize(t.MaxPoolSize)) } return ops }
这个 mongox 包提供了一个简单的 MongoDB 连接工具,主要功能包括:
定义 MongoDB 配置结构体
建立 MongoDB 连接并返回数据库实例
构造 MongoDB 连接 URI
在aiwork/etc/dev/api.yaml 中进行配置
Mongo: User: "" Password: "" Hosts: ["127.0.0.1"] Port : 27017 Database: "test"
在aiwork/internal/config/config.go 中进行配置
Mongo struct { User string //用户名 Password string //密码 Hosts []string //host地址 Port int //端口 Database string //数据库名 Params string //其他参数 //MaxPoolSize uint64 // 连接池大小 }
在aiwork/internal/svc/servicecontext.go 中进行创建链接实例
ServiceContext 是应用程序的核心服务容器,负责:
1.存储应用配置
2.管理数据库连接
3.提供统一的服务访问入口
type ServiceContext struct { Config config.Config // todo repo and pkg object instance Mongo *mongo.Database } func NewServiceContext(c config.Config) (*ServiceContext, error) { mongoDb, err := mongox.MongodbDatabase( &mongox.MongodbConfig{ User: c.Mongo.User, Password: c.Mongo.Password, Hosts: c.Mongo.Hosts, Port: c.Mongo.Port, Database: c.Mongo.Database, }, ) if err != nil { return nil, err } return &ServiceContext{ Mongo: mongoDb, Config: c, }, nil }
我们需要在上下文中判断是否有超级用户没有则添加用户
package svc import ( "aiwork/internal/config" "aiwork/internal/middleware" "aiwork/internal/model" "aiwork/pkg/mongox" "context" "go.mongodb.org/mongo-driver/mongo" ) type ServiceContext struct { Config config.Config // todo repo and pkg object instance Mongo *mongo.Database *middleware.Jwt model.UserModel // 定义用户模型 } func NewServiceContext(c config.Config) (*ServiceContext, error) { mongoDb, err := mongox.MongodbDatabase( &mongox.MongodbConfig{ User: c.Mongo.User, Password: c.Mongo.Password, Hosts: c.Mongo.Hosts, Port: c.Mongo.Port, Database: c.Mongo.Database, }, ) if err != nil { return nil, err } svc := &ServiceContext{ Jwt: middleware.NewJwt(c.Jwt.Secret), Mongo: mongoDb, UserModel: model.NewUserModel(mongoDb), Config: c, } return svc, initUser(svc) } func initUser(svc *ServiceContext) error { ctx := context.Background() _, err := svc.UserModel.FindSystemUser(ctx) if err != nil && err != model.ErrNotUser { return err } if err == nil { //查询到已经存在了 return nil } //需要创建root用户 return svc.UserModel.Insert(ctx, &model.User{ Name: "root", Password: "$2a$10$ddIvqt7U6zNA9poys.FNCuEZTJY6V.axWy4P7A44TuT9KBegGZlD6", Status: 0, IsSystem: true, }) }
jwt中间件设置aiwork/internal/middleware/jwt.go
package middleware import ( "aiwork/pkg/httpx" "aiwork/pkg/token" "github.com/gin-gonic/gin" ) type Jwt struct { tokenParser *token.Parse //令牌解析器 } func NewJwt(secret string) *Jwt { return &Jwt{ tokenParser: token.NewTokenParse(secret), } } func (m *Jwt) Handler(ctx *gin.Context) { r, err := m.tokenParser.ParseWithContext(ctx.Request) if err != nil { httpx.FailWithErr(ctx, err) ctx.Abort() return } ctx.Request = r ctx.Next() }

浙公网安备 33010602011771号