likes
comments
collection
share

Go 项目ORM、测试、api文档搭建

作者站长头像
站长
· 阅读数 24

最近前端已死的话题非常热,作为前端就想着学一点后端,还有啥精神内耗?

刚好这个课 山竹记账 Go 后端版 满足我的需求

命令行工具

代码

import "github.com/spf13/cobra"

func Run() {
  rootCmd := &cobra.Command{
    Use: "mangosteen",
  }

  severCmd := &cobra.Command{
    Use: "server",
    Run: func(cmd *cobra.Command, args []string) {
      RunServer()
    },
  }

  rootCmd.AddCommand(severCmd)

  rootCmd.Execute()
}

使用

go run main.go server

测试

  1. 新建文件夹 test
  2. 新建文件 xxx_test.go
  3. 测试函数 TestXxx(t *testing.T)
  4. 运行命令 go test ./test/xxx_test.go

构建 http 请求

import (
  "go-mangosteen/internal/router"
  "net/http"
  "net/http/httptest"
  "testing"

  "github.com/stretchr/testify/assert"
)

func TestPingController(t *testing.T) {
  r := router.New()
  w := httptest.NewRecorder()
  req, _ := http.NewRequest("GET", "/ping", nil)
  r.ServeHTTP(w, req)

  assert.Equal(t, 200, w.Code)
  assert.Equal(t, "pong", w.Body.String())
}

测试覆盖率

go test -coverprofile=coverage.out ./...

生成 html 文件:go tool cover -html=coverage.out -o coverage.html

数据库连接 —— sqlc

创建 postgres 数据库

docker run -d --name pg-for-go-mangosteen -e POSTGRES_USER=mangosteen -e POSTGRES_PASSWORD=123456 -e POSTGRES_DB=mangosteen_dev -e PGDATA=/var/lib/postgresql/data/pgdata -v pg-go-mangosteen-data:/var/lib/postgresql/data --network=network1 postgres:14

配置 sqlc

配置文件 sqlc.yaml,放在项目根目录下,内容如下:

version: "2"
sql:
  - engine: "postgresql"
    queries: "config/sqls" # sql 文件夹,CRUD 语句
    schema: "config/schema.sql" # 数据库结构文件,比如创建表的语句
    gen:
      go:
        package: "queries" # 生成的包名
        out: "config/gen_queries" # 将 sql 生成的文件放在 config/gen_queries 文件夹下

运行命令 sqlc generate,会在 config/gen_queries 文件夹下生成 db.gomodels.go 文件

连接数据库

import (
  "database/sql"
  "fmt"
  "log"

  _ "github.com/golang-migrate/migrate/v4/database/postgres"
  _ "github.com/golang-migrate/migrate/v4/source/file"
)

var SqlcDB *sql.DB

const (
  host     = "pg-for-go-mangosteen"
  port     = 5432
  username = "mangosteen"
  password = "123456"
  dbName   = "mangosteen_dev"
)

func SqlcConnect() {
  dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, username, password, dbName)
  db, err := sql.Open("postgres", dsn)
  if err != nil {
    log.Fatalln(err)
  }

  SqlcDB = db

  err = SqlcDB.Ping()
  if err != nil {
    log.Fatalln(err)
  }
}

func SqlcClose() {
  SqlcDB.Close()
  log.Println("Successfylly close db")
}

安装迁移文件工具 migrate

go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest

创建迁移文件

在命令行中使用

migrate create -ext sql  -dir config/migrations -seq create_users_table

封装 migrate 命令

func SqlcCreateMigration(filename string) {
  cmd := exec.Command("migrate", "create", "-ext", "sql", "-dir", "config/migrations", filename)
  if err := cmd.Run(); err != nil {
    log.Fatalln(err)
  }
}

createMigrationCmd := &cobra.Command{
  Use: "create:migration",
  Run: func(cmd *cobra.Command, args []string) {
    database.SqlcCreateMigration(args[0])
  },
}
rootCmd.AddCommand(createMigrationCmd)

最终可使用方式

