golang项目基于gin+gorm搭建改进版(router+controller+dto+service+model)附:模版链接

这里直接放上gitee项目模版

跳转gitee个人项目模版:gin_tem

项目目录

	gin_tem//项目名
		--config//配置文件
			app_dev.yaml
			app.pro.yaml
		--global//全局变量及初始化函数
			--globalInit.go//全局变量及初始化函数汇总文件
			--globalModel.go//结构体文件
			--initLog.go//日志初始化文件
			--initRedis.go//redis初始化文件
			--initSql.go//数据库初始化文件
			--whiteList.go//路由白名单文件
		--initernal//这里的数据流向 router-->controller-->service--->model(router<--controller<--service<---model)
		//dto负责结构体转换,请求体映射,查询结果结构体映射
			--controller
			--dto
			--model
			--router
			--service
		--log//日志文件夹
			--app.log//请求日志
			--sql.log//数据库日志
		--middlewares//中间件文件夹
			--jwt.go//jwt中间件 token生成,解析方法
			--log.go//日志中间件
		--tmp//忽略,本地使用fresh热更新自动生成
		--utils
			common.go//公共函数
			md5.go//md5加密函数
			validator.go//封装的请求校验函数
	--go.mod
		--go.sum
	--main.go//main文件,放置main函数
			

需要安装的包:

//gin
github.com/gin-gonic/gin

//gorm
gorm.io/gorm

//mysql
gorm.io/driver/mysql

//redis
github.com/go-redis/redis/v8

//viper
github.com/spf13/viper

//zap
go.uber.org/zap

//natefinch/lumberjack.v2
gopkg.in/natefinch/lumberjack.v2

//jwt
github.com/dgrijalva/jwt-go

配置yaml

app_dev.yaml

base: # 应用基本配置
  env: development # 环境名称
  port: 9001 # 服务监听端口号
  app_name: tem # 应用名称
  app_url: 127.0.0.1 # 应用域名
log:
  level: info # 日志等级
  root_dir: ./logs # 日志根目录
  filename: app.log # 日志文件名称
  format: # 写入格式 可选json
  show_line: true # 是否显示调用行
  max_backups: 3 # 旧文件的最大个数
  max_size: 500 # 日志文件最大大小(MB)
  max_age: 2 # 旧文件的最大保留天数
  compress: true # 是否压缩
# database 配置
sql:
  driver: mysql # 数据库驱动
  host: 127.0.0.1 # 域名
  port: 3306 # 端口号
  database: demoproject # 数据库名称 # 根据自己的需求设置
  username: root # 用户名 # 根据自己的需求设置
  password: Duxxx # 密码 # 根据自己的需求设置
  charset: utf8mb4 # 编码格式
  max_idle_conns: 10 # 空闲连接池中连接的最大数量
  max_open_conns: 100 # 打开数据库连接的最大数量
  log_mode: info # 日志级别
  enable_file_log_writer: true # 是否启用日志文件
  log_filename: sql.log # 日志文件名称
# jwt 配置
jwt:
  secret: Duxxxinfo # 根据自己的需求设置
  jwt_t: 43200 # 根据自己的需求设置
  issuer: Duxxx # 根据自己的需求设置
md5:
  key: xxx
#redis
redis:
  addr: 127.0.0.1:6379 # 域名
  password:  # 端口号
  db:

global文件夹配置

globalModel.go 配置需要用到的结构体

package global

// AppConfig 总全局出口结构体
type config struct {
	Sql   Sql   `json:"sql" yaml:"sql"`
	Jwt   Jwt   `json:"jwt" yaml:"jwt"`
	Log   Log   `json:"log" yaml:"log"`
	Md5   Md5   `json:"md5" yaml:"md5"`
	Base  Base  `json:"base" yaml:"base"`
	Redis Redis `json:"redis" yaml:"redis"`
}

// Base 应用基本配置
type Base struct {
	//环境
	Env string `mapstructure:"env" json:"env" yaml:"env"`
	//端口
	Port string `mapstructure:"port" json:"port" yaml:"port"`
	//项目名
	AppName string `mapstructure:"app_name" json:"app_name" yaml:"app_name"`
	//应用域名
	AppUrl string `mapstructure:"app_url" json:"app_url" yaml:"app_url"`
}

