目录

文章目录

前言

一、golang web是什么?

二、搭建流程

1.模块划分

2.详细开发步骤

总结前言

例如:习惯了java springboot 开发方式,比较疑惑golang web开发的流程和模块化的区分,

就golang web整个后台模块做下梳理。


一、golang web是什么?

   golang web是指 web后台HTTP接口开发模块。

二、搭建流程

1.模块划分

project
    -app       通常是程序启动后需要加载的数据
        app.go --加载gin 路由,指向 controller,并且做了拦截 heard token,解析放入上下文
    -biz       
       --model 
           --dto --请求的struct
           --entity --实体类
       --service   
    -common    公共常量,定义返回值 struct
       common.go
    -conf      配置信息,从配置文件里面读取到 struct
       config.go //根据环境变量读取环境resorce配置信息
    -controller  访问入口
       account_controller.go
    -resource  配置文件
       app.yml   配置数据库信息,根据 环境变量获取 取哪个配置文件
       app-dev.yml
       app-test.yml
       app-prod.yml 
    -util
     token_util.go 生成和解析token  
    -test      测试包,单元测试文件
    Dockerfile docker打包文件
    go.mod -go包管理
    main.go 启动入口

2.详细开发步骤

    相信大家看完前面的包,也有很多疑惑,该怎么开始呢?

    现在就一个包,一个包,把代码拆开来

  1.     main.go 文件
import (
	"fmt"
	"app"
	"log"
)

func main() {
    //调用 app包里面的启动
	err := app.Run()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("go web start ..!")
}


  1. 2.app包, 只有一个app.go 文件
  • 使用gin路由,指向contoller包
  • 使用util 包里面的解析 head里面的token,并把 解析后的userId,放入用户信息
  • 处理跨域请求
  • 初始化 dao 数据库连接
package app

import (
	"errors"
	"fmt"
     // 引入gin web插件
	"github.com/gin-gonic/gin"
    //引入mysql插件
	_ "github.com/go-sql-driver/mysql"
    //引入 http插件
   "net/http"


    //项目 自定义包
	"project/biz/dao"
	"project/biz/service"
	"ibc/common"
	"project/controller"
	"project/util"
	
)

func Run() error {
    //初始化数据库连接
	dao.InitDB()
	defer dao.CloseDB()
    //创建service
	accountService := service.NewAccountService()
	activitiesService := service.NewOfflineActivitiesService()

    //定义路由
	router := gin.Default()
	router.GET("/", homeEndpoint)    
	accountGroup := router.Group("/account")
	{
		accountCtl := controller.NewAccountController(accountService)
		accountGroup.POST("/", wrapHandler(accountCtl.InsertAccount))
		accountGroup.GET("/:userId", wrapHandler(accountCtl.FindAccount))
		accountGroup.GET("/account-flow/:accountId",                      wrapHandler(accountCtl.FindAccountFlow))
	}
	
	return router.Run(":8080")
}


type EndpointFunc func(*gin.Context) (any, error)

//定义返回
func homeEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"code": "SUCCESS"})
}


// 定义统一处理器
func wrapHandler(handler EndpointFunc) gin.HandlerFunc {
	return func(c *gin.Context) {
        // 处理跨域请求
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		Cors(c)
       
         
		//从header 获取token,
		var token = c.GetHeader("token")
		//解析token,如果token不存在,或者 解析错误,则返回 没权限
		userId, err := util.TokenHandle(token)
		if err != nil {
			fmt.Errorf(err.Error())
			c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "token is invalid"})
			return
		}
         //解析后 userId 放入上下文中,后面的service服务,可以获取该用户信息
		c.Set("userId", userId)
		data, err := handler(c)
		if err != nil {
			var systemErr *common.Error
			if errors.As(err, &systemErr) {
				c.JSON(systemErr.Status, common.NewErrorResult(systemErr.Code, systemErr.Msg))
			} else {
				c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": err.Error()})
			}
			return
		}
		c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "data": data})
	}
}

