GO学习记录 —— 创建一个GO项目

news2025/4/13 12:37:49

文章目录

  • 前言
  • 一、项目介绍
  • 二、目录介绍
  • 三、创建过程
    • 1.引入Gin框架、创建main
    • 2.加载配置文件
    • 3.连接MySQL、redis
    • 4.创建结构体
    • 5.错误处理、返回响应处理


前言

代码地址

下载地址:https://github.com/Lee-ZiMu/Golang-Init.git

一、项目介绍

1、使用Gin框架来创建项目
2、使用viper读取配置文件
3、使用gorm连接MySQL、生成结构体
4、使用redigo连接redis
5、包含异常处理、jwt、日志、ws的初始化或使用

二、目录介绍

其中业务代码部分可以拆分为多个业务模块

在这里插入图片描述

其中业务代码部分:

在这里插入图片描述

公共组件部分

在这里插入图片描述

三、创建过程

1.引入Gin框架、创建main

Gin文档地址:https://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/

package main

import (
	"github.com/gin-gonic/gin"
	viperInit "github.com/spf13/viper"
	"hy_heymate/api/route"
	"hy_heymate/common/cron"
	"hy_heymate/common/logger"
	"hy_heymate/config/viper"
	"hy_heymate/database"
)

func main() {
	r := gin.Default()

	// 读取配置文件
	viper.Init("../config/config.yaml")

	// 初始化日志
	logger.Init()

	// 连接mysql数据库
	database.ConnectToMySQL()

	// 连接redis
	database.ConnectToRedis()

	// 加载路由
	route.LoadRouters(r)

	// 定时
	cron.Cron()

	r.Run(":" + viperInit.GetString("server.port"))

}

2.加载配置文件

package viper

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

func Init(configPath string) {
	//制定配置文件的路径
	viper.SetConfigFile(configPath)
	// 读取配置信息
	err := viper.ReadInConfig()
	if err != nil {
		// 读取配置信息失败
		panic(fmt.Errorf("配置文件读取失败,错误信息:%v", err))
	}
	//监听修改
	viper.WatchConfig()
	//为配置修改增加一个回调函数
	viper.OnConfigChange(func(in fsnotify.Event) {
		fmt.Println("配置文件修改了...")
	})
}

3.连接MySQL、redis

连接mysql

package database

import (
	"fmt"
	viperInit "github.com/spf13/viper"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"hy_heymate/common/file"
	"hy_heymate/gen/query"
	"log"
	"os"
	"time"
)

var db = new(gorm.DB)

func ConnectToMySQL() {
	// 创建日志文件夹
	if err := file.MkdirAll(viperInit.GetString("mysql.logpath")); err != nil {
		panic(err)
	}

	logFile, err := os.Create(viperInit.GetString("mysql.logpath") + "db.log")
	if err != nil {
		panic(err)
	}

	sqlStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?%s",
		viperInit.GetString("mysql.username"),
		viperInit.GetString("mysql.password"),
		viperInit.GetString("mysql.host"),
		viperInit.GetInt("mysql.port"),
		viperInit.GetString("mysql.dbname"),
		viperInit.GetString("mysql.conf"),
	)
	db, err = gorm.Open(mysql.Open(sqlStr), &gorm.Config{
		Logger: logger.New(
			log.New(logFile, "\n", log.LstdFlags),
			logger.Config{
				SlowThreshold: time.Second,
				Colorful:      true,
				LogLevel:      logger.Info,
			}),
		NamingStrategy: schema.NamingStrategy{SingularTable: true},
	})
	if err != nil {
		panic(fmt.Errorf("mysql连接失败,错误信息:%s", err))
	}

	sqlDB, _ := db.DB()
	sqlDB.SetMaxIdleConns(viperInit.GetInt("mysql.maxIdleConns"))                                      // 最大连接数
	sqlDB.SetMaxOpenConns(viperInit.GetInt("mysql.maxOpenConns"))                                      // 最大打开连接数
	sqlDB.SetConnMaxLifetime(time.Duration(viperInit.GetInt64("mysql.vonnMaxLifetime")) * time.Second) // 设置可以重用连接的最大时间量

	query.SetDefault(db)

}