// Sql Database 数据库
type Sql struct {
	Driver              string `mapstructure:"driver" json:"driver" yaml:"driver"`
	Host                string `mapstructure:"host" json:"host" yaml:"host"`
	Port                int    `mapstructure:"port" json:"port" yaml:"port"`
	Database            string `mapstructure:"database" json:"database" yaml:"database"`
	UserName            string `mapstructure:"username" json:"username" yaml:"username"`
	Password            string `mapstructure:"password" json:"password" yaml:"password"`
	Charset             string `mapstructure:"charset" json:"charset" yaml:"charset"`
	MaxIdleConns        int    `mapstructure:"max_idle_conns" json:"max_idle_conns" yaml:"max_idle_conns"`
	MaxOpenConns        int    `mapstructure:"max_open_conns" json:"max_open_conns" yaml:"max_open_conns"`
	LogMode             string `mapstructure:"log_mode" json:"log_mode" yaml:"log_mode"`
	EnableFileLogWriter bool   `mapstructure:"enable_file_log_writer" json:"enable_file_log_writer" yaml:"enable_file_log_writer"`
	LogFilename         string `mapstructure:"log_filename" json:"log_filename" yaml:"log_filename"`
}

// Jwt  登录校验配置信息结构体
type Jwt struct {
	Secret string `mapstructure:"secret" json:"secret" yaml:"secret"`
	JwtT   int64  `mapstructure:"jwt_t" json:"jwt_t" yaml:"jwt_t"`    // token 有效期(秒)
	Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` //签发人
}

// Log 日志配置结构体
type Log struct {
	//等级
	Level string `mapstructure:"level" json:"level" yaml:"level"`
	//文件
	RootDir string `mapstructure:"root_dir" json:"root_dir" yaml:"root_dir"`
	//文件名字
	Filename string `mapstructure:"filename" json:"filename" yaml:"filename"`
	//写入格式 可选json
	Format string `mapstructure:"format" json:"format" yaml:"format"`
	//显示调用行
	ShowLine bool `mapstructure:"show_line" json:"show_line" yaml:"show_line"`
	//只保留最近多少个日志文件,用于控制程序总日志的大小
	MaxBackups int `mapstructure:"max_backups" json:"max_backups" yaml:"max_backups"`
	//每个日志文件长度的最大大小,默认100M
	MaxSize int `mapstructure:"max_size" json:"max_size" yaml:"max_size"` // MB
	//日志保留的最大天数
	MaxAge int `mapstructure:"max_age" json:"max_age" yaml:"max_age"` // day
	// 是否压缩日志文件,压缩方法gzip
	Compress bool `mapstructure:"compress" json:"compress" yaml:"compress"`
}

// Md5 key
type Md5 struct {
	Key string `mapstructure:"key" json:"key" yaml:"key"`
}

// Redis 数据库
type Redis struct {
	Addr     string `json:"addr" yaml:"addr"`
	Password string `json:"password" yaml:"password"`
	DB       int    `json:"db" yaml:"db"`
}

globalConfig.go 初始化全局变量

package global

import (
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gorm.io/gorm"
)

//安装viper配置工具 go get -u github.com/spf13/viper
//安装日志zap go get -u go.uber.org/zap

//初始化 global全局配置信息

//初始化全局信息载体

var Config = new(config)//config结构体 已在globalModel.go中声明

// 实例变量
var (
	// zap 日志等级
	level zapcore.Level
	// zap 配置项
	options []zap.Option
	// ZapLog Log 实例
	ZapLog *zap.Logger
	// DB gormDB 实例
	DB          *gorm.DB
	RedisDB     RedisStore//RedisStore在初始化redis中声明的结构体,后面会有
	RedisClient *redis.Client
)

// InitGlobal 初始化全局配置
func InitGlobal() {
	//test git
	v := viper.New()
	v.SetConfigFile("config/app_dev.yaml")
	//读取文件
	err := v.ReadInConfig()
	if err != nil { // 处理读取错误
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}
	// 将配置赋值给全局变量 Config
	if err := v.Unmarshal(&Config); err != nil {
		fmt.Println("Unmarshal fail:", err)
	}
	//
	初始化日志
	InitializeLog()
	//初始化redis
	RedisDB = InitRedis()
	初始化数据库
	DB = InitializeDB()
}

initLog.go 初始化日志配置

package global

import (
	"gin_tem/utils"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"os"
)

// InitializeLog ========================================初始化日志====================================
func InitializeLog() {
	// 创建根目录
	//判断文件是否存在
	if ok := utils.HasPath(Config.Log.RootDir); !ok {
		//不存在就创建
		_ = os.Mkdir(Config.Log.RootDir, 0755)
	}
	//设置日志等级
	setLevel()
	if Config.Log.ShowLine {
		options = append(options, zap.AddCaller())
	}
	// 初始化 zap
	ZapLog = zap.New(getLogCore(), options...)
	//刷新缓冲
	defer ZapLog.Sync()

}

// ========================================================设置log level等级========================================================
func setLevel() {
	switch Config.Log.Level {
	case "debug":
		level = zap.DebugLevel
		options = append(options, zap.AddStacktrace(level))
	case "info":
		level = zap.InfoLevel
	case "warn":
		level = zap.WarnLevel
	case "error":
		level = zap.ErrorLevel
		options = append(options, zap.AddStacktrace(level))
	case "dpanic":
		level = zap.DPanicLevel
	case "panic":
		level = zap.PanicLevel
	case "fatal":
		level = zap.FatalLevel
	default:
		level = zap.InfoLevel
	}

}

// ========================================================获取日志写入器========================================================
func getLogWriter() zapcore.WriteSyncer {
	file := &lumberjack.Logger{
		//日志输出文件路径
		Filename: Config.Log.RootDir + "/" + Config.Log.Filename,
		//日志文件最大 size, 单位是 MB
		MaxSize: Config.Log.MaxSize,
		//最大过期日志保留的个数
		MaxBackups: Config.Log.MaxBackups,
		//保留过期文件的最大时间间隔,单位是天
		MaxAge: Config.Log.MaxAge,
		//是否需要压缩滚动日志, 使用的 gzip 压缩
		Compress: Config.Log.Compress,
	}
	//写入到log文件
	syncFile := zapcore.AddSync(file)
	return zapcore.NewMultiWriteSyncer(syncFile)
	//打印到控制台
	//syncConsole := zapcore.AddSync(os.Stderr)
	//return zapcore.NewMultiWriteSyncer(syncFile, syncConsole)

}
func getLogCore() zapcore.Core {
	var encoder zapcore.Encoder

	// ========================================================调整编码器默认配置 NewProductionEncoderConfig这里创建了zapcore.EncoderConfig 创建了一个编码器
	encoderConfig := zap.NewProductionEncoderConfig()
	=============================================等同上面======================================================================
	//--------------------------------------------------------------------------------------修改时间编码器
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	//--------------------------------------------------------------------------------------在日志文件中使用大写字母记录日志级别
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	// ===============================设置编码器 获取全局中管理log的字段format进行判断 设置==============================================
	if Config.Log.Format == "json" {
		encoder = zapcore.NewJSONEncoder(encoderConfig)
	} else {
		encoder = zapcore.NewConsoleEncoder(encoderConfig)
	}
	// ========================================================zapcore.NewCore()需要三个参数========================================================
	//encode 以什么形式写入 json获取其他
	//getLogWriter() 写入的位置
	//level 等级
	return zapcore.NewCore(encoder, getLogWriter(), level)
}

initRedis.go 初始化redis配置

package global

import (
	"context"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"time"
)

type RedisStore struct {
}

func InitRedis() RedisStore {
	// 创建Redis客户端实例
	RedisClient = redis.NewClient(&redis.Options{
		Addr:     Config.Redis.Addr, // Redis服务器地址和端口
		Password: "",                // 如果有密码,填写密码
		DB:       0,                 // 使用的数据库编号,默认为0
	})
	// 检查连接是否成功
	if err := RedisClient.Ping(context.Background()).Err(); err != nil {
		ZapLog.Info("Failed to connect to Redis: ", zap.Any("error", err.Error()))
	} else {
		ZapLog.Info("redis连接成功:%#v", zap.Any("host", Config.Redis.Addr))
	}
	return RedisStore{}
}

// Set 设置key value
func (RedisStore) Set(key string, value string) error {
	err := RedisClient.Set(context.Background(), key, value, time.Second*10).Err()
	if err != nil {
		return err
	}
	return nil
}

// Get 获取key value
func (RedisStore) Get(key string) (string, error) {
	val, err := RedisClient.Get(context.Background(), key).Result()
	if err != nil {
		return "", err
	}
	return val, nil
}

func (RedisStore) TTL(key string) (time.Duration, error) {
	time, err := RedisClient.TTL(context.Background(), key).Result()
	if err != nil {
		return 0, err
	} else {
		return time, nil
	}
}

initSql.go 初始化数据库

package global

import (
	"fmt"
	"go.uber.org/zap"
	"gopkg.in/natefinch/lumberjack.v2"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"io"
	"log"
	"os"
	"strconv"
	"time"
)

// InitializeDB 初始化数据库
func InitializeDB() *gorm.DB {
	// 根据驱动配置进行初始化
	switch Config.Sql.Driver {
	case "mysql":
		return initMySqlGorm()
	default:
		return initMySqlGorm()
	}
}

// 安装日志切割库 go get -u gopkg.in/natefinch/lumberjack.v2
func getGormLogWriter() logger.Writer {
	var writer io.Writer
	//根据yaml配置的是否启用日志文件
	if Config.Sql.EnableFileLogWriter {
		// 自定义 Writer
		writer = &lumberjack.Logger{
			//日志输出文件路径
			Filename: Config.Log.RootDir + "/" + Config.Sql.LogFilename,
			//日志文件最大 size, 单位是 MB
			MaxSize: Config.Log.MaxSize,
			//最大过期日志保留的个数
			MaxBackups: Config.Log.MaxBackups,
			//保留过期文件的最大时间间隔,单位是天
			MaxAge: Config.Log.MaxAge,
			//是否需要压缩滚动日志, 使用的 gzip 压缩
			Compress: Config.Log.Compress,
		}
	} else {
		// 默认 Writer
		writer = os.Stdout
	}
	return log.New(writer, "\r\n", log.LstdFlags)
}

// getGormLogger gorm日志配置
func getGormLogger() logger.Interface {
	var logMode logger.LogLevel

	switch Config.Sql.LogMode {
	case "silent":
		logMode = logger.Silent
	case "error":
		logMode = logger.Error
	case "warn":
		logMode = logger.Warn
	case "info":
		logMode = logger.Info
	default:
		logMode = logger.Info
	}

	return logger.New(getGormLogWriter(), logger.Config{
		SlowThreshold:             200 * time.Millisecond,          // 慢 SQL 阈值
		LogLevel:                  logMode,                         // 日志级别
		IgnoreRecordNotFoundError: false,                           // 忽略ErrRecordNotFound(记录未找到)错误
		Colorful:                  !Config.Sql.EnableFileLogWriter, // 禁用彩色打印
	})
}

// 初始化 mysql gorm.DB
func initMySqlGorm() *gorm.DB {
	dbConfig := Config.Sql
	//数据库名未设置 直接return
	if dbConfig.Database == "" {
		return nil
	}
	//数据库链接拼接
	dsn := dbConfig.UserName + ":" + dbConfig.Password + "@tcp(" + dbConfig.Host + ":" + strconv.Itoa(dbConfig.Port) + ")/" +
		dbConfig.Database + "?charset=" + dbConfig.Charset + "&parseTime=True&loc=Local"
	mysqlConfig := mysql.Config{
		DSN:                       dsn,   // DSN data source name
		DefaultStringSize:         255,   // string 类型字段的默认长度
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据版本自动配置
	}
	if db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,            // 禁用自动创建外键约束
		Logger:                                   getGormLogger(), // 使用自定义 Logger
	}); err != nil {
		ZapLog.Error("mysql connect failed, err:", zap.Any("err", err))
		return nil
	} else {
		sqlDB, _ := db.DB()
		sqlDB.SetMaxIdleConns(dbConfig.MaxIdleConns)
		sqlDB.SetMaxOpenConns(dbConfig.MaxOpenConns)
		fmt.Println("mysql连接成功")
		ZapLog.Info("mysql连接成功:%#v", zap.Any("host", Config.Sql.Host))
		return db
	}
}

whiteList.go 白名单配置

package global

// WhiteList 白名单
var WhiteList = []string{
	"/user/login",
	"/user/create",
}

middlewares 配置

jwt.go 配置

package middlewares

import (
	"errors"
	"gin_tem/global"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"strings"
	"time"
)

// MyClaims JWT结构体
type MyClaims struct {
	//除了满足下面的Claims,还需要以下用户信息
	//保存用户账号
	//Account string `json:"account"`
	//保存用户id
	ID int `json:"id"`
	//jwt中标准的Claims
	jwt.StandardClaims
}

// 使用指定的 secret 签名声明一个 key ,便于后续获得完整的编码后的字符串token
var key = []byte(global.Config.Jwt.Secret)

func keyFunc(_ *jwt.Token) (i interface{}, err error) {
	return key, nil
}

// GenToken 生成token的方法
func GenToken(id int) (string, error) {
	//创建一个我们自己的声明
	c := MyClaims{
		//account, //自定义字段 账号
		id, //自定义字段 id
		jwt.StandardClaims{
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), //过期时间
			Issuer:    global.Config.Jwt.Issuer,              //签发人
		},
	}

	//使用指定的签名方法创建签名对象
	//这里使用HS256加密算法
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)

	//注意这个地方的 key 一定要是字节切片不能是字符串
	return token.SignedString(key)
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (claims *MyClaims, err error) {
	// 解析token
	var token *jwt.Token
	claims = new(MyClaims)
	token, err = jwt.ParseWithClaims(tokenString, claims, keyFunc)
	if err != nil {
		return nil, err
	}
	// 对token对象中的Claim进行类型断言
	if token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")

}

// JwtParse 中间件
func JwtParse() func(c *gin.Context) {
	return func(c *gin.Context) {
		currentPath := strings.Split(c.Request.URL.Path, "?")[0]
		var flag = true
		//判断是否白名单
		for i := range global.WhiteList {
			if currentPath == global.WhiteList[i] {
				flag = false
				break
			}
			flag = true
		}
		if flag {
			authHeader := c.Request.Header.Get("Authorization")
			if authHeader == "" {
				c.JSON(401, gin.H{
					"code": -1,
					"msg":  "未登录",
				})
				c.Abort()
				return
			}
			// 按空格分割
			parts := strings.SplitN(authHeader, " ", 2)
			if !(len(parts) == 2 && parts[0] == "Bearer") {
				c.JSON(401, gin.H{
					"code": -1,
					"msg":  "访问失败,无效的token,请登录!",
				})
				c.Abort()
				return
			}
			// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
			mc, err := ParseToken(parts[1])
			if err != nil {
				c.JSON(401, gin.H{
					"code": -1,
					"msg":  "访问失败,无效的token,请登录!",
				})
				c.Abort()
				return
			}
			// 将当前请求的userID信息保存到请求的上下文c上
			//c.Set("account", mc.Account)
			c.Set("id", mc.ID)
			c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息
		} else {
			c.Next()
		}

	}
}

logger.go 日志中间件配置

package middlewares

import (
	"gin_tem/global"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"time"
)

// Logger 请求日志中间件
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		query := c.Request.URL.RawQuery
		c.Next()
		useTime := time.Since(start)
		global.ZapLog.Info("Request",
			zap.Int("status", c.Writer.Status()),
			zap.String("method", c.Request.Method),
			zap.String("path", c.Request.URL.Path),
			zap.String("query", query),
			zap.String("ip", c.ClientIP()),
			zap.String("user-agent", c.Request.UserAgent()),
			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
			zap.Duration("useTime", useTime),
		)
	}
}

utils文件夹

common.go配置

package utils

import (
	"gorm.io/gorm"
	"os"
)

// HasPath ===============================================HasPath 判断文件路径是否存在===============================================
func HasPath(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

// PaginateFunc gorm 分页查询
type PaginateFunc[T any] func(db *gorm.DB) *gorm.DB

func Paginate[T any](page, pageSize int) PaginateFunc[T] {
	return func(db *gorm.DB) *gorm.DB {
		if page < 1 {
			page = 1
		}
		offset := (page - 1) * pageSize
		if pageSize > 500 {
			pageSize = 500
		}
		return db.Offset(offset).Limit(pageSize)
	}
}

md5.go

package utils

import (
	"crypto/hmac"
	"crypto/md5"
	"encoding/hex"
)

// Hmac 加密
func Hmac(key, data string) string {
	// 创建对应的md5哈希加密算法
	hash := hmac.New(md5.New, []byte(key))

	hash.Write([]byte(data))

	return hex.EncodeToString(hash.Sum([]byte("")))

}

validator.go

package utils

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
	"reflect"
	"strings"
)

// DefaultValidator validator校验函数
func DefaultValidator(c *gin.Context, params interface{}) error {
	if err := c.ShouldBind(params); err != nil {
		return err
	}
	// ---------------------------------------------------------------中文翻译器
	uni := ut.New(zh.New())
	trans, _ := uni.GetTranslator("zh")

	//=================================================================实例化验证器
	validate := validator.New()
	//==================================================================注册备用字段名字
	validate.RegisterTagNameFunc(func(field reflect.StructField) string {
		return field.Tag.Get("name")
	})
	// ===============================================================注册翻译器到校验器
	err := zh_translations.RegisterDefaultTranslations(validate, trans)
	if err != nil {
		return err
	}
	//===================================================================校验参数
	errs := validate.Struct(params)
	if errs != nil {
		var sliceErrs []string
		for _, err := range errs.(validator.ValidationErrors) {
			sliceErrs = append(sliceErrs, err.Translate(trans))
		}
		return errors.New(strings.Join(sliceErrs, ","))
	}
	return nil

}

internal文件夹

router文件夹创建router.go 路由初始化函数

创建router.go

package router

import (
	"gin_tem/middlewares"
	"github.com/gin-gonic/gin"
)

// R 声明路由对象
var R *gin.Engine

// InitRouters 初始化路由函数
func InitRouters() {
	R = gin.Default()
	//使用请求日志中间件  jwt中间件
	R.Use(middlewares.Logger(), middlewares.JwtParse())
	//初始化用户路由
	//InitUserRouter()//该函数来自router文件夹下面创建用户user.go文件夹

	R.Run(":9001")
}

model下创建common.go

package model

import (
	"gorm.io/gorm"
	"time"
)

type IDModel struct {
	ID        uint           `gorm:"primaryKey" json:"id"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
}