//跨域请求处理
func Cors(context *gin.Context) {
	method := context.Request.Method
	// 必须,接受指定域的请求,可以使用*不加以限制,但不安全
	//context.Header("Access-Control-Allow-Origin", "*")
	context.Header("Access-Control-Allow-Origin", context.GetHeader("Origin"))
	fmt.Println(context.GetHeader("Origin"))
	// 必须,设置服务器支持的所有跨域请求的方法
	context.Header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
	// 服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
	context.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Token")
	// 可选,设置XMLHttpRequest的响应对象能拿到的额外字段
	context.Header("Access-Control-Expose-Headers", "Access-Control-Allow-Headers, Token")
	// 可选,是否允许后续请求携带认证信息Cookir,该值只能是true,不需要则不设置
	context.Header("Access-Control-Allow-Credentials", "true")
	// 放行所有OPTIONS方法
	if method == "OPTIONS" {
		context.AbortWithStatus(http.StatusNoContent)
		return
	}
	context.Next()
}

2.dao包

  • 初始化连接
  • 业务dao层
package dao

import (
	"fmt"
    //引入 sqlx  
	"github.com/jmoiron/sqlx"
    //项目内的 配置信息
	"project/conf"
	"log"
)

var BaseDb sqlx.DB

// app.go 里面调用
func InitDB() {

	datasource := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&charset=utf8", conf.Config.Mysql.User, conf.Config.Mysql.Passwd, conf.Config.Mysql.Host, conf.Config.Mysql.DBName)
	db, err := sqlx.Open("mysql", datasource)
	if err != nil {
		log.Fatal(err)
		return
	}
	BaseDb = *db
	
}

// app.go 里面调用
func CloseDB() {
	err := BaseDb.Close()
	if err != nil {
		log.Fatal(err)
	}
}
package dao

import (
	"database/sql"
	"fmt"
	"project/biz/model/entity"
	"strings"
)

type UserAccountRepository interface {
	Insert(account entity.UserAccount) error
	Update(account entity.UserAccount) error
	DeleteById(id string) (bool, error)
	FindById(userId string) (entity.UserAccount, error)
	ListAll() ([]entity.UserAccount, error)
	ExistsByUserId(userId string) (error, bool)
}
type UserAccountFlowRepository interface {
	Insert(accountFlow entity.UserAccountFlow) (int, error)
	DeleteById(id string) (bool, error)
	FindById(id string) (entity.UserAccountFlow, error)
	ListAll() ([]entity.UserAccountFlow, error)
	ListByAccountId(accountId string) (*[]entity.UserAccountFlow, error)
	FindByActivities(id string, id2 int64) (*[]entity.UserAccountFlow, error)
}

type accountRepo struct {
}

type accountFlowRepo struct {
}

//NewAccountRepository 申明实现了 UserAccountRepository 接口
func NewAccountRepository() UserAccountRepository {
	return &accountRepo{}
}

//Insert  < 具体实现 NewAccountRepository
func (u accountRepo) Insert(account entity.UserAccount) error {
	sql := `insert into user_account(id,user_id,blockchain_address,balance)values(:id,:user_id,:blockchain_address,:balance)`
	result, err := BaseDb.NamedExec(sql, &account)
	if err != nil {
		return err
	}
	fmt.Println(result)

	return nil

}
func (u accountRepo) ExistsByUserId(userId string) (error, bool) {
	sqlStr := `select * from user_account where user_id=?`
	var account entity.UserAccount
	err := BaseDb.Get(&account, sqlStr, userId)
	if err == sql.ErrNoRows {
		return nil, false
	}
	if err != nil {
		return err, false
	}
	if account == (entity.UserAccount{}) {
		return nil, true
	}

	return nil, false

}