func Get() *gorm.DB {
	return db
}

连接redis

package database

import (
	"fmt"
	"github.com/gomodule/redigo/redis"
	viperInit "github.com/spf13/viper"
	"time"
)

var (
	RedisClient *redis.Pool
)

func ConnectToRedis() {
	host := viperInit.GetString("redis.host")
	port := viperInit.GetString("redis.port")
	//username := viperInit.GetString("redis.username")
	//password := viperInit.GetString("redis.password")
	MaxIdle := viperInit.GetInt("redis.MaxIdle")
	MaxActive := viperInit.GetInt("redis.MaxActive")
	IdleTimeout := viperInit.GetInt("redis.IdleTimeout")

	pool := &redis.Pool{
		MaxIdle:     MaxIdle,                                  // 最大空闲连接数
		MaxActive:   MaxActive,                                // 最大活跃连接数
		IdleTimeout: time.Duration(IdleTimeout) * time.Second, // 空闲连接超时时间
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", host+":"+port)
			if err != nil {
				panic(fmt.Errorf("redis连接失败,错误信息:%s", err))
			}
			return c, err
		},
	}
	RedisClient = pool
}

4.创建结构体


package main

import (
	"fmt"
	viperInit "github.com/spf13/viper"
	"gorm.io/driver/mysql"
	"gorm.io/gen"
	"gorm.io/gorm"
	"hy_heymate/config/viper"
)