model下创建user_model.go

package model

type User struct {
	IDModel  `gorm:embedded`
	Account  string `json:"account"`
	Password string `json:"password"`
	NickName string `json:"nickName"`
}

// TableName 数据库 表名
func (User) TableName() string {
	return "user"
}

dto文件夹下创建user.go

package dto

import "time"
//CreateUserRequest 用于用户注册 请求体映射校验
type CreateUserRequest struct {
	Account  string `json:"account" form:"account" validate:"required" name:"账号"`
	Password string `json:"password" form:"password" validate:"required" name:"密码"`
	NickName string `json:"nickName" form:"nickName" validate:"required" name:"昵称"`
}
//LoginUserRequest 用于用户登录请求结构体映射校验
type LoginUserRequest struct {
	Account  string `json:"account" form:"account" validate:"required" name:"账号"`
	Password string `json:"password" form:"password" validate:"required" name:"密码"`
}
//LoginUserResponse 用于用户登录相应结构体数据映射
type LoginUserResponse struct {
	Account string `json:"account"`
	//ID      int    `json:"id"`
	Token    string `json:"token"`
	NickName string `json:"nickName"`
}
//ListUserResponse 用于查询用户列表数据结果映射
type ListUserResponse struct {
	ID        int       `json:"id"`
	Account   string    `json:"account"`
	CreatedAt time.Time `json:"created_at"`
}
//ListUserRequest 用于用户分页查询用户列表请求体映射校验
type ListUserRequest struct {
	Page     int `json:"page" form:"page" validate:"required"`
	PageSize int `json:"pageSize" form:"pageSize" validate:"required"`
}