go build ./ && ./mangosteen db create:migration create_users_table
# 或
migrate create -ext sql  -dir config/migrations -seq create_users_table

执行迁移文件

在命令行中使用

migrate -database "postgres://mangosteen:123456@pg-for-go-mangosteen:5432/mangosteen_dev?sslmode=disable" -source "file://$(pwd)/config/migrations" up

封装 migrate 命令

func SqlcMigration() {
  dir, err := os.Getwd()
  if err != nil {
    log.Fatalln(err)
  }

  m, err := migrate.New(
    fmt.Sprintf("file://%s/config/migrations", dir),
    fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", username, password, host, port, dbName),
  )
  if err != nil {
    log.Fatalln(err)
  }

  err = m.Up()
  if err != nil {
    if strings.Contains(err.Error(), "no change") {
      log.Fatalln(err)
    }
  }
}

migrateCmd := &cobra.Command{
  Use: "migrate",
  Run: func(cmd *cobra.Command, args []string) {
    database.SqlcMigration()
  },
}
rootCmd.AddCommand(migrateCmd)

最终可使用的方式

go build ./ && ./mangosteen db migrate
# 或
migrate -database "postgres://mangosteen:123456@pg-for-go-mangosteen:5432/mangosteen_dev?sslmode=disable" -source "file://$(pwd)/config/migrations" up

回滚迁移文件

在命令行中使用

migrate -database "postgres://mangosteen:123456@pg-for-go-mangosteen:5432/mangosteen_dev?sslmode=disable" -source "file://$(pwd)/config/migrations" down 1

封装 migrate 命令

func SqlcMigrationDown() {
  dir, err := os.Getwd()
  if err != nil {
    log.Fatalln(err)
  }

  m, err := migrate.New(
    fmt.Sprintf("file://%s/config/migrations", dir),
    fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", username, password, host, port, dbName),
  )
  if err != nil {
    log.Fatalln(err)
  }

  err = m.Steps(-1)
  if err != nil {
    log.Fatalln(err)
  }
}

migrateDownCmd := &cobra.Command{
  Use: "migrate:down",
  Run: func(cmd *cobra.Command, args []string) {
    database.SqlcMigrationDown()
  },
}
rootCmd.AddCommand(migrateDownCmd)

最终可使用的方式

go build ./ && ./mangosteen db migrate:down
# 或
migrate -database "postgres://mangosteen:123456@pg-for-go-mangosteen:5432/mangosteen_dev?sslmode=disable" -source "file://$(pwd)/config/migrations" down 1

使用方式

  1. 运行【创建迁移文件】命令
    • 运行命令后,会在 config/migrations 文件夹下生成 xxxx_create_users_table.up.sqlxxxx_create_users_table.down.sql 文件
      • xxxx_create_users_table.up.sql 文件内容如下:
        create table if not exists users(
          id serial primary key,
          email varchar(255) not null unique,
          created_at timestamp not null default now(),
          updated_at timestamp not null default now()
        );
        
      • xxxx_create_users_table.down.sql 文件内容如下:
        drop table if exists users;
        
  2. 运行【执行迁移文件】命令
    • 运行命令后,会在数据库中创建 users
  3. 运行【回滚迁移文件】命令
    • 运行命令后,会在数据库中删除 users
  4. 如果要增加字段或者删除字段,上述步骤
    • 其中【回滚迁移文件】命令只能回滚一次,如果要回滚多次,需要在执行多次【回滚迁移文件】命令
    • 【执行迁移文件】命令,会更新到最新的状态

编写 sql

  1. 将表结构写在 config/schema.sql 文件中
  2. 新建 config/sqls/xxx.sql 文件夹,编写 sql
    -- name: ListUsers :many
    select * from users
    order by id
    offset $1 limit $2;
    
  3. 运行命令 sqlc generate 生成对应的 go 文件

数据库连接 —— sql

连接数据库

import (
  "database/sql"
  "fmt"
  "log"
)

const (
  host     = "pg-for-go-mangosteen"
  port     = 5432
  user     = "mangosteen"
  password = "123456"
  dbName   = "mangosteen_dev"
)

