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

文章目录

  • 前言
  • 一、项目介绍
  • 二、目录介绍
  • 三、创建过程
    • 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.mfbz.cn/a/280933.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Angular] 笔记 21:@ViewChild

chatgpt: 在 Angular 中,ViewChild 是一个装饰器,用于在组件类中获取对模板中子元素、指令或组件的引用。它允许你在组件类中访问模板中的特定元素,以便可以直接操作或与其交互。 例如,如果你在模板中有一个子组件或一个具有本地…

【shell】命令行自动补全(compgen、complete、compopt)

目录 用途 小例子 说明 进阶-多级补齐 Bash自动补齐原理 用途 自编写的Shell脚本/命令,很多时候都需要输入一定的参数。当参数较多而且较复制的时候,如果能使用Tab键补全就显得非常的便利。 小例子 例如,我们自定义一个命令 footest function…

使用flutter开发windows桌面软件读取ACR22U设备的nfc卡片id,5分钟搞定demo

最近有个需求,要使用acr122u读卡器插入电脑usb口,然后读取nfc卡片的id,并和用户账号绑定,调研了很多方式,之前使用rust实现过一次,还有go实现过一次,然后使用electron的时候遇到安装pcsc-lite失…

ORACLE P6 v23.12 最新虚拟机(VM)全套系统环境分享

引言 根据上周的计划,我简单制作了两套基于ORACLE Primavera P6 最新发布的23.12版本预构建了虚拟机环境,里面包含了全套P6 最新版应用服务 此虚拟机仅用于演示、培训和测试目的。如您在生产环境中使用此虚拟机,请先与Oracle Primavera销售代…

python常见报错信息!错误和异常!附带处理方法

作为 Python 初学者,在刚学习 Python 编程时,经常会看到一些报错信息。 Python 有两种错误很容易辨认:语法错误和异常。 Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。 …

【UE5蓝图】读取本地json文件修改窗口大小

效果 插件 蓝图 1.判断文件存在 2.1文件不存在,生成文件 {"ResolutionX":540, "ResolutionY":960} 2.2文件存在,直接读取 3.设置窗口大小 遇到的坑 1.分辨率太大,导致效果不理想,建议先往小填写。 2.选对…

QT 利用开源7z 实现解压各种压缩包,包括进度条和文件名的显示(zip,7z,rar,iso等50多种格式)

想做一个winRAR一样的解压软件吗?很简单,利用开源的7z库就能实现。我看网上其他人说的方法不敢苟同,误人子弟。以前自己在项目中使用过7z,这次又有需要,就想记录下来。如果你研究过如何用7z的话,一定知道7z的每一个GUID都代表了一种格式,50多种GUID也就有50多个格式,最…

Unity坦克大战开发全流程——开始场景——音效数据逻辑

开始场景——音效数据逻辑 从这里开始到后面的三小节我们都将干一件很重要的事——数据存储,只有实现了数据存储才能在再次进入游戏时保持游戏数据不被丢失。 类图分析:数据管理类是一个大类,它其中关联了两个类(这两个类都是数据…

迭代归并:归并排序非递归实现解析

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《数据结构&算法》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 📋 前言 归并排序的思想上我们已经全部介绍完了,但是同时也面临和快速排序一样的问题那就是递…

Go语言中的性能考虑和优化

优化您的Go代码以达到最佳性能 性能优化是软件开发的关键方面,无论您使用哪种编程语言。在这篇文章中,我们将探讨Go语言中的性能考虑和优化,Go是一种以其效率而著称的静态类型和编译语言。我们将深入探讨三个关键领域:分析并发代…

用CSS中的动画效果做一个转动的表

<!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title></title><style>*{margin:0;padding:0;} /*制作表的样式*/.clock{width: 500px;height: 500px;margin:0 auto;margin-top:100px;border-rad…

【Electron】webview 实现网页内嵌

实现效果&#xff1a; 当在输入框内输入某个网址后并点击button按钮 , 该网址内容就展示到下面 踩到的坑&#xff1a;之前通过web技术实现 iframe 标签内嵌会出现 同源策略&#xff0c;同时尝试过 vue.config.ts 内配置跨域项 那样确实 是实现啦 但不知道如何动态切换 tagert …

深入浅出Java虚拟机

文章目录 总体图类装载子系统一、类的加载过程一、加载二、链接三、初始化 二、类的加载器 运行时数据区一、程序计数器(ProgramCounter)二、虚拟机栈( Java Stack )三、本地方法栈&#xff08; Native Method Stack &#xff09;四、堆内存&#xff08;Direct Memory&#xff…

51单片机的中断相关知识

51单片机的中断相关知识点 一、中断概念和功能 概念 程序执行过程中CPU会遇到一些特殊情况&#xff0c;是正在执行的程序被“中断”&#xff0c;cpu中止原来正在执行的程序&#xff0c;转到处理异常情况或特殊事件的程序去执行&#xff0c;结束后再返回到原被中止的程序处(断…

【Android12】Android Framework系列---tombstone墓碑生成机制

tombstone墓碑生成机制 Android中程序在运行时会遇到各种各样的问题&#xff0c;相应的就会产生各种异常信号&#xff0c;比如常见的异常信号 Singal 11&#xff1a;Segmentation fault表示无效的地址进行了操作&#xff0c;比如内存越界、空指针调用等。 Android中在进程(主要…

Screenshot-to-code开源项目mac上实践

github上的开源项目&#xff0c;看介绍可以将设计ui图片转换为 HTML 和 CSS 源码地址&#xff1a; GitCode - 开发者的代码家园 我的mac安装了2.7和3.11&#xff0c;就用3吧直接上代码 安装 pip3 install keras tensorflow pillow h5py jupyter 报错 ERROR: Could not in…

TCP服务器的编写(下)

我们现在开始对我们的客户端开始封装 我们的客户端&#xff0c;创建完套接字&#xff0c;需不需要bind呢&#xff1f;&#xff1f; 当然是不需要的&#xff0c;你本身是一个客户端&#xff0c;其他人写的应用也可能是客户端&#xff0c;如果我们bind&#xff0c;一定意味着我们…

Javaweb之Mybatis入门的详细解析

Mybatis入门 前言 在前面我们学习MySQL数据库时&#xff0c;都是利用图形化客户端工具(如&#xff1a;idea、datagrip)&#xff0c;来操作数据库的。 在客户端工具中&#xff0c;编写增删改查的SQL语句&#xff0c;发给MySQL数据库管理系统&#xff0c;由数据库管理系统执行S…

CentOS7安装部署Zookeeper

文章目录 CentOS7安装部署Zookeeper一、前言1.简介2.架构3.集群角色4.特点5.环境 二、正文1.部署服务器2.基础环境1&#xff09;主机名2&#xff09;Hosts文件3&#xff09;关闭防火墙4&#xff09;JDK 安装部署 3.单机部署1&#xff09;下载和解压2&#xff09;配置文件3&…

linux文件夹介绍

在linux内核文件夹下面存在着许多文件夹&#xff0c;那么那些文件夹是什么用处呢&#xff0c;下面将为你介绍。 (1)documentation 这个文件夹下没有内核代码&#xff0c;仅仅有一套实用的文档&#xff0c;但这些文档的质量不一。比如内核文档的文件系统&#xff0c;在该文件夹下…
最新文章