service文件夹下创建user_service.go

package service

import (
	"errors"
	"fmt"
	"gin_tem/global"
	"gin_tem/internal/dto"
	"gin_tem/internal/model"
	"gin_tem/middlewares"
	"gin_tem/utils"
	"gorm.io/gorm"
)

type UserService interface {
	// CreateUser 用户注册
	CreateUser(req *dto.CreateUserRequest) error
	//Login 用户登录
	Login(req *dto.LoginUserRequest) (*dto.LoginUserResponse, error)
	List(req *dto.ListUserRequest) (*[]dto.ListUserResponse, error)
}
type userService struct {
	db *gorm.DB
}

func NewUserService(db *gorm.DB) UserService {
	return &userService{db: db}
}

// CreateUser 注册
func (s *userService) CreateUser(req *dto.CreateUserRequest) error {
	//数据库没有user表时,调用AutoMigrate更具model数据类型 自动创建
	s.db.AutoMigrate(&model.User{})
	hashedPassword := utils.Hmac(global.Config.Md5.Key, req.Password)
	user := &model.User{
		Account:  req.Account,
		Password: hashedPassword,
		NickName: req.NickName,
	}

	tx := s.db.Where("account = ?", req.Account).Find(&model.User{})
	fmt.Printf("%#v\n", tx)
	if tx.RowsAffected == 1 {
		return errors.New("账号已存在")
	}
	result := s.db.Create(user)
	if result.Error != nil {
		return result.Error
	}
	return nil
}