func main() {
	// 读取配置文件
	viper.Init("../config/config.yaml")

	// 连接mysql数据库
	sqlStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?%s",
		viperInit.GetString("mysql.username"),
		viperInit.GetString("mysql.password"),
		viperInit.GetString("mysql.host"),
		viperInit.GetInt("mysql.port"),
		viperInit.GetString("mysql.dbname"),
		viperInit.GetString("mysql.conf"),
	)
	var err error
	db, err := gorm.Open(mysql.Open(sqlStr), &gorm.Config{})
	if err != nil {
		panic(fmt.Errorf("mysql连接失败,错误信息:%s", err))
	}

	// 生成实例
	g := gen.NewGenerator(gen.Config{
		// 相对执行`go run`时的路径, 会自动创建目录
		OutPath: "./query",

		// WithDefaultQuery 生成默认查询结构体(作为全局变量使用), 即`Q`结构体和其字段(各表模型)
		// WithoutContext 生成没有context调用限制的代码供查询
		// WithQueryInterface 生成interface形式的查询代码(可导出), 如`Where()`方法返回的就是一个可导出的接口类型
		Mode: gen.WithDefaultQuery | gen.WithQueryInterface,

		// 表字段可为 null 值时, 对应结体字段使用指针类型
		FieldNullable: true, // generate pointer when field is nullable

		// 表字段默认值与模型结构体字段零值不一致的字段, 在插入数据时需要赋值该字段值为零值的, 结构体字段须是指针类型才能成功, 即`FieldCoverable:true`配置下生成的结构体字段.
		// 因为在插入时遇到字段为零值的会被GORM赋予默认值. 如字段`age`表默认值为10, 即使你显式设置为0最后也会被GORM设为10提交.
		// 如果该字段没有上面提到的插入时赋零值的特殊需要, 则字段为非指针类型使用起来会比较方便.
		FieldCoverable: false, // generate pointer when field has default value, to fix problem zero value cannot be assign: https://gorm.io/docs/create.html#Default-Values

		// 模型结构体字段的数字类型的符号表示是否与表字段的一致, `false`指示都用有符号类型
		FieldSignable: false, // detect integer field's unsigned type, adjust generated data type
		// 生成 gorm 标签的字段索引属性
		FieldWithIndexTag: false, // generate with gorm index tag
		// 生成 gorm 标签的字段类型属性
		FieldWithTypeTag: true, // generate with gorm column type tag
	})
	// 设置目标 db
	g.UseDB(db)

	// 自定义字段的数据类型
	// 统一数字类型为int64,兼容protobuf
	dataMap := map[string]func(detailType gorm.ColumnType) (dataType string){
		"tinyint":   func(detailType gorm.ColumnType) (dataType string) { return "int64" },
		"smallint":  func(detailType gorm.ColumnType) (dataType string) { return "int64" },
		"mediumint": func(detailType gorm.ColumnType) (dataType string) { return "int64" },
		"bigint":    func(detailType gorm.ColumnType) (dataType string) { return "int64" },
		"int":       func(detailType gorm.ColumnType) (dataType string) { return "int64" },
	}
	// 要先于`ApplyBasic`执行
	g.WithDataTypeMap(dataMap)

	// 自定义模型结体字段的标签
	// 将特定字段名的 json 标签加上`string`属性,即 MarshalJSON 时该字段由数字类型转成字符串类型
	//jsonField := gen.FieldJSONTagWithNS(func(columnName string) (tagContent string) {
	//	toStringField := `balance, `
	//	if strings.Contains(toStringField, columnName) {
	//		return columnName + ",string"
	//	}
	//	return columnName
	//})
	// 将非默认字段名的字段定义为自动时间戳和软删除字段;
	// 自动时间戳默认字段名为:`updated_at`、`created_at, 表字段数据类型为: INT 或 DATETIME
	// 软删除默认字段名为:`deleted_at`, 表字段数据类型为: DATETIME
	//autoUpdateTimeField := gen.FieldGORMTag("update_time", "column:update_time;type:int unsigned;autoUpdateTime")
	//autoCreateTimeField := gen.FieldGORMTag("create_time", "column:create_time;type:int unsigned;autoCreateTime")
	//softDeleteField := gen.FieldType("delete_time", "soft_delete.DeletedAt")
	// 模型自定义选项组
	//fieldOpts := []gen.ModelOpt{jsonField, autoCreateTimeField, autoUpdateTimeField, softDeleteField}

	// 创建模型的结构体,生成文件在 model 目录; 先创建的结果会被后面创建的覆盖
	// 这里创建个别模型仅仅是为了拿到`*generate.QueryStructMeta`类型对象用于后面的模型关联操作中
	//User := g.GenerateModel("user")
	// 创建全部模型文件, 并覆盖前面创建的同名模型
	allModel := g.GenerateAllTable()

	// 创建有关联关系的模型文件
	// 可以用于指定外键
	//Score := g.GenerateModel("score",
	//	append(
	//		fieldOpts,
	//		// user 一对多 address 关联, 外键`uid`在 address 表中
	//		gen.FieldRelate(field.HasMany, "user", User, &field.RelateConfig{GORMTag: "foreignKey:UID"}),
	//	)...,
	//)

	// 创建模型的方法,生成文件在 query 目录; 先创建结果不会被后创建的覆盖
	//g.ApplyBasic(User)
	g.ApplyBasic(allModel...)

	g.Execute()

}

5.错误处理、返回响应处理

在service层对错误进行处理

在这里插入图片描述

在controller返回给ginresult

在这里插入图片描述

在ginResult处理
如果错误是我们定义的错误,那么返回给用户,如果不是则返回通用错误 ServeError

package result

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	"hy_heymate/common/errType"
	"hy_heymate/common/logger"
	"net/http"
)

