├── main.go ├── model ├── user.go └── db.go ├── README.md ├── go.mod ├── route └── route.go ├── middleware └── transaction.go ├── service └── user_service.go ├── repository └── user_repository.go ├── controller └── user_controller.go └── go.sum /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "golang-transaction/model" 5 | "golang-transaction/route" 6 | ) 7 | 8 | func main() { 9 | 10 | db, _ := model.DBConnection() 11 | route.SetupRoutes(db) 12 | } 13 | -------------------------------------------------------------------------------- /model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "gorm.io/gorm" 4 | 5 | // User ... User Database Model 6 | type User struct { 7 | gorm.Model 8 | Email string `json:"email" gorm:"unique;not null"` 9 | Wallet float64 `json:"wallet"` 10 | } 11 | 12 | //MoneyTransfer --- MoneyTransfer Struct 13 | type MoneyTransfer struct { 14 | Receiver uint `json:"receiver"` 15 | Giver uint `json:"giver"` 16 | Amount float64 `json:"amount"` 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Implement Database Transactions in Golang(Gin and GORM) Controller-Service-Repoitory Pattern Application 3 | 4 | In this project I will demonstrate how to implement DB Transaction in the Golang Gorm Project where Project is of Controller-Service-Repository Pattern. 5 | 6 | Want to read detailed article about this? [Read here](https://articles.wesionary.team/implement-database-transactions-with-repository-pattern-golang-gin-and-gorm-application-907517fd0743) 7 | 8 | 9 | ![project-structure](https://user-images.githubusercontent.com/10809061/115715579-0211a780-a398-11eb-9b29-4b8a4794910d.jpg) 10 | ![example](https://user-images.githubusercontent.com/10809061/115719084-7b5ec980-a39b-11eb-9526-54e7ea50165b.jpg) 11 | ![Transaction Flow Setup](https://miro.medium.com/v2/resize:fit:4800/format:webp/1*UMXLdvfuWg3Wn-lTCFfsMw.jpeg) 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module golang-transaction 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.7.1 7 | github.com/go-playground/validator/v10 v10.5.0 // indirect 8 | github.com/go-sql-driver/mysql v1.6.0 // indirect 9 | github.com/golang/protobuf v1.5.2 // indirect 10 | github.com/json-iterator/go v1.1.10 // indirect 11 | github.com/leodido/go-urn v1.2.1 // indirect 12 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 13 | github.com/modern-go/reflect2 v1.0.1 // indirect 14 | github.com/ugorji/go v1.2.5 // indirect 15 | golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc // indirect 16 | golang.org/x/sys v0.0.0-20210415045647-66c3f260301c // indirect 17 | golang.org/x/text v0.3.6 // indirect 18 | gopkg.in/yaml.v2 v2.4.0 // indirect 19 | gorm.io/driver/mysql v1.0.5 20 | gorm.io/gorm v1.21.8 21 | ) 22 | -------------------------------------------------------------------------------- /model/db.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "gorm.io/driver/mysql" 10 | "gorm.io/gorm" 11 | "gorm.io/gorm/logger" 12 | ) 13 | 14 | //DBConnection -> return db instance 15 | func DBConnection() (*gorm.DB, error) { 16 | USER := "root" 17 | PASS := "root" 18 | HOST := "localhost" 19 | PORT := "3306" 20 | DBNAME := "db-transaction" 21 | 22 | newLogger := logger.New( 23 | log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer 24 | logger.Config{ 25 | SlowThreshold: time.Second, // Slow SQL threshold 26 | LogLevel: logger.Info, // Log level 27 | Colorful: true, // Disable color 28 | }, 29 | ) 30 | 31 | url := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", USER, PASS, HOST, PORT, DBNAME) 32 | 33 | return gorm.Open(mysql.Open(url), &gorm.Config{Logger: newLogger}) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /route/route.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "golang-transaction/controller" 5 | "golang-transaction/middleware" 6 | "golang-transaction/repository" 7 | "golang-transaction/service" 8 | "log" 9 | 10 | "github.com/gin-gonic/gin" 11 | "gorm.io/gorm" 12 | ) 13 | 14 | //SetupRoutes : all the routes are defined here 15 | func SetupRoutes(db *gorm.DB) { 16 | httpRouter := gin.Default() 17 | 18 | userRepository := repository.NewUserRepository(db) 19 | 20 | if err := userRepository.Migrate(); err != nil { 21 | log.Fatal("User migrate err", err) 22 | } 23 | userService := service.NewUserService(userRepository) 24 | 25 | userController := controller.NewUserController(userService) 26 | 27 | users := httpRouter.Group("users") 28 | 29 | users.GET("/", userController.GetAllUser) 30 | users.POST("/", userController.AddUser) 31 | 32 | httpRouter.POST("/money-transfer", middleware.DBTransactionMiddleware(db), userController.TransferMoney) 33 | httpRouter.Run() 34 | 35 | } 36 | -------------------------------------------------------------------------------- /middleware/transaction.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/gin-gonic/gin" 8 | "gorm.io/gorm" 9 | ) 10 | 11 | //StatusInList -> checks if the given status is in the list 12 | func StatusInList(status int, statusList []int) bool { 13 | for _, i := range statusList { 14 | if i == status { 15 | return true 16 | } 17 | } 18 | return false 19 | } 20 | 21 | // DBTransactionMiddleware : to setup the database transaction middleware 22 | func DBTransactionMiddleware(db *gorm.DB) gin.HandlerFunc { 23 | return func(c *gin.Context) { 24 | txHandle := db.Begin() 25 | log.Print("beginning database transaction") 26 | 27 | defer func() { 28 | if r := recover(); r != nil { 29 | txHandle.Rollback() 30 | } 31 | }() 32 | 33 | c.Set("db_trx", txHandle) 34 | c.Next() 35 | 36 | if StatusInList(c.Writer.Status(), []int{http.StatusOK, http.StatusCreated}) { 37 | log.Print("committing transactions") 38 | if err := txHandle.Commit().Error; err != nil { 39 | log.Print("trx commit error: ", err) 40 | } 41 | } else { 42 | log.Print("rolling back transaction due to status code: ", c.Writer.Status()) 43 | txHandle.Rollback() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /service/user_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "golang-transaction/model" 5 | "golang-transaction/repository" 6 | 7 | "gorm.io/gorm" 8 | ) 9 | 10 | // UserService : represent the user's service contract 11 | type UserService interface { 12 | Save(model.User) (model.User, error) 13 | GetAll() ([]model.User, error) 14 | WithTrx(*gorm.DB) userService 15 | IncrementMoney(uint, float64) error 16 | DecrementMoney(uint, float64) error 17 | } 18 | 19 | type userService struct { 20 | userRepository repository.UserRepository 21 | } 22 | 23 | // NewUserService -> returns new user service 24 | func NewUserService(r repository.UserRepository) UserService { 25 | return userService{ 26 | userRepository: r, 27 | } 28 | } 29 | 30 | // WithTrx enables repository with transaction 31 | func (u userService) WithTrx(trxHandle *gorm.DB) userService { 32 | u.userRepository = u.userRepository.WithTrx(trxHandle) 33 | return u 34 | } 35 | 36 | func (u userService) Save(user model.User) (model.User, error) { 37 | 38 | return u.userRepository.Save(user) 39 | } 40 | 41 | func (u userService) GetAll() ([]model.User, error) { 42 | 43 | return u.userRepository.GetAll() 44 | } 45 | 46 | func (u userService) IncrementMoney(receiver uint, amount float64) error { 47 | 48 | return u.userRepository.IncrementMoney(receiver, amount) 49 | } 50 | 51 | func (u userService) DecrementMoney(giver uint, amount float64) error { 52 | 53 | return u.userRepository.DecrementMoney(giver, amount) 54 | } 55 | -------------------------------------------------------------------------------- /repository/user_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "errors" 5 | "golang-transaction/model" 6 | "log" 7 | 8 | "gorm.io/gorm" 9 | ) 10 | 11 | type userRepository struct { 12 | DB *gorm.DB 13 | } 14 | 15 | // UserRepository : represent the user's repository contract 16 | type UserRepository interface { 17 | Save(model.User) (model.User, error) 18 | GetAll() ([]model.User, error) 19 | IncrementMoney(uint, float64) error 20 | DecrementMoney(uint, float64) error 21 | WithTrx(*gorm.DB) userRepository 22 | Migrate() error 23 | } 24 | 25 | // NewUserRepository -> returns new user repository 26 | func NewUserRepository(db *gorm.DB) UserRepository { 27 | return userRepository{ 28 | DB: db, 29 | } 30 | } 31 | 32 | func (u userRepository) Migrate() error { 33 | log.Print("[UserRepository]...Migrate") 34 | return u.DB.AutoMigrate(&model.User{}) 35 | } 36 | 37 | func (u userRepository) Save(user model.User) (model.User, error) { 38 | log.Print("[UserRepository]...Save") 39 | err := u.DB.Create(&user).Error 40 | return user, err 41 | 42 | } 43 | 44 | func (u userRepository) GetAll() (users []model.User, err error) { 45 | log.Print("[UserRepository]...Get All") 46 | err = u.DB.Find(&users).Error 47 | return users, err 48 | 49 | } 50 | 51 | func (u userRepository) WithTrx(trxHandle *gorm.DB) userRepository { 52 | if trxHandle == nil { 53 | log.Print("Transaction Database not found") 54 | return u 55 | } 56 | u.DB = trxHandle 57 | return u 58 | } 59 | 60 | func (u userRepository) IncrementMoney(receiver uint, amount float64) error { 61 | log.Print("[UserRepository]...Increment Money") 62 | return u.DB.Model(&model.User{}).Where("id=?", receiver).Update("wallet", gorm.Expr("wallet + ?", amount)).Error 63 | } 64 | 65 | func (u userRepository) DecrementMoney(giver uint, amount float64) error { 66 | log.Print("[UserRepository]...Decrement Money") 67 | return errors.New("something") 68 | // return u.DB.Model(&model.User{}).Where("id=?", giver).Update("wallet", gorm.Expr("wallet - ?", amount)).Error 69 | } 70 | -------------------------------------------------------------------------------- /controller/user_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "golang-transaction/model" 5 | "golang-transaction/service" 6 | "log" 7 | "net/http" 8 | 9 | "github.com/gin-gonic/gin" 10 | "gorm.io/gorm" 11 | ) 12 | 13 | // UserController : represent the user's controller contract 14 | type UserController interface { 15 | AddUser(*gin.Context) 16 | GetAllUser(*gin.Context) 17 | TransferMoney(*gin.Context) 18 | } 19 | 20 | type userController struct { 21 | userService service.UserService 22 | } 23 | 24 | //NewUserController -> returns new user controller 25 | func NewUserController(s service.UserService) UserController { 26 | return userController{ 27 | userService: s, 28 | } 29 | } 30 | 31 | func (u userController) GetAllUser(c *gin.Context) { 32 | log.Print("[UserController]...get all Users") 33 | 34 | users, err := u.userService.GetAll() 35 | if err != nil { 36 | c.JSON(http.StatusBadRequest, gin.H{"error": "Error while getting users"}) 37 | return 38 | } 39 | 40 | c.JSON(http.StatusOK, gin.H{"data": users}) 41 | } 42 | 43 | func (u userController) AddUser(c *gin.Context) { 44 | log.Print("[UserController]...add User") 45 | var user model.User 46 | if err := c.ShouldBindJSON(&user); err != nil { 47 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 48 | return 49 | } 50 | 51 | user, err := u.userService.Save(user) 52 | if err != nil { 53 | c.JSON(http.StatusBadRequest, gin.H{"error": "Error while saving user"}) 54 | return 55 | } 56 | 57 | c.JSON(http.StatusOK, gin.H{"data": user}) 58 | } 59 | 60 | func (u userController) TransferMoney(c *gin.Context) { 61 | log.Print("[UserController]...get all Users") 62 | 63 | txHandle := c.MustGet("db_trx").(*gorm.DB) 64 | 65 | var moneyTransfer model.MoneyTransfer 66 | if err := c.ShouldBindJSON(&moneyTransfer); err != nil { 67 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 68 | return 69 | } 70 | 71 | if err := u.userService.WithTrx(txHandle).IncrementMoney(moneyTransfer.Receiver, moneyTransfer.Amount); err != nil { 72 | c.JSON(http.StatusBadRequest, gin.H{"error": "Error while incrementing money"}) 73 | txHandle.Rollback() 74 | return 75 | } 76 | 77 | if err := u.userService.WithTrx(txHandle).DecrementMoney(moneyTransfer.Giver, moneyTransfer.Amount); err != nil { 78 | c.JSON(http.StatusBadRequest, gin.H{"error": "Error while decrementing money"}) 79 | txHandle.Rollback() 80 | return 81 | } 82 | 83 | if err := txHandle.Commit().Error; err != nil { 84 | log.Print("trx commit error: ", err) 85 | } 86 | 87 | c.JSON(http.StatusOK, gin.H{"msg": "Successfully Money Transferred"}) 88 | } 89 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 4 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 5 | github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8= 6 | github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= 7 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 8 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 9 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 10 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 11 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 12 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= 13 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 14 | github.com/go-playground/validator/v10 v10.5.0 h1:X9rflw/KmpACwT8zdrm1upefpvdy6ur8d1kWyq6sg3E= 15 | github.com/go-playground/validator/v10 v10.5.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= 16 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 17 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 18 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 19 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 20 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 21 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 22 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 23 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 24 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 25 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 26 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 27 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 28 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 29 | github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 30 | github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= 31 | github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 32 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 33 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 34 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 35 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 36 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 37 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 38 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 39 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 40 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 41 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 42 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 43 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 44 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 45 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 46 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 47 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 48 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 49 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 50 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 51 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 52 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 53 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 54 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 55 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 56 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 57 | github.com/ugorji/go v1.2.5 h1:NozRHfUeEta89taVkyfsDVSy2f7v89Frft4pjnWuGuc= 58 | github.com/ugorji/go v1.2.5/go.mod h1:gat2tIT8KJG8TVI8yv77nEO/KYT6dV7JE1gfUa8Xuls= 59 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 60 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 61 | github.com/ugorji/go/codec v1.2.5 h1:8WobZKAk18Msm2CothY2jnztY56YVY8kF1oQrj21iis= 62 | github.com/ugorji/go/codec v1.2.5/go.mod h1:QPxoTbPKSEAlAHPYt02++xp/en9B/wUdwFCz+hj5caA= 63 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 64 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 65 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 66 | golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA= 67 | golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 68 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 69 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 70 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 71 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 72 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 73 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 74 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 75 | golang.org/x/sys v0.0.0-20210415045647-66c3f260301c h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4= 76 | golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 77 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 78 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 79 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 80 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 81 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 82 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 83 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 84 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 85 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 86 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 87 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 88 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 89 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 90 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 91 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 92 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 93 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 94 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 95 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 96 | gorm.io/driver/mysql v1.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o= 97 | gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI= 98 | gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= 99 | gorm.io/gorm v1.21.8 h1:2CEwZSzogdhsKPlJ9OvBKTdlWIpELXb6HbfLfMNhSYI= 100 | gorm.io/gorm v1.21.8/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 101 | --------------------------------------------------------------------------------