// Login 登录
func (s *userService) Login(req *dto.LoginUserRequest) (*dto.LoginUserResponse, error) {
	hashedPassword := utils.Hmac(global.Config.Md5.Key, req.Password)
	user := &model.User{}
	result := s.db.Where("account = ? AND password = ?", req.Account, hashedPassword).Find(&user)
	fmt.Println(result.Error)
	if result.Error != nil {
		return nil, result.Error
	}
	if result.RowsAffected != 0 {
		if token, err := middlewares.GenToken(int(user.ID)); err != nil {
			return nil, err
		} else {
			return &dto.LoginUserResponse{
				Account: user.Account,
				//ID:      int(user.ID),
				Token:    token,
				NickName: user.NickName,
			}, nil
		}

	} else {
		return nil, errors.New("账号或密码不正确")
	}

}

// List 查询所有用户
func (s *userService) List(req *dto.ListUserRequest) (*[]dto.ListUserResponse, error) {
	var list []dto.ListUserResponse
	result := utils.Paginate[model.User](req.Page, req.PageSize)(s.db.Table("user")).Scan(&list)
	if result.Error != nil {
		return nil, result.Error
	}
	return &list, nil
}

//

controller下创建user.go

package controller

import (
	"gin_tem/internal/dto"
	"gin_tem/internal/service"
	"gin_tem/utils"
	"github.com/gin-gonic/gin"
)

