响应的格式、数据类型设计

  1. 响应的数据全部存在data字段中,可以设置makereponse函数生成响应
  2. 设计合适的结构体

数据库进阶操作

  1. 外键、主键、联合主键
    主键:唯一标识某一行
    联合主键:多个列的值联合标识某一行
    外键:将一个表的列与另一个表的列相关联,一个表中的外键值必须在另一个表中存在。用于实现一对多和多对多的查询
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    CustomerID INT,
    FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
//连接多个表查询  
//根据 order找出customer的country
var country Country
if err := global.GVA_DB.
    Joins("JOIN customers ON orders.customer_id = customers.id").
    Joins("JOIN countries ON customers.country_id = countries.id").
    Where("orders.id = ?", 1).
    Select("countries.name").
    First(&country).Error; err != nil {
    // 处理查询错误
} else {
    // 使用 country 数据
}

//joins语法 
db.Joins("JOIN table_name ON condition")
//condition是SQL的条件语句
  1. 使用gorm创建、查询、更新、删除
    如果涉及多个表同时查询写入,采用事务,方便回滚
    预加载,它在一次查询中获取了关联的数据,而不是为每个主对象执行额外的查询。
// 开始事务
tx := db.Begin()

// 执行事务操作
if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
    // 发生错误,回滚事务
    tx.Rollback()
    // 处理事务错误
} else {
    // 事务操作成功,提交事务
    tx.Commit()
}
  1. 自动迁移表格automigrate
    用于确保数据库中的表格结构与应用程序中的数据模型一致。

好用的库

  1. copier
copier.Copy(&employee, &user) 

就是将user对象中的字段赋值到employee的同名字段中。如果目标对象中没有同名的字段,则该字段被忽略。
比如创建群组功能,不需要单个字段赋值给group结构,只需要用copier把groupreq的字段传递给group即可

  1. zap 日志库
    从标准日志库log到zap
  • 创建 Logger实例 使用 Zap 创建一个 Logger 实例,Logger 用于记录日志消息。通常你会在应用程序的初始化代码中创建 Logger。

    logger, err := zap.NewProduction()// NewProduction() 和 NewDevelopment() NewExample() 三种模式
    if err != nil {
        // 处理错误
    }
    defer logger.Sync() // 在程序退出时释放资源
    
  • 日志配置
    zap.NewDevelopmentConfig() 是 Zap 日志库提供的一个函数,用于创建一个适用于开发和调试环境的日志配置。

    • Level: 默认设置为 Debug,允许记录所有日志级别,包括 Info、Debug、Warn、Error、 DPanic 和 Panic。

    • Encoding: 默认使用 JSON 编码格式,以便日志可以轻松地被其他工具解析。

    • Output Paths: 默认将日志输出到标准错误流(stderr),这在开发环境中通常是合适的。

    • Error Output Paths: 默认将错误级别的日志(Error、DPanic 和 Panic)也输出到标准错误流。

    • Encoder Config: 编码器配置允许对 JSON 编码进行一些自定义设置,如时间格式。

  • 日志级别 Info(), Debug(), Warn(), Error(), Fatal()…

  • 结构化日志

logger.Info("User login", zap.String("username", "john_doe"), zap.Int("status_code", 200))
  • 设置全局字段
logger = logger.With(zap.String("app_name", "MyApp"))
  • 设置日志输出的位置
 // 创建一个输出到文件的 WriteSyncer
    fileWriter := zapcore.AddSync(file)
    
    // 创建一个 core
    fileCore := zapcore.NewCore(
        zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), // 或者使用其他编码器
        fileWriter,
        zap.DebugLevel, // 设置日志级别
    )
    
    // 创建 Logger
    logger := zap.New(fileCore, zap.AddCaller())
    defer logger.Sync()
  1. mapstructure将map映射到结构体,viper解析配置文件
    –无需自己写解析函数
# config.yaml
Mysql:
  host: "localhost"
  port: "3306"
  user: "用户名"
  password: "数据库密码"
  charset: "utf8"
  database: "TeamToDo"
type MySQLConfig struct {
	Host     string `mapstructure:"host"`
	Port     string `mapstructure:"port"`
	User     string `mapstructure:"user"`
	Password string `mapstructure:"password"`
	Charset  string `mapstructure:"charset"`
	Database string `mapstructure:"database"`
}
// 从文件加载配置
	viper.SetConfigFile("config.yaml")//设置配置文件
	err := viper.ReadInConfig()
	if err != nil {
		log.Fatalf("读取配置文件失败: %v", err)
	}

	// 初始化一个空的 Config 结构体
	var config Config

	// 使用 mapstructure 将配置文件加载到结构体中
	err = viper.Unmarshal(&config, viper.DecoderConfigOption(mapstructure.NewDecoderConfigOption()))//解码器
	if err != nil {
		log.Fatalf("解析配置文件失败: %v", err)
	}

	// 打印配置信息
	fmt.Printf("Mysql Host: %s\n", config.Mysql.Host) 
  1. rand生成随机数