var SqlDB *sql.DB

func SqlConnect() {
  connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbName)
  db, err := sql.Open("postgres", connStr)
  if err != nil {
    log.Fatalln(err)
  }
  SqlDB = db
  err = db.Ping()
  if err != nil {
    log.Fatal(err)
  }
  log.Println("Successfully connect to db")
}
func SqlClose() {
  SqlDB.Close()
  log.Println("Successfylly close db")
}

增删改查直接写 sql

文档:type DB

_, err = DB.Exec(`CREATE TABLE items (
  id SERIAL PRIMARY KEY,
  amount INT NOT NULL,
  happened_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)`)
if err != nil {
  log.Println(err)
}

数据库连接 —— gorm

安装工具

import (
  "gorm.io/driver/postgres"
  "gorm.io/gorm"
)

连接数据库

import (
  "fmt"
  "log"
  "time"

  "gorm.io/driver/postgres"
  "gorm.io/gorm"
)

var GormDB *gorm.DB

func GormConnect() {
  dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", gormHost, gormPort, gormUser, gormPassword, gormDbname)
  db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
  if err != nil {
    log.Fatalln(err)
  }
  GormDB = db
}

func GormClose() {
  gormDB, err := GormDB.DB()
  if err != nil {
    log.Fatalln(err)
  }
  gormDB.Close()
}

创建表

需要先指定表的结构体,内容如下

type User struct {
  ID        int
  Email     string `gorm:"uniqueIndex"`
  Phone     int
  CreatedAt time.Time
  UpdatedAt time.Time
}

type Item struct {
  ID         int
  UserID     int
  Amount     int
  HappenedAt time.Time
  CreatedAt  time.Time
  UpdatedAt  time.Time
}

type Tag struct {
  ID   int
  Name string
}
var models = []any{&User{}, &Item{}, &Tag{}}

创建表,内容如下:

func GormCreateTables() {
  for _, model := range models {
    if err := GormDB.Migrator().CreateTable(model); err != nil {
      log.Println(err)
    }
  }
}

修改表结构

先修改表对应的结构体,然后运行 GormDB.AutoMigrate 函数

func GormMigrate() {
  GormDB.AutoMigrate(models...)
}

增删改查

文档:Types

users := []User{}
tx := GormDB.Raw("select * from users where id = ?", 1).Scan(&users)
if tx.Error != nil {
  log.Println(tx.Error)
} else {
  log.Println(users)
}

什么是 migrate

在开发的过程中,由于需求的变动,数据库的结构可能进行修改、新增或删除表、列、约束等操作,这些操作统称为 migrate

migrate 的目的是为了保证数据库的结构和代码的结构保持一致,这样才能保证代码的正常运行

它有点类似于 git,提供版本控制、回滚操作

api 文档

api 文档使用的是:swag

安装工具:

import (
  "github.com/swaggo/swag"
  swaggerFiles "github.com/swaggo/files"
  ginSwagger "github.com/swaggo/gin-swagger"
)

新建路由

r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

编写注释

//	@title			山竹记账
//	@description	一个简单的记账系统
//	@BasePath		/api/v1
//	@host			localhost:8080
func main() {
  cmd.Run()
}

// Ping godoc
// @Summary      用来测试 API 是否正常
// @Description  用来测试 API 是否正常
// @Accept       json
// @Produce      json
// @Success      200
// @Failure      500
// @Router       /ping [get]
func PingController(c *gin.Context) {
  c.String(200, "pong")
}

生成文档

swag init -g router/router.go # 指定文件
# 或
swag init # 默认生成当前目录下的所有文件

启动服务

go run main.go server

go 安装依赖

  1. go mod tidy:用于整理和更新依赖列表
  2. go install xxx:当想要直接使用可执行文件时,用此方法,也就是说可以在命令行中使用
  3. go get xxx:通常在项目初始化阶段或首次引入新的包时使用,它会将包添加到项目的依赖列表中

往期文章

转载自:https://juejin.cn/post/7231434092230066236
评论
请登录