// UserController 用户controller
type UserController struct {
	//这里难理解 就看router文件夹下user.go分组初始化 注释说明
	userService service.UserService
}
//NewUserController NewUserController函数创建user的控制器
func NewUserController(userService service.UserService) *UserController {
	//返回的就是UserController 的引用 接受userService结构体实例 这样&UserController{userService: userService}可以调用控制器的方法 方法中又可以通过控制前对象调用service服务层对象(控制器对象报过了service服务器的对象,使用.调用)
	return &UserController{userService: userService}
}

func (uc *UserController) CreateUser(c *gin.Context) {
	var req dto.CreateUserRequest
	if err := utils.DefaultValidator(c, &req); err != nil {
		c.JSON(200, gin.H{
			"code": 1,
			"msg":  err.Error(),
		})
		return
	}
	err := uc.userService.CreateUser(&req)
	if err != nil {
		c.JSON(200, gin.H{"code": 1, "msg": err.Error()})
		return
	}
	c.JSON(200, gin.H{
		"code": 0,
		"msg":  "注册成功!",
	})
}

func (uc *UserController) Login(c *gin.Context) {
	var req dto.LoginUserRequest
	if err := utils.DefaultValidator(c, &req); err != nil {

	}
	response, err := uc.userService.Login(&req)
	if err != nil {
		c.JSON(200, gin.H{
			"code": 1,
			"msg":  err.Error(),
		})
		return
	}
	c.JSON(200, gin.H{
		"code": 0,
		"data": response,
	})
}