//Update  < 具体实现 NewAccountRepository
func (u accountRepo) Update(account entity.UserAccount) error {

	sqlColum := `update user_account set `
	if account.Balance >= -1 {
		sqlColum += ` balance =:balance ,`
	}
	if account.BlockchainAddress != "" {
		sqlColum += ` blockchain_address =:blockchain_address ,`
	}
	sqlColum = strings.TrimSuffix(sqlColum, ",")
	sqlColum += " where id =:id "

	_, err := BaseDb.NamedExec(sqlColum, &account)
	return err

}

//DeleteById  < 具体实现 NewAccountRepository
func (u accountRepo) DeleteById(id string) (bool, error) {
	sql := "update user_account set is_delete=1 where id =?"
	_, err := BaseDb.Exec(sql, id)
	if err != nil {
		return false, err
	}
	return true, err
}

//FindById  < 具体实现 NewAccountRepository
func (u accountRepo) FindById(userId string) (entity.UserAccount, error) {
	sql := "select * from user_account where user_id=?"
	var userAccount entity.UserAccount
	err := BaseDb.Get(&userAccount, sql, userId)
	if err != nil {
		return userAccount, err
	}
	return userAccount, err

}

//ListAll  < 具体实现 NewAccountRepository
func (u accountRepo) ListAll() ([]entity.UserAccount, error) {
	sql := "select * from user_account"
	var accountFlow []entity.UserAccount
	err := BaseDb.Get(&accountFlow, sql)

	return accountFlow, err
}

func NewAccountFlowRepository() UserAccountFlowRepository {
	return &accountFlowRepo{}
}

//Insert  < 具体实现 NewAccountFlowRepository
func (accountFlowRepo) Insert(account entity.UserAccountFlow) (int, error) {
	sql := `insert into user_account_flow(user_id,account_id,type,amount,blockchain_address,before_balance,balance,activities_id,transaction_info)
     values(:user_id,:account_id,:type,:amount,:blockchain_address,:before_balance,:balance,:activities_id,:transaction_info)`
	result, err := BaseDb.NamedExec(sql, account)
	if err != nil {
		return 0, err
	}
	id, err := result.LastInsertId()
	if err != nil {
		return 0, err
	}
	account.Id = id
	return int(id), nil

}

//DeleteById  < 具体实现 NewAccountFlowRepository
func (accountFlowRepo) DeleteById(id string) (bool, error) {
	sql := "update user_account_flow set is_delete=1 where id =?"
	_, err := BaseDb.Exec(sql, id)
	if err != nil {
		return false, err
	}
	return true, err
}

//FindById  < 具体实现 NewAccountFlowRepository
func (accountFlowRepo) FindById(id string) (entity.UserAccountFlow, error) {
	sql := "select * from user_account_flow where id=?"
	var userAccount entity.UserAccountFlow
	err := BaseDb.Get(&userAccount, sql, id)
	if err != nil {
		return userAccount, err
	}
	return userAccount, err

}

//ListAll  < 具体实现 NewAccountFlowRepository
func (accountFlowRepo) ListAll() ([]entity.UserAccountFlow, error) {
	sql := "select * from user_account_flow where is_delete =0"
	var accountFlow []entity.UserAccountFlow
	err := BaseDb.Get(&accountFlow, sql)

	return accountFlow, err
}

//ListByAccountId  < 具体实现 NewAccountFlowRepository
func (accountFlowRepo) ListByAccountId(accountId string) (*[]entity.UserAccountFlow, error) {
	sqlStr := "select * from user_account_flow where is_delete =0 and account_id =?"
	var accountFlow []entity.UserAccountFlow
	err := BaseDb.Select(&accountFlow, sqlStr, accountId)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	return &accountFlow, err
}
func (accountFlowRepo) FindByActivities(accountId string, activitiesId int64) (*[]entity.UserAccountFlow, error) {
	sqlStr := "select * from user_account_flow where is_delete =0 and account_id =? and activities_id=?"
	var accountFlow []entity.UserAccountFlow
	err := BaseDb.Select(&accountFlow, sqlStr, accountId, activitiesId)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	return &accountFlow, err
}

