响应的格式、数据类型设计
- 响应的数据全部存在data字段中,可以设置makereponse函数生成响应
- 设计合适的结构体
数据库进阶操作
- 外键、主键、联合主键
主键:唯一标识某一行
联合主键:多个列的值联合标识某一行
外键:将一个表的列与另一个表的列相关联,一个表中的外键值必须在另一个表中存在。用于实现一对多和多对多的查询
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的条件语句
- 使用gorm创建、查询、更新、删除
如果涉及多个表同时查询写入,采用事务,方便回滚
预加载,它在一次查询中获取了关联的数据,而不是为每个主对象执行额外的查询。
// 开始事务
tx := db.Begin()
// 执行事务操作
if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
// 发生错误,回滚事务
tx.Rollback()
// 处理事务错误
} else {
// 事务操作成功,提交事务
tx.Commit()
}
- 自动迁移表格automigrate
用于确保数据库中的表格结构与应用程序中的数据模型一致。
好用的库
- copier
copier.Copy(&employee, &user)
就是将user对象中的字段赋值到employee的同名字段中。如果目标对象中没有同名的字段,则该字段被忽略。
比如创建群组功能,不需要单个字段赋值给group结构,只需要用copier把groupreq的字段传递给group即可
- 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()
- 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)
- rand生成随机数
rand.New(rand.NewSource(time.Now().UnixNano())).Intn(1000000)
rand.New()//创建随机数生成器
rand.NewSource()//创建随机数种子
Intn()//随机数范围
- 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是否有效
-
strconv
提供字符串与其他数据类型的互转 -
gomail用于发送电子邮件
-
regexp正则表达式
//创建正则表达式对象
pattern := `^1[3456789]\d{9}$`
reg := regexp.MustCompile(pattern)//编译正则表达式
//匹配字符串
reg.MatchString(phone)
//查找字符串
reg.FindString(phone)
//查找字符串中所有符合条件的子串
reg.FindAllString(phone, -1)
//替换字符串
reg.ReplaceAllString(phone, "****")
其他
- iota
iota 是 Go 语言中的一个常量生成器,它用于在常量声明中生成连续的整数值。
iota 从 0 开始,每次在常量声明中使用它时都会自增 1。一般用于枚举常量,简化了常量的定义,使代码更具可读性。
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
- 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