func GinResult(c *gin.Context, data interface{}, err error) {
	if err == nil {
		// 成功返回
		c.JSON(http.StatusOK, Success(data))
		return
	}

	// 打印错误日志到控制台
	log, _ := zap.NewDevelopment()
	log.Error(fmt.Sprintf("操作失败,错误信息:%s", err))

	// 错误返回
	errcode := errType.ServeError
	errmsg := errType.GetErrorMsg(errType.ServeError)

	causeErr := errors.Cause(err) // err类型
	e, ok := causeErr.(*errType.CodeError)
	if ok { // 自定义错误类型
		// 自定义CodeError
		errcode = e.GetErrCode()
		errmsg = e.GetErrMsg()
	}
	logger.Errorf("http-error: %v", err)
	c.JSON(http.StatusOK, Error(errcode, errmsg))

}

定义的错误如下,可根据业务需求添加

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1344203.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SVN下载安装(服务器与客户端)

1.下载 服务器下载:Download | VisualSVN Server 客户端下载:自行查找 2. 服务器安装 双击执行 运行 下一步 同意下一步 下一步 选中安装目录 3. 客户端安装 双击执行 下一步 4. 服务器创建仓库 5. 服务器创建用户 6. 客户端获取资源 文件夹右键

【机器学习】深度学习概论(二)

五、受限玻尔兹曼机(Restricted Boltzmann Machine,RBM) 5.1 RBM介绍 示例代码: Python 编写了一个简单的 RBM 实现,并用一些假数据训练了它。然后,他展示了如何用 RBM 来解释用户的电影偏好,以…

传统项目基于tomcat cookie单体会话升级分布式会话解决方案

传统捞项目基于servlet容器 cookie单体会话改造分布式会话方案 ##引入redis,spring-session依赖 <!--redis依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>&…

将网页变身移动应用:网址封装成App的完全指南

什么是网址封装&#xff1f; 网址封装是一个将你的网站或网页直接嵌入到一个原生应用容器中的过程。用户可以通过下载你的App来访问网站&#xff0c;而无需通过浏览器。这种方式不仅提升了用户体验&#xff0c;还可利用移动设备的功能&#xff0c;如推送通知和硬件集成。 小猪…

权威Scrum敏捷开发企业培训分享

课程简介 Scrum是目前运用最为广泛的敏捷开发方法&#xff0c;是一个轻量级的项目管理和产品研发管理框架。 这是一个两天的实训课程&#xff0c;面向研发管理者、项目经理、产品经理、研发团队等&#xff0c;旨在帮助学员全面系统地学习Scrum和敏捷开发, 帮助企业快速启动敏…

【HarmonyOS】鸿蒙开发简介与项目基础配置演示

从今天开始&#xff0c;博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”&#xff0c;对于刚接触这项技术的小伙伴在学习鸿蒙开发之前&#xff0c;有必要先了解一下鸿蒙&#xff0c;从你的角度来讲&#xff0c;你认为什么是鸿蒙呢&#xff1f;它出现的意义又是…

双语!性能优越|融合黏菌和差分变异的量子哈里斯鹰算法SDMQHHO

前面的文章里卡卡介绍了哈里斯鹰优化算法(Harris Hawks Optimization, HHO).HHO是 Heidari等[1]于2019年提出的一种新型元启发式算法&#xff0c;设计灵感来源于哈里斯鹰在捕食猎物过程中的合作行为以及突然袭击的狩猎风格&#xff0c;具有需调参数少、原理简单易实现、局部搜索…

大数据学习(30)-Spark Shuffle

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…

添加 Android App Links

添加 Android App Links功能 介绍一个简单的效果Android配置Add Url intent filtersAdd logic to handle the intentAssociate website 搭建网页支持AppLinks 介绍 Android App Links 是指将用户直接转到 Android 应用内特定内容的 HTTP 网址。Android App Links 可为您的应用带…

【深度学习-图像分类】02 - AlexNet 论文学习与总结