3.service

package service

import (
	"database/sql"
	"errors"
	"fmt"
	"project/dao"
	"project/biz/model/dto"
	"project/biz/model/entity"
	"project/util"
	"math"
	"strconv"
	"strings"
)

type UserAccountService interface {
	// InsertAccount 新增账户信息
	InsertAccount(accountReq dto.CreateAccountRequest) (*entity.UserAccount, error)
	// FindAccount 查找账户信息
	FindAccount(userId string) (any, error)
	// FindAccountFlow 根据账户ID查找账户
	FindAccountFlow(accountId string) ([]entity.UserAccountFlow, error)
	//RewardAccount 扫码奖励
	RewardAccount(scanning dto.Scanning) (any, error)
}

func NewAccountService() UserAccountService {
	return &accountService{
		AccountRepo:           dao.NewAccountRepository(),
		AccountFlowRepo:       dao.NewAccountFlowRepository(),
		OfflineActivitiesRepo: dao.NewOfflineActivitiesRepo(),
	}
}

type accountService struct {
	AccountRepo           dao.UserAccountRepository
	AccountFlowRepo       dao.UserAccountFlowRepository
	OfflineActivitiesRepo dao.OfflineActivitiesRepository
}

// InsertAccount 新增账户信息
func (me *accountService) InsertAccount(accountDto dto.CreateAccountRequest) (*entity.UserAccount, error) {
	err, b := me.AccountRepo.ExistsByUserId(accountDto.UserId)
	if err != nil {
		return nil, err
	}
	if b {
		return nil, err
	}
	node, _ := util.NewWorker(10)
	account := entity.UserAccount{
		UserId:            accountDto.UserId,
		Id:                "AC" + strconv.Itoa(int(node.GetId())),
		BlockchainAddress: accountDto.Address,
	}
	err = me.AccountRepo.Insert(account)
	if err != nil {
		return nil, err
	}

	return &account, nil
}

// FindAccount 查找账户信息
func (me *accountService) FindAccount(userId string) (any, error) {
	account, err := me.AccountRepo.FindById(userId)
	if err == sql.ErrNoRows {
		accDto := dto.CreateAccountRequest{}
		accDto.UserId = userId
		address, err := indiev_wallet.GetAddress()
		if err != nil {
			return nil, err
		}
		accDto.Address = address
		accountEntity, err := me.InsertAccount(accDto)
		if err != nil {
			return nil, err
		}
		return accountEntity, nil
	}
	if account.BlockchainAddress != "" {
		chainBalance := util.GetBalance(account.BlockchainAddress)
		chainBalance = strings.TrimLeft(chainBalance, "0x")
		f, err := strconv.ParseUint(chainBalance, 16, 32)
		if err != nil {
			return nil, err
		}
		//金额转换
		account.Balance = int64(float64(f) / math.Pow(10, 8))

	}
	return account, err

}

// FindAccountFlow 根据账户ID查找账户
func (me *accountService) FindAccountFlow(accountId string) ([]entity.UserAccountFlow, error) {
	accountFlows, err := me.AccountFlowRepo.ListByAccountId(accountId)
	if err != nil {
		return nil, err
	}
	return *accountFlows, nil
}


type scanningDto struct {
	RewardValue int64  `json:"rewardValue" db:"reward_value"`
	Unit        string `json:"unit" db:"reward_value"`
}

4.controller包

package controller

import (
	"github.com/gin-gonic/gin"
    //项目
	"project/biz/model/dto"
	"project/biz/service"
)

func NewAccountController(accountService service.UserAccountService) *AccountController {
	return &AccountController{
		accountService: accountService,
	}
}

