# ginchat **Repository Path**: shadaileng/ginchat ## Basic Information - **Project Name**: ginchat - **Description**: git chat - **Primary Language**: Go - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-10 - **Last Updated**: 2024-03-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # GinChat ## 创建项目 ```bash # 创建目录 mkdir ginchat && cd $_ # 初始化项目 go mod init ginchat ``` 创建程序入口`main.go` ```go package main func main(){} ``` ## 引入Gorm ### 创建模型 用户模型: `models/userBasic.go` ```go package models import ( "time" "gorm.io/gorm" ) type UserBasic struct { gorm.Model Name string Password string Phone string Email string Identity string ClientIP string ClientPort string LoginTime time.Time HeartbeatTime time.Time LogoutTime time.Time IsLogout bool DeviceInfo string } func (*UserBasic) TableName() string { return "user_basic" } ``` ### 测试模型 模型测试: `test/testGorm.go` ```go package main import ( "fmt" "ginchat/models" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func main() { db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { panic("failed to connect database") } // Migrate the schema db.AutoMigrate(&models.UserBasic{}) // Create user := &models.UserBasic{} user.Name = "qpf" db.Create(user) // Read fmt.Println(db.First(user, 1)) // find product with integer primary key // Update - update product's price to 200 db.Model(user).Update("Password", "1234") // Delete - delete product // db.Delete(&product, 1) } ``` ### 执行测试 ```bash # 导入依赖 go mod tidy # 执行测试程序 go run test/testGorm.go # Sqlite驱动以来CGO,需要开启CGO go env -w CGO_ENABLED=1 ``` ## 引入Gin 创建基础目录结构 ```text ├── common ├── config ├── go.mod ├── go.sum ├── main.go ├── models │ └── userBasic.go ├── router │ └── app.go ├── service │ └── index.go └── test └── testGorm.go ``` 1. `router/app.go`创建路由 ```go package router import ( "ginchat/service" "github.com/gin-gonic/gin" ) func Router() *gin.Engine { app := gin.Default() app.GET("/index", service.GetIndex) return app } ``` 2. `service/index.go`创建`index`服务 ```go package service import "github.com/gin-gonic/gin" func GetIndex(ctx *gin.Context) { ctx.JSON(200, gin.H{ "message": "Welcome !!", }) } ``` 3. 入口程序启动服务 ```go package main import ( "ginchat/router" ) func main() { r := router.Router() r.Run() } ``` ## 读取配置文件 1. 创建配置文件`config/app.yaml` ```yaml sqlite: dns: test.db ``` 2. `utils/initSystem.go`文件读取配置和创建数据库连接 ```go package utils import ( "fmt" "ginchat/models" "github.com/spf13/viper" "gorm.io/driver/sqlite" "gorm.io/gorm" ) var DB *gorm.DB func InitConfig() { viper.SetConfigName("app") viper.AddConfigPath("config") err := viper.ReadInConfig() if err != nil { fmt.Println(err) } fmt.Println("sqlite config: ", viper.Get("sqlite")) } func InitSqlite() *gorm.DB { var err error DB, err = gorm.Open(sqlite.Open(viper.GetString("sqlite.dns")), &gorm.Config{}) if err != nil { panic("failed to connect database") } user := &models.UserBasic{} DB.Find(user) // fmt.Println(user) return DB } ``` 3. `main.go`入口程序在启动服务之前,初始化系统 ```go package main import ( "ginchat/router" "ginchat/utils" ) func main() { utils.InitConfig() utils.InitSqlite() r := router.Router() r.Run() } ``` 4. 创建`service/user.go`程序作为用户服务 ```go package service import ( "ginchat/models" "ginchat/utils" "github.com/gin-gonic/gin" ) func GetUserList(ctx *gin.Context) { users := make([]*models.UserBasic, 10) utils.DB.Find(&users) ctx.JSON(200, gin.H{ "data": users, }) } ``` 5. `router/app.go`添加用户列表路由 ```go package router import ( "ginchat/service" "github.com/gin-gonic/gin" ) func Router() *gin.Engine { app := gin.Default() app.GET("/index", service.GetIndex) app.GET("/users", service.GetUserList) return app } ``` 6. 启动服务,访问地址: `http://127.0.0.1:8080/users` ## 整合Swagger 1. 下载`swagger`,并初始化 ```bash go install github.com/swaggo/swag/cmd/swag@latest swag init ``` 2. `main`函数设置`swag` ```go package main import ( "ginchat/docs" "ginchat/router" "ginchat/utils" swaggerfiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" ) func main() { utils.InitConfig() utils.InitSqlite() r := router.Router() docs.SwaggerInfo.BasePath = "" r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) r.Run() } ``` 3. 为路由函数添加注释 ```go package service import "github.com/gin-gonic/gin" // GetIndex // @Tags 首页 // @Produce json // @Success 200 {string} json{"code", "msg", "data"} // @Router /index [get] func GetIndex(ctx *gin.Context) { ctx.JSON(200, gin.H{ "code": 1, "msg": "Welcome !!", }) } ``` 4. 创建文档 ```bash swag init ``` 5. 启动服务,访问`http://127.0.0.1:8080/swagger/index.html`查看是否整合成功 ## 日志打印SQL `utils/initSystem.db`初始化`DB`时,配置`logger` ```go package utils import ( "fmt" "ginchat/models" "log" "os" "time" "github.com/spf13/viper" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) var DB *gorm.DB func InitConfig() { viper.SetConfigName("app") viper.AddConfigPath("config") err := viper.ReadInConfig() if err != nil { fmt.Println(err) } fmt.Println("sqlite config: ", viper.Get("sqlite")) } func InitSqlite() *gorm.DB { newLogger := logger.New( log.New(os.Stdout, "\n", log.LstdFlags), logger.Config{ SlowThreshold: time.Second, // 慢SQL阈值 LogLevel: logger.Info, // 日志级别 Colorful: true, // 日志颜色 }, ) var err error DB, err = gorm.Open(sqlite.Open(viper.GetString("sqlite.dns")), &gorm.Config{ Logger: newLogger, }) if err != nil { panic("failed to connect database") } user := &models.UserBasic{} DB.Find(user) // fmt.Println(user) return DB } ``` ## 用户增删改查 1. `service/user.go` ```go package service import ( "fmt" "ginchat/models" "ginchat/utils" "strconv" "github.com/gin-gonic/gin" ) // GetUserList // @Summary 用户列表 // @Tags 用户模块 // @Produce json // @Success 200 {string} json{"code", "msg", "data"} // @Router /users [get] func GetUserList(ctx *gin.Context) { users := make([]*models.UserBasic, 10) utils.DB.Find(&users) ctx.JSON(200, gin.H{ "data": users, }) } // CreateUser // @Summary 新增用户 // @Tags 用户模块 // @Param name formData string false "name" // @Param password formData string false "password" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user [post] func CreateUser(ctx *gin.Context) { user := models.UserBasic{} user.Name = ctx.PostForm("name") user.Password = ctx.PostForm("password") result := utils.DB.Create(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "msg": fmt.Sprintf("创建用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "data": "创建用户成功", }) } // DeleteUser // @Summary 删除用户 // @Tags 用户模块 // @Param id path int true "id" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user/{id} [delete] func DeleteUser(ctx *gin.Context) { user := models.UserBasic{} id, _ := strconv.Atoi(ctx.Param("id")) user.ID = uint(id) // user.ID = 0 fmt.Println(ctx.Param("id")) fmt.Println(&user) result := utils.DB.Delete(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "msg": fmt.Sprintf("删除用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "msg": "删除用户成功", }) } // UpdateUser // @Summary 修改用户 // @Tags 用户模块 // @Param id path int true "id" // @Param name formData string false "name" // @Param password formData string false "password" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user/{id} [put] func UpdateUser(ctx *gin.Context) { user := models.UserBasic{} id, _ := strconv.Atoi(ctx.Param("id")) user.ID = uint(id) user.Name = ctx.PostForm("name") user.Password = ctx.PostForm("password") fmt.Println(ctx.PostForm("name")) fmt.Println(&user) result := utils.DB.Model(&user).Updates(models.UserBasic{Name: user.Name, Password: user.Password}) if result.Error != nil { ctx.JSON(500, gin.H{ "msg": fmt.Sprintf("修改用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "data": "修改用户成功", }) } ``` 2. `router/app.go`配置路由 ```go package router import ( "ginchat/service" "github.com/gin-gonic/gin" ) func Router() *gin.Engine { app := gin.Default() app.GET("/index", service.GetIndex) app.GET("/users", service.GetUserList) app.POST("/user", service.CreateUser) app.DELETE("/user/:id", service.DeleteUser) app.PUT("/user/:id", service.UpdateUser) return app } ``` ## 验证结构体字段 1. `models/userBasic.go` ```go package models import ( "fmt" "time" "gorm.io/gorm" ) type UserBasic struct { gorm.Model Name string Password string Phone string `valid:"matches(^1[1-9]{1}\\d{9})"` Email string `valid:"email"` Identity string ClientIP string ClientPort string LoginTime time.Time HeartbeatTime time.Time LogoutTime time.Time IsLogout bool DeviceInfo string } func (*UserBasic) TableName() string { return "user_basic" } func (u *UserBasic) String() string { // `created_at`,`updated_at`,`deleted_at`,`name`,`password`,`phone`,`email`,`identity`,`client_ip`,`client_port`,`login_time`,`heartbeat_time`,`logout_time`,`is_logout`,`device_info` return fmt.Sprintf("UserBasic{CreateAt= %v, UpdateAt=%v, DeleteAt=%v, ID=%d, Name=%s, Password=%s, Phone=%s, Email=%s, Identity=%s, ClientIP=%s, ClientPort=%s, LoginTime=%s, HeartbeatTime=%s, LogoutTime=%s, IsLogout=%v, DeviceInfo=%s}", u.CreatedAt, u.UpdatedAt, u.DeletedAt, u.ID, u.Name, u.Password, u.Phone, u.Email, u.Identity, u.ClientIP, u.ClientPort, u.LoginTime, u.HeartbeatTime, u.LogoutTime, u.IsLogout, u.DeviceInfo) } ``` 2. `service/user.go`更新字段之前验证字段 ```go package service import ( "fmt" "ginchat/models" "ginchat/utils" "strconv" "github.com/asaskevich/govalidator" "github.com/gin-gonic/gin" ) // GetUserList // @Summary 用户列表 // @Tags 用户模块 // @Produce json // @Success 200 {string} json{"code", "msg", "data"} // @Router /users [get] func GetUserList(ctx *gin.Context) { users := make([]*models.UserBasic, 10) result := utils.DB.Find(&users) if result.Error != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("查找用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "code": 1, "msg": users, }) } // GetUser // @Summary 查询用户 // @Tags 用户模块 // @Param id path int true "id" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user/{id} [get] func GetUser(ctx *gin.Context) { user := models.UserBasic{} id, _ := strconv.Atoi(ctx.Param("id")) user.ID = uint(id) result := utils.DB.First(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("查找用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "code": 1, "data": user, }) } // CreateUser // @Summary 新增用户 // @Tags 用户模块 // @Param name formData string false "name" // @Param password formData string false "password" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user [post] func CreateUser(ctx *gin.Context) { user := models.UserBasic{} user.Name = ctx.PostForm("name") user.Password = ctx.PostForm("password") result := utils.DB.Create(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("创建用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "code": 1, "data": "创建用户成功", }) } // DeleteUser // @Summary 删除用户 // @Tags 用户模块 // @Param id path int true "id" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user/{id} [delete] func DeleteUser(ctx *gin.Context) { user := models.UserBasic{} id, _ := strconv.Atoi(ctx.Param("id")) user.ID = uint(id) // user.ID = 0 fmt.Println(ctx.Param("id")) fmt.Println(&user) result := utils.DB.Delete(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("删除用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "code": 1, "msg": "删除用户成功", }) } // UpdateUser // @Summary 修改用户 // @Tags 用户模块 // @Param id path int true "id" // @Param name formData string false "name" // @Param password formData string false "password" // @Param phone formData string false "phone" // @Param email formData string false "email" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user/{id} [put] func UpdateUser(ctx *gin.Context) { user := models.UserBasic{} id, _ := strconv.Atoi(ctx.Param("id")) user.ID = uint(id) user.Name = ctx.PostForm("name") user.Password = ctx.PostForm("password") user.Phone = ctx.PostForm("phone") user.Email = ctx.PostForm("email") fmt.Println(&user) valid, err := govalidator.ValidateStruct(user) fmt.Println(valid, err) if err != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("修改用户失败: %s", err), }) return } result := utils.DB.Model(&user).Updates(models.UserBasic{Name: user.Name, Password: user.Password, Phone: user.Phone, Email: user.Email}) // result := utils.DB.Save(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("修改用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "code": 1, "msg": "修改用户成功", }) } ``` ## 注册与登陆生成和验证秘密 1. `utils/md5.go`生成和校验密码 ```go package utils import ( "crypto/md5" "encoding/hex" "strings" ) func Md5(data string) string { md5_ := md5.New() md5_.Write([]byte(data)) return hex.EncodeToString(md5_.Sum(nil)) } func MD5(data string) string { return strings.ToUpper(Md5(data)) } func MakePassword(pass, slat string) string { return Md5(pass + slat) } func ValidPassword(pass, slat, password string) bool { return MakePassword(pass, slat) == password } ``` 2. `service/user.go` ```go package service import ( "fmt" "ginchat/models" "ginchat/utils" "math/rand/v2" "strconv" "github.com/asaskevich/govalidator" "github.com/gin-gonic/gin" ) // UserLogin // @Summary 用户登陆 // @Tags 用户模块 // @Param name formData string false "name" // @Param password formData string false "password" // @Success 200 {string} json{"code", "msg", "data"} // @Router /login [post] func UserLogin(ctx *gin.Context) { user := models.UserBasic{} name := ctx.PostForm("name") password := ctx.PostForm("password") result := utils.DB.Where("name = ?", name).First(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("用户不存在: %s", result.Error), }) return } if !utils.ValidPassword(password, user.Slat, user.Password) { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("密码错误"), }) return } ctx.JSON(200, gin.H{ "code": 1, "data": "登陆成功", }) } // CreateUser // @Summary 新增用户 // @Tags 用户模块 // @Param name formData string false "name" // @Param password formData string false "password" // @Success 200 {string} json{"code", "msg", "data"} // @Router /user [post] func CreateUser(ctx *gin.Context) { user := models.UserBasic{} name := ctx.PostForm("name") password := ctx.PostForm("password") utils.DB.Where("name = ?", name).First(&user) if user.Name != "" { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("用户已注册"), }) return } slat := fmt.Sprintf("%06d", rand.Int32()) mkpass := utils.MakePassword(password, slat) user.Password = mkpass user.Name = name user.Slat = slat result := utils.DB.Create(&user) if result.Error != nil { ctx.JSON(500, gin.H{ "code": -1, "msg": fmt.Sprintf("创建用户失败: %s", result.Error), }) return } ctx.JSON(200, gin.H{ "code": 1, "data": "创建用户成功", }) } ``` 3. `router/app.go`注册路由 ```go package router import ( "ginchat/service" "github.com/gin-gonic/gin" ) func Router() *gin.Engine { app := gin.Default() app.GET("/index", service.GetIndex) app.GET("/users", service.GetUserList) app.GET("/user/:id", service.GetUser) app.POST("/user", service.CreateUser) app.DELETE("/user/:id", service.DeleteUser) app.PUT("/user/:id", service.UpdateUser) app.POST("/login", service.UserLogin) return app } ```