论文地址&#xff1a;ImageNet Classification with Deep Convolutional Neural Networks 论文学习 1. 摘要 本研究训练了一个大型深度卷积神经网络&#xff08;CNN&#xff09;&#xff0c;用于对ImageNet LSVRC-2010比赛中的1.2百万高分辨率图像进行分类&#xff0c;这些图…

CompletableFuture是什么?以及CompletableFuture的作用

文章目录 CompletableFuture 今天我们来聊聊 CompletableFuture CompletableFuture CompletableFuture 是 JDK1.8 里面引入的一个基于事件驱动的异步回调类。 简单来说&#xff0c;就是当使用异步线程去执行一个任务的时候&#xff0c;我们希望在任务结束以后触发一个后续的动作…

字符串转换tuple对象

给定“前导空格分隔的元组字符串”&#xff0c;还原成合法的python元组tuple对象。 (笔记模板由python脚本于2023年12月29日 19:29:03创建&#xff0c;本篇笔记适合熟悉Python元组tuple的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org…

适应变化:动态预测在机器学习中的作用

一、介绍 机器学习 (ML) 中的动态预测是指随着新数据的出现而不断更新预测的方法。这种方法在从医疗保健到金融等各个领域越来越重要&#xff0c;其中实时数据分析和最新预测可以带来更好的决策和结果。在本文中&#xff0c;我将讨论机器学习中动态预测的概念、其优势、挑战以及…

案例-旋转的太极图案(HTML+CSS)

使用css的动画变换效果完成“ 旋转太极“。 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>*{margin: 0;padding: 0;background-color: antiquewhite;}.tj{width: 0;height: 300px;/* border…

【c语言】飞机大战(1)

提前准备好游戏要的素材&#xff0c;可以到爱给网去找&#xff0c;飞机大战我们需要的是一个我方战机图片&#xff0c;一个背景图&#xff0c;三个敌方战机的图&#xff0c;我方战机的图片&#xff0c;敌方战机的图片&#xff0c;并且将图片和.cpp放在同一文件夹下. 这里创建.…

【nodejs】Express概念与使用介绍

Express Express是基于Node.js平台&#xff0c;从内置模块http封装出来的第三方模块&#xff0c;可以更方便的开发Web服务器。 中文官网&#xff1a; http://www.expressjs.com.cn/ 一、基本使用 // 导入express const express require(express) // 创建web服务器 const a…

Linux驱动开发简易流程

推荐视频&#xff1a; 正点原子【第四期】手把手教你学 Linux之驱动开发篇 小智-学长嵌入式Linux&Android底层开发入门教程 能力矩阵 基础能力矩阵 熟悉c/c、熟悉数据结构 熟悉linux系统&#xff0c;Shell脚本&#xff0c;Makefile/cmake/mk 文件IO、多线程、竞争、并发…

selenium模块有哪些用途?

Selenium模块是一个用于Web应用程序测试的模块&#xff0c;具有多种示例用法。以下是一些示例&#xff1a; 1.打开网页并执行一些基本操作&#xff0c;如点击按钮、输入文本等。 定位网页元素并执行操作&#xff0c;例如使用 find_element 方法查找单个元素&#xff0c;使用 f…

关于Omlox定位标准(一)——omlox hub

关于Omlox定位标准 Omlox是世界上第一个开放的定位标准&#xff0c;旨在实现灵活的实时定位解决方案&#xff0c;&#xff0c;可以使用来自各个制造商的单元。“omlox"一词源自拉丁词汇"omni”&#xff08;无处不在&#xff09;和"locus"&#xff08;位置…

磁盘阵列raid

一、服务器硬件 cpu 、 主板 、内存、硬盘、网卡、电源、raid卡、风扇、远程管理卡 二、硬盘尺寸 目前生产环境中主流的两种类型硬盘 3.5寸 和 2.5寸 硬盘 2.5寸硬盘可以通过使用硬盘托架后适用于3.5寸硬盘的服务器&#xff0c;但是3.5寸没法转换成2.5寸 1.如何在服务器上…