// InsertAccount 新增账户信息
func (me *AccountController) InsertAccount(c *gin.Context) (any, error) {
	var req dto.CreateAccountRequest
	err := c.ShouldBindJSON(&req)
	if err != nil {
		return nil, err
	}

	account, err := me.accountService.InsertAccount(req)
	if err != nil {
		return nil, err
	}
	return account, err
}

// FindAccount 查找账户信息
func (me *AccountController) FindAccount(c *gin.Context) (any, error) {
	userId := c.Param("userId")
	account, err := me.accountService.FindAccount(userId)
	if err != nil {
		return nil, err
	}
	return account, err

}

// FindAccountFlow 根据账户ID查找账户
func (me *AccountController) FindAccountFlow(c *gin.Context) (any, error) {
	accountId := c.Param("accountId")
	account, err := me.accountService.FindAccountFlow(accountId)
	if err != nil {
		return nil, err
	}
	return account, err
}

type AccountController struct {
	accountService service.UserAccountService
}

5.common包

  • 返回struct
package common

import "net/http"

const (
	CodeSuccess        = "SUCCESS"
	CodeError          = "ERROR"
	ParamFormatError   = "PARAM_FORMAT_ERROR"
	ParamNotExistError = "PARAM_NOT_EXIST_ERROR"
	AppNotExistError   = "APP_NOT_EXIST_ERROR"
	AppExistError      = "APP_EXIST_ERROR"

	InternalServiceErr = "INTERNAL_SERVICE_ERROR"

	Unauthorized = "UNAUTHORIZED"
)

type Result struct {
	Code    string `json:"code"`
	Data    any    `json:"data"`
	Message string `json:"message"`
}

func NewOkResult(msg string) *Result {
	return &Result{
		Code:    CodeSuccess,
		Data:    nil,
		Message: msg,
	}
}

func NewDataResult(data any) *Result {
	return &Result{
		Code:    CodeSuccess,
		Data:    data,
		Message: "",
	}
}

func NewErrorResult(code, msg string) *Result {
	return &Result{
		Code:    code,
		Data:    nil,
		Message: msg,
	}
}

type Error struct {
	Status int
	Code   string
	Msg    string
}

func BadRequestErr(code string, msg string) error {
	return &Error{
		Status: http.StatusBadRequest,
		Code:   code,
		Msg:    msg,
	}
}

func NotFoundErr(code string, msg string) error {
	return &Error{
		Status: http.StatusNotFound,
		Code:   code,
		Msg:    msg,
	}
}

func UnauthorizedErr() error {
	return &Error{
		Status: http.StatusUnauthorized,
		Code:   "Unauthorized",
		Msg:    "",
	}
}

func InternalServerErr(code string, msg string) error {
	return &Error{
		Status: http.StatusInternalServerError,
		Code:   code,
		Msg:    msg,
	}
}

func (e *Error) Error() string {
	return e.Msg
}

6.config包

package conf

import (
	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"os"
)
//数据库配置
type DbConfig struct {
	Host   string
	User   string
	Passwd string
	DBName string
}

type Struct struct {
	Mysql DbConfig
}

var Config Struct

//初始化,优先加载
func init() {
	var appName = "app"
    //路径
	viper.AddConfigPath("./resource")
     //如果 环境信息,或者命令行有 这个变量,则追加环境信息,如果没有,默认取 app.yml 
     //加载了环境则是 app-dev.yml |app-test.yml
	configEnv := os.Getenv("GO_ENV")

	if configEnv != "" {
		appName += "-" + configEnv
	}

	viper.SetConfigName(appName)
	if err := viper.ReadInConfig(); err != nil {
		log.Panicf("Error reading config file  %s\n", err)
	}
    // viper读取了配置,并且将 json字符 反序列化 到对象
	err := viper.Unmarshal(&Config)
	if err != nil {
		log.Panicf("unable to decode into struct, %v", err)
	}

}

7.util包

package util