rand.New(rand.NewSource(time.Now().UnixNano())).Intn(1000000)
rand.New()//创建随机数生成器
rand.NewSource()//创建随机数种子
Intn()//随机数范围
  1. jwt使用
  • 创建jwt
//创建claims
claims := jwt.MapClaims{
        "userID": id,
        "exp":    time.Now().Add(time.Second * time.Duration(global.Server.JWT.ExpireTime)).Unix(),
    }
//创建token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

//or   
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"userID": id,
		"exp":    time.Now().Add(time.Second * time.Duration(global.Server.JWT.ExpireTime)).Unix(),
	})
//使用密钥签名
tokenString, err := token.SignedString([]byte(global.Server.JWT.Key))

//从头部中获取token
auth := c.GetHeader("Authorization")
tokenString := auth[len("Bearer "):]//切片
//解析token
//允许你将特定声明解析到自定义的结构体中。
token, err := jwt.ParseWithClaims(tokenString, &jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) {
			return []byte(jwtKey), nil
		})
//获取基本claims
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return []byte(global.Server.JWT.Key), nil
    })
claims, ok := token.Claims.(*jwt.MapClaims)//claims转换
token.vaild//token是否有效
  1. strconv
    提供字符串与其他数据类型的互转

  2. gomail用于发送电子邮件

  3. regexp正则表达式

//创建正则表达式对象
pattern := `^1[3456789]\d{9}$`
reg := regexp.MustCompile(pattern)//编译正则表达式
//匹配字符串
reg.MatchString(phone)
//查找字符串
reg.FindString(phone)
//查找字符串中所有符合条件的子串
reg.FindAllString(phone, -1)
//替换字符串
reg.ReplaceAllString(phone, "****")

其他

  1. iota
    iota 是 Go 语言中的一个常量生成器,它用于在常量声明中生成连续的整数值。
    iota 从 0 开始,每次在常量声明中使用它时都会自增 1。一般用于枚举常量,简化了常量的定义,使代码更具可读性。
const (
		Sunday = iota // 0
		Monday        // 1
		Tuesday       // 2
		Wednesday     // 3
		Thursday      // 4
		Friday        // 5
		Saturday      // 6
	)
  1. git之rebase
  • 什么是rebase
    Rebase命令是一个在另一个分支基础之上重新应用提交的过程,用于把一个分支的修改合并到当前分支。使用rebase命令可以保持提交历史的整洁和线性,但是也可能导致冲突和提交历史的改变。

  • rebase和merge的区别

    • 合并历史的方式:

      Merge(合并): 在合并操作中,Git 会创建一个新的合并提交(merge commit),该提交将两个分支的历史记录合并在一起。这个合并提交拥有两个父提交,分别指向被合并的分支的最新提交。这种方法会保留分支的整体历史记录,但可能会导致历史记录中出现复杂的合并提交。

      Rebase(变基): 在变基操作中,Git 会将一系列提交从一个分支复制到另一个分支,并以线性的方式应用这些提交,就好像它们是在目标分支上连续提交的一样。这种方式会改写提交历史,不会创建合并提交,使得提交历史看起来更加干净和线性。

    • 提交历史的清晰度:

      Merge: Merge 操作会保留每个分支的独立历史,包括分支的起始点和合并点。这可以让您清楚地看到分支是如何演化的,但可能会导致历史记录中出现许多合并提交,使得历史较为复杂。

      Rebase: Rebase 操作会使提交历史更加线性和干净,因为它将提交直接应用在目标分支的顶部,看起来就像它们是按顺序提交的。这种方式通常让提交历史更容易理解,但也会删除分支的独立历史。

    • 合并冲突的处理:

      Merge: 如果在合并操作中出现冲突,Git 会创建一个合并提交,您需要解决冲突并提交该合并提交。这个过程相对较为简单,因为您只需要处理一次冲突。

      Rebase: 如果在变基操作中出现冲突,Git 会在每个提交上都可能出现冲突,您需要逐一解决这些冲突。这可能会更复杂,因为您需要处理多个提交的冲突。

    • 分支历史的保留与整理:

      Merge: Merge 会保留分支的整个历史记录,包括分支的起始点和合并点。这对于跟踪分支的演变非常有用,但可能导致历史记录复杂。

      Rebase: Rebase 会整理提交历史,将一系列提交线性化,并将它们应用在目标分支的顶部。这可以保持提交历史的整洁,但会删除分支的独立历史。

  • 参考 csdn
    freecodecamp