第十节:使用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()
}

 

 

 

 

 

posted @ 2025-09-25 20:09  phpwyl  阅读(9)  评论(0)    收藏  举报