func (uc *UserController) List(c *gin.Context) {
	dtoPageDate := dto.ListUserRequest{}
	err := utils.DefaultValidator(c, &dtoPageDate)
	if err != nil {
		c.JSON(500, gin.H{
			"code": 0,
			"msg":  err.Error(),
		})

	} else {
		list, err := uc.userService.List(&dtoPageDate)
		if err != nil {
			c.JSON(500, gin.H{
				"code": 500,
				"msg":  err.Error(),
			})
			return
		}
		c.JSON(200, gin.H{
			"code": 0,
			"data": list,
		})
	}

}

router文件夹下创建user.go路由组

package router

import (
	"gin_tem/global"
	"gin_tem/internal/controller"
	"gin_tem/internal/service"
)

func InitUserRouter() {
	r := R.Group("/user")
	//1
	//调用controller.NewUserController  ===============》controller.NewUserController

	//2
	//传入service层中的NewUserService方法 ====================》service.NewUserService
	//service层中的NewUserService方法传入db数据库========================》service.NewUserService(global.DB)
	//返回userService层的结构体{db:db} 该结构体实现了UserService接口 service.UserService

	//3 也就是说userService层的结构体 传入了NewUserController函数中=============> controller.NewUserController(service.NewUserService(global.DB))

	//4 调用NewUserController的Login.CreateUser.List函数=============>controller.NewUserController(service.NewUserService(global.DB)).Login

	//5 调用userService层的结构体对应的Login.CreateUser.List函数 ====================>uc.userService.Login

	//uc就是controller层中对应的结构体
	//.userService 获取到包含的userService结构体
	//然后调用userService的Login方法

	userController := controller.NewUserController(service.NewUserService(global.DB))
	{
		r.POST("/login", userController.Login)
		//注册
		r.POST("/create", userController.CreateUser)
		//查询用户列表
		r.GET("list", userController.List)
	}
}