import (
	"encoding/json"
	"fmt"
	"github.com/golang-jwt/jwt"
	_ "github.com/golang-jwt/jwt"
	"log"
	"time"
)
// 秘钥,如果要对接,问问上游生成的时候,有没有 秘钥
var mySigningKey = []byte("asfasfdafasdfdasfa.")
//TokenHandle 解析token

func TokenHandle(tokenString string) (string, error) {
	token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
		return mySigningKey, nil
	})
	if err != nil {
		log.Println(err.Error())
		return "", err
	}
	data := token.Claims.(jwt.MapClaims)["data"]
	var tokenData = new(TokenData)
	if data != nil {
		var info = data.(string)
		err = json.Unmarshal([]byte(info), &tokenData)
	}

	return tokenData.UserId, err
}
//CreateToken is created token 
func CreateToken(data TokenData) (string, error) {
	dataByte, err := json.Marshal(data)
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"data": string(dataByte),
		"exp":  time.Now().Unix() + 1000*5,
		"iss":  "ibc_business",
	})

	tokenString, err := token.SignedString(mySigningKey)
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	fmt.Println("加密后的token字符串", tokenString)
	return tokenString, nil
}

//TokenData is 用户对象
type TokenData struct {
	UserId   string
	Age      int32
	NickName string
	Name     string
	Phone    string
}

8.go.mod

module project //项目名

go 1.18

require (
	github.com/33cn/chain33 v1.67.3
	github.com/gin-gonic/gin v1.8.1
	github.com/go-sql-driver/mysql v1.6.0
	github.com/golang-jwt/jwt v3.2.1+incompatible
	github.com/jmoiron/sqlx v1.3.5
	github.com/miguelmota/go-ethereum-hdwallet v0.1.1
	github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1
	github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
	github.com/sirupsen/logrus v1.9.0
	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
	github.com/spf13/viper v1.13.0
)

require (
	github.com/BurntSushi/toml v0.3.1 // indirect
	github.com/btcsuite/btcd v0.22.0-beta // indirect
	github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect
	github.com/decred/base58 v1.0.3 // indirect
	github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
	github.com/ethereum/go-ethereum v1.10.16 // indirect
	github.com/fsnotify/fsnotify v1.5.4 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-playground/locales v0.14.0 // indirect
	github.com/go-playground/universal-translator v0.18.0 // indirect
	github.com/go-playground/validator/v10 v10.10.0 // indirect
	github.com/go-stack/stack v1.8.0 // indirect
	github.com/goccy/go-json v0.9.7 // indirect
	github.com/golang/protobuf v1.5.2 // indirect
	github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
	github.com/hashicorp/hcl v1.0.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/leodido/go-urn v1.2.1 // indirect
	github.com/magiconair/properties v1.8.6 // indirect
	github.com/mattn/go-colorable v0.1.12 // indirect
	github.com/mattn/go-isatty v0.0.14 // indirect
	github.com/mitchellh/mapstructure v1.5.0 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/mr-tron/base58 v1.2.0 // indirect
	github.com/pelletier/go-toml v1.9.5 // indirect
	github.com/pelletier/go-toml/v2 v2.0.5 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/prometheus/client_golang v1.11.1 // indirect
	github.com/shopspring/decimal v1.2.0 // indirect
	github.com/spf13/afero v1.8.2 // indirect
	github.com/spf13/cast v1.5.0 // indirect
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
	github.com/subosito/gotenv v1.4.1 // indirect
	github.com/tjfoc/gmsm v1.3.2 // indirect
	github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
	github.com/ugorji/go/codec v1.2.7 // indirect
	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
	golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
	golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
	golang.org/x/text v0.3.7 // indirect
	google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
	google.golang.org/grpc v1.46.2 // indirect
	google.golang.org/protobuf v1.28.0 // indirect
	gopkg.in/ini.v1 v1.67.0 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)

总结

以上就是 golang搭建web项目的流程,使用gin 做http请求,同时封装了标准返回,拦截token.并且使用sqlx做 mysql的连接。