从网上找资料,发现,很多都是直接的结构
路由,后端的controller层,还有model层,都是放在了同一个main.go文件中,如果写项目的话,还得自己去拆文件,拆代码,经过查询和自己总结,下面放一个目录框架
总体目录结构
按照业务流程顺序,解释说明
1、加载自定义封装函数文件、数据库、redis
package main
import (
"ginchat/router"
"ginchat/utils"
)
func main() {
utils.InitConfig()
utils.InitMysql()
utils.InitRedis()
r := router.Router()
r.Run(":8081")
}
依次的三个函数
system_init.go文件里面
package utils
import (
"fmt"
"github.com/go-redis/redis/v8"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
var (
DB *gorm.DB
err error
Red *redis.Client
)
func InitRedis() {
fmt.Print("随便打印点什么,标记一下")
Red = redis.NewClient(&redis.Options{
Addr: viper.GetString("redis.addr"),
Password: viper.GetString("redis.password"),
DB: viper.GetInt("redis.DB"),
PoolSize: viper.GetInt("redis.poolSize"),
MinIdleConns: viper.GetInt("redis.minIdleConn"),
})
}
func InitConfig() {
viper.SetConfigName("app")
viper.AddConfigPath("config")
err := viper.ReadInConfig()
if err != nil {
fmt.Println(err)
}
fmt.Println("config app", viper.Get("app"))
fmt.Println("config mysql", viper.Get("mysql"))
}
func InitMysql() {
newLogger := logger.New(
//自定义日志模版 打印SQL语句
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second, //慢SQL阈值
LogLevel: logger.Info, //级别
Colorful: true, //彩色
},
)
fmt.Print(newLogger)
DB, err = gorm.Open(mysql.Open(viper.GetString("mysql.dns")), &gorm.Config{})
if err != nil {
fmt.Println("连接数据库失败", err)
} else {
fmt.Printf("数据库连接成功: %v", DB)
}
}
2、加载路由文件
package router
import (
"fmt"
"ginchat/service"
"github.com/gin-gonic/gin"
)
func Router() *gin.Engine {
fmt.Print("进入路由了")
r := gin.Default()
//用户模块
r.GET("/user/getUserList", service.GetUserList)
r.POST("/user/createUser", service.CreateUser)
r.DELETE("/user/deleteUser", service.DeleteUser)
r.PUT("/user/updateUser", service.UpdateUser)
return r
}
3、这里面的MySQL和Redis配置,可以直接写死在这个文件里面,也可以单独摘出来,放在配置文件中,这里是放在了配置文件中
app.yml中
mysql:
dns: admin3:123456@tcp(127.0.0.1:3306)/ginchat?charset=utf8mb4&parseTime=True&loc=Local
redis:
addr:"127.0.0.1:6379"
password:""
DB:0
poolSize:30
minIdleConn:30
说明一下,dns里面的参数
账号:密码@tcp(ip:端口号)/数据库名字?xxxxxxxx
4、然后,预备的工作就完成了。另外,有一个sql文件下面的testGorm.go文件
package main
import (
"fmt"
"ginchat/models"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
db, err := gorm.Open(mysql.Open("admin3:123456@tcp(127.0.0.1:3306)/ginchat?charset=utf8mb4&parseTime=True&loc=Local"), &gorm.Config{})
if err != nil {
fmt.Println("连接数据库失败", err)
} else {
fmt.Printf("数据库连接成功: %v", db)
}
err2 := db.AutoMigrate(&models.UserBasic{})
if err2 != nil {
return
} else {
fmt.Printf("创建表成功: %v", db)
}
}
用于临时生成数据表的一个文件,可以去直接执行此函数,生成数据表,或者也可以自己手动去利用navicat去创建表格
这里用到了models包下面的UserBasic{}
package models
import (
"fmt"
"ginchat/utils"
"gorm.io/gorm"
)
type UserBasic struct {
gorm.Model
Name string
Password string
Phone string `valid:"matches(^1[3-9]{1}\\d{9}$)"`
Email string `valid:"email"`
Avatar string //头像
Identity string
ClientIp string
Salt string
ClientPort string
LoginTime uint64
HeartbeatTime uint64
LoginOutTime uint64
IsLogout bool
DeviceInfo string
}
func (table *UserBasic) TableName() string {
return "user_basic"
}
func GetUserList() []*UserBasic {
data := make([]*UserBasic, 10)
//fmt.Printf("data的内容为: %v\n", &data)
utils.DB.Find(&data)
for _, v := range data {
fmt.Println(v)
}
return data
}
func CreateUser(user UserBasic) *gorm.DB {
return utils.DB.Create(&user)
}
func DeleteUser(user UserBasic) *gorm.DB {
return utils.DB.Delete(&user)
}
func UpdateUser(user UserBasic) *gorm.DB {
return utils.DB.Model(&user).Updates(UserBasic{Name: user.Name, Phone: user.Phone, Email: user.Email, Password: user.Password, Avatar: user.Avatar})
}
func FindUserByName(name string) UserBasic {
user := UserBasic{}
utils.DB.Where("name=?", name).First(&user)
return user
}
func FindUserByPhone(phone string) UserBasic {
user := UserBasic{}
utils.DB.Where("phone=?", phone).First(&user)
return user
}
func FindUserByEmail(email string) UserBasic {
user := UserBasic{}
utils.DB.Where("email=?", email).First(&user)
return user
}
UserBasic里面需要定义好数据表的字段
5、gorm.Model是gorm包已经先给预先设置好的4个字段
同时,前面是用驼峰式写法,如果不做特殊说明的话,基本上就会转变为下划线方式去设置字段,这个是gorm默认的对应关系
字段名可以控制,字段类型和大小,如果在创建之后不符合自己要求,可以自己额外修改
6、接下来就是后面的调取引用函数了
controller层
package service
import (
"fmt"
"ginchat/models"
"ginchat/utils"
"github.com/asaskevich/govalidator"
"github.com/gin-gonic/gin"
"math/rand"
"strconv"
)
func GetUserList(c *gin.Context) {
data := make([]*models.UserBasic, 10)
data = models.GetUserList()
c.JSON(200, gin.H{
"code": 200,
"message": data,
})
}
func CreateUser(c *gin.Context) {
user := models.UserBasic{}
user.Name = c.PostForm("name")
user.Phone = c.PostForm("phone")
user.Email = c.PostForm("email")
password := c.PostForm("password")
rePassword := c.PostForm("repassword")
salt := fmt.Sprintf("%06d", rand.Int31())
if password != rePassword {
c.JSON(200, gin.H{
"code": -1,
"message": "两次密码不一样!",
})
return
}
data1 := models.FindUserByName(user.Name)
fmt.Print(data1)
if data1.Name != "" {
c.JSON(200, gin.H{
"code": -1,
"message": "用户名不能重复!",
})
return
}
data2 := models.FindUserByPhone(user.Phone)
fmt.Print(data2)
if data2.Phone != "" {
c.JSON(200, gin.H{
"code": -1,
"message": "手机号不能重复!",
})
return
}
data3 := models.FindUserByEmail(user.Email)
fmt.Print(data3)
if data3.Email != "" {
c.JSON(200, gin.H{
"code": -1,
"message": "邮箱不能重复!",
})
return
}
user.Password = utils.MakePassword(password, salt)
user.Salt = salt
models.CreateUser(user)
c.JSON(200, gin.H{
"code": 0,
"message": "新增用户成功!",
})
}
func DeleteUser(c *gin.Context) {
user := models.UserBasic{}
id, _ := strconv.Atoi(c.Query("id"))
user.ID = uint(id)
models.DeleteUser(user)
c.JSON(200, gin.H{
"code": 0,
"message": "删除用户成功!",
})
}
func UpdateUser(c *gin.Context) {
user := models.UserBasic{}
id, _ := strconv.Atoi(c.PostForm("id"))
user.ID = uint(id)
user.Name = c.PostForm("name")
user.Password = c.PostForm("password")
user.Phone = c.PostForm("phone")
user.Email = c.PostForm("email")
_, err := govalidator.ValidateStruct(user)
if err != nil {
fmt.Print(err)
c.JSON(200, gin.H{
"code": -1,
"message": "修改参数不匹配!",
})
return
} else {
fmt.Print(user)
models.UpdateUser(user)
c.JSON(200, gin.H{
"code": 0,
"message": "修改用户成功!",
})
}
}
这里举例的是,用户信息表的相关增删改查
另外两个自己封装函数文件,暂时上面几个函数未用到
先贴在这里md5.go和resp.go
package utils
import (
"crypto/md5"
"encoding/hex"
"strings"
)
// Md5Encode 小写
func Md5Encode(data string) string {
h := md5.New()
h.Write([]byte(data))
tempStr := h.Sum(nil)
return hex.EncodeToString(tempStr)
}
// MD5Encode 大写
func MD5Encode(data string) string {
return strings.ToUpper(Md5Encode(data))
}
// MakePassword 加密
func MakePassword(plainpwd, salt string) string {
return Md5Encode(plainpwd + salt)
}
// ValidPassword 解密
func ValidPassword(plainpwd, salt, password string) bool {
return Md5Encode(plainpwd+salt) == password
}
package utils
import (
"encoding/json"
"net/http"
)
type H struct {
Code int
Msg string
Data interface{}
Rows interface{}
Total interface{}
}
func Resp(w http.ResponseWriter, code int, data interface{}, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
h := H{
Code: code,
Data: data,
Msg: msg,
}
ret, err := json.Marshal(h)
if err != nil {
panic(err)
}
w.Write(ret)
}
func RespFail(w http.ResponseWriter, msg string) {
Resp(w, -1, nil, msg)
}
func RespOK(w http.ResponseWriter, data interface{}, msg string) {
Resp(w, 0, data, msg)
}
func RespOKList(w http.ResponseWriter, data interface{}, total interface{}) {
RespList(w, 0, data, total)
}
func RespList(w http.ResponseWriter, code int, data interface{}, total interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
h := H{
Code: code,
Rows: data,
Total: total,
}
ret, err := json.Marshal(h)
if err != nil {
panic(err)
}
w.Write(ret)
}
此部分代码是看了B站up主之后总结出来的,感兴趣的可以去搜索看一下