回到router下router.go文件 调用初始化用户路由

package router

import (
	"gin_tem/middlewares"
	"github.com/gin-gonic/gin"
)

// R 声明路由对象
var R *gin.Engine

// InitRouters 初始化路由函数
func InitRouters() {
	R = gin.Default()
	//使用请求日志中间件  jwt中间件
	R.Use(middlewares.Logger(), middlewares.JwtParse())
	//调用初始化用户路由 
	InitUserRouter()

	R.Run(":9001")
}

回到main.go文件

package main

import (
	"gin_tem/global"
	"gin_tem/internal/router"
)

func main() {
	//初始化全局配置
	global.InitGlobal()
	//初始化路由
	router.InitRouters()
}

启动

1.已安装fresh直接控制台fresh启动
2.未安装fresh,直接控制台 go run main.go启动

注意

先安装配置好mysql和redis

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Golang中的Gin框架提供了一种简单而强大的方法来构建Web应用程序。与此同时,Golang标准库中的"net/http"包提供了构建WebSocket服务器和客户端的功能。 首先,我们来看一下如何使用Gin和WebSocket构建WebSocket服务器。首先,需要导入相关的包: ```go import ( "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) ``` 下来,在Gin中创建一个WebSocket处理函数: ```go func WebSocketHandler(c *gin.Context) { upgrader := websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } for { messageType, message, err := conn.ReadMessage() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } err = conn.WriteMessage(messageType, message) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } } } ``` 上面的代码创建了一个基本的WebSocket处理函数。它使用WebSocket标准库中的Upgrader结构来处理升级连并创建一个WebSocket连。 然后,我们需要在Gin中设置路由来处理WebSocket请求: ```go router := gin.Default() router.GET("/ws", WebSocketHandler) ``` 以上代码将在根路径下创建一个WebSocket处理函数。 下来,我们来看一下如何使用GolangGin构建WebSocket客户端。首先,我们需要导入所需的包: ```go import ( "github.com/gorilla/websocket" "net/http" ) ``` 然后,我们可以使用以下代码来创建一个WebSocket客户端: ```go func main() { c, _, err := websocket.DefaultDialer.Dial("ws://localhost:8080/ws", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() done := make(chan struct{}) go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } log.Printf("recv: %s", message) } }() ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-done: return case <-ticker.C: err := c.WriteMessage(websocket.TextMessage, []byte("Hello, Server!")) if err != nil { log.Println("write:", err) return } } } } ``` 上面的代码创建了一个WebSocket客户端,它使用WebSocket标准库中的`DefaultDialer`结构来建立WebSocket连。 以上就是使用Golang Gin和WebSocket构建WebSocket客户端和服务器的简单示例。这些代码可以帮助我们使用GinGolang的标准库来构建强大的Web应用程序,并处理WebSocket通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值