雁过留声 发表于 2025-3-13 09:16:46

go个人论坛项目

搭建个人论坛

https://i-blog.csdnimg.cn/direct/30871a2c5579462cbfb326f8f91ed695.jpeg
项目地址:MyForum: go+gin+vue搭建论坛 - Gitee.com
PS:有些地方没有写好,请选择性查看
初始化项目


[*]创建目录结构
https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5C17585%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20250226101407898.png&pos_id=img-xA9evsPn-1741771108195

[*]利用ini配置初始化框架

AppMode = debug
HttpPort = :3000
JwtKey = "dhjasdkajh321"


Db = mysql
DbHost = localhost
DbPort = 3306
DbUser = root
DbPassWord = flzx3qc
DbName = gForum
package config

import (
        "fmt"

        "gopkg.in/ini.v1"
)

var (
        AppMode string
        HttpPortstring
        JwtKey string

        Db string
        DbHost string
        DbPort string
        DbUser string
        DbPassWord string
        DbName string
)

func init(){
        file, err := ini.Load("config/config.ini")
        if err!=nil{
                fmt.Println("参数初始化错误:",err)
        }
        LoadServer(file)
        LoadDataBase(file)
}

func LoadServer(file *ini.File){
        AppMode = file.Section("server").Key("AppMode").MustString("debug")
        HttpPort = file.Section("server").Key("HttpPort").MustString("3000")
        JwtKey = file.Section("server").Key("JwtKey").MustString("dhajsfkas")
}

func LoadDataBase(file *ini.File){
        Db = file.Section("databse").Key("Db").MustString("")
        DbHost = file.Section("databse").Key("DbHost").MustString("")
        DbPort = file.Section("databse").Key("DbPort").MustString("")
        DbUser = file.Section("databse").Key("DbUser").MustString("")
        DbPassWord = file.Section("databse").Key("DbPassWord").MustString("")
        DbName = file.Section("databse").Key("DbName").MustString("")
}

[*]搭建gin框架
package myforum

import "MY-FORUM/routers"

func main() {
        routers.InitRouter()
}
package routers

import (
        "MY-FORUM/config"
        "github.com/gin-gonic/gin"
)

func InitRouter() {
        gin.SetMode(config.AppMode)
        r := gin.Default()
        r.Run(config.HttpPort)
}
注册登录模块

创建数据库

package main

import (
        "MY-FORUM/models"
        "MY-FORUM/routers"
)

func main() {
        // 连接数据库
        models.InitDb()
        // 配置路由
        routers.InitRouter()
}
package models

import (
        "MY-FORUM/config"
        "fmt"
        "time"
        "gorm.io/driver/mysql"
        "gorm.io/gorm"
)

var db *gorm.DB
var err error

func InitDb(){
        dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local",
        config.DbUser, config.DbPassWord, config.DbHost, config.DbPort, config.DbName)
        db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
        if err!=nil{
                fmt.Println("数据库连接失败", err)
        }
        sqlDB, err := db.DB()

        if err!=nil{
                fmt.Println("数据库连接失败:", err)
        }

        // 模型迁移
        db.AutoMigrate(&User{})

        // 设置连接池最大闲置连接数
        sqlDB.SetMaxIdleConns(10)

        // 设置数据库的最大连接数量
        sqlDB.SetMaxOpenConns(100)

        // 设置连接的最大可复用时间
        sqlDB.SetConnMaxLifetime(10*time.Second)
}
package models

import "gorm.io/gorm"

type User struct {
        gorm.Model
        UserId int64 `gorm:"type:bigint(20);primaryKey;autoIncrement" josn:"userid"`
        UserName string `gorm:"type:varchar(64);not null" json:"username"`
        PassWord string `gorm:"type:varchar(64);not null" json:"password"`
        Email string `gorm:"type:varchar(64)" json:"email"`
        Gender int `gorm:"type:tinyint(4)" json:"gender"`
        Role int `gorm:"type:tinyint(4);default:1" json:"role"`
}
注册


[*]定义注册接收表单结构体,并利用validator进行参数校验设置
PS:binding在绑定时进行校验,validate必要手动调用函数进行校验(参考:Go 验证器 validator 详解 | Go 技术论坛)
type UserSignUp struct{
        UserName string `json:"username" validate:"required"`
        PassWord string `json:"password" validate:"required"`
        ConfirmPassWord string `json:"confirm_password" validate:"required,eqfield=PassWord"`
}

[*]进行gorm利用
查询用户名是否已经存在
// 判断username是否已经存在
func FindUserNameExist(username string)bool{
        var user User
        err := db.Where("user_name = ?", username).First(&user).Error
        if err!=nil{
                fmt.Println("用户名称查询失败", err)
        }
        if user.ID > 0{
                return true
        }else{
                return false
        }
}
添加用户
// 添加用户
func AddUser(user User)int{
        err := db.Create(&user).Error
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}

[*]进行api函数的编写
func SignUp(c *gin.Context){
        var userSignUp models.UserSignUp
        _ = c.ShouldBind(&userSignUp)
        err := translater.Validate.Struct(userSignUp)
        // 参数校验有问题
        if err!=nil{
                msg := []string{}
                for _, err := range err.(validator.ValidationErrors) {
            msg = append(msg, err.Translate(translater.Trans))
      }
                fmt.Println(msg)
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : msg,
                        "data" : userSignUp,
                })
                c.Abort()
                return
        }
        username := userSignUp.UserName
        password := userSignUp.PassWord
        if models.FindUserNameExist(username){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.USERNAME_IS_EXIST),
                        "data" : userSignUp,
                })
                c.Abort()
                return
        }
        user := models.User{
                UserName: username,
                PassWord: password,
        }
        code := models.AddUser(user)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : userSignUp,
        })
}

[*]注册路由
func InitUserRouter(r *gin.Engine){
        UserRouter := r.Group("v1/user")
        UserRouter.POST("add", controller.SignUp) // 用户注册
}
登录


[*]定义接收登录表达信息的结构体
type UserLogin struct{
        UserName string `json:"username" validate:"required"`
        PassWord string`json:"password" validate:"required"`
        AccessToken string
        RefreshToken string
}

[*]在库中查询暗码
// 获取用户密码
func GetPassWord(username string)string{
        var user User
        err := db.Where("user_name = ?", username).First(&user).Error
        if err!=nil{
                fmt.Println("密码获取失败", err)
        }
        return user.PassWord
}

[*]获取Token
package jwt

import (
        "MY-FORUM/config"
        "errors"
        "time"
        "github.com/dgrijalva/jwt-go"
)

type MyClaims struct {
        UserName string `json:"username"`
        jwt.StandardClaims
}

var mySecret = []byte(config.JwtKey)

func keyFunc(_ *jwt.Token) (i interface{}, err error) {
        return mySecret, nil
}

const tokenExpire = 2*time.Hour


// 生成accessToken refreshToken
func GenToken(username string) (aToken, rToken string, err error) {
        // 创建一个自己的声明
        c := MyClaims{
                username,
                jwt.StandardClaims{        // JWT规定的7个官方字段
                        ExpiresAt: time.Now().Add(tokenExpire).Unix(), // 过期时间
                        Issuer:    "why",                                 // 签发人
                },
        }
        // 加密并获得完整的编码后的字符串token
        aToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(mySecret)

        // refresh token 不需要存任何自定义数据
        rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
                ExpiresAt: time.Now().Add(time.Second * 30).Unix(), // 过期时间
                Issuer:    "bluebell",                              // 签发人
        }).SignedString(mySecret)
        // 使用指定的secret签名并获得完整的编码后的字符串token
        return
}

[*]编写登录api
// 登录
func Login(c *gin.Context){
        var userLogin models.UserLogin
        _ = c.ShouldBind(&userLogin)
        err := Validate.Struct(userLogin)
        // 参数校验错误
        if err!=nil{
                msg := []string{}
                for _, err := range err.(validator.ValidationErrors){
                        msg = append(msg, err.Translate(Trans))
                }
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : msg,
                        "data" : userLogin,
                })
                c.Abort()
                return
        }
        // 判断用户是否存在
        if !models.FindUserNameExist(userLogin.UserName){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.USER_NOT_EXIST),
                        "data" : userLogin,
                })
                c.Abort()
                return
        }
        // 验证密码是否正确
        password := encrypt.GenerateSpw(userLogin.PassWord)
        truePassword := models.GetPassWord(userLogin.UserName)
        if password!=truePassword{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.PASSWORD_IS_WRONG),
                        "data" : userLogin,
                })
                c.Abort()
                return
        }
        // 获取token
        atoken,rtoken, err := jwt.GenToken(userLogin.UserName)
        if err != nil {
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.TOKEN_GET_ERROR),
                        "data" : userLogin,
                })
                c.Abort()
                return
        }
        userLogin.AccessToken = atoken
        userLogin.RefreshToken = rtoken
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(errmsg.SUCCES),
                "data" : userLogin,
        })
}
鉴权

jwt鉴权中心件
func JwtAuthMiddle() func(c *gin.Context){
        return func(c *gin.Context){
                // 验证header中是否有Authorization
                authHeader := c.GetHeader("Authorization")
                if authHeader=="" || !strings.HasPrefix(authHeader,"Bearer "){
                        c.JSON(http.StatusOK, gin.H{
                                "status" : errmsg.GetErrMsg(errmsg.ERROR),
                                "msg" : "用户权限不足",
                        })
                        c.Abort()
                        return
                }
                // 解析atoken
                atoken := authHeader
                claims, err := jwt.ParseToken(atoken)
                if err!=nil{
                        c.JSON(http.StatusOK, gin.H{
                                "status" : errmsg.GetErrMsg(errmsg.TOKEN_PARSE_ERROR),
                                "data" : atoken,
                        })
                        c.Abort()
                        return
                }
                // 验证用户
                username := claims.UserName
                if !models.FindUserNameExist(username){
                        c.JSON(http.StatusOK, gin.H{
                                "status" : errmsg.GetErrMsg(errmsg.USER_NOT_EXIST),
                                "data" : username,
                        })
                        c.Abort()
                        return
                }
                // 将username写入上下文
                c.Set("username", username)
                c.Next()
        }
}
rtoken refresh atoken
// rtoken刷新atoken
func RefreshToken(atoken, rtoken string)(token string, err error){
        // 判断rtoken是否有效
        if _,err = jwt.Parse(rtoken, keyFunc);err!=nil{
                return
        }
        // 从旧的atoken中解析出来username
        claims, err := ParseToken(atoken)
        // 如果还未过期,则返回开始的atoken
        if err==nil{
                token = atoken
                return
        }
        // 判断atoken是否为过期类型的错误
        v,_ := err.(*jwt.ValidationError)
        if v.Errors == jwt.ValidationErrorExpired{
                token,err = GetaToken(claims.UserName)
                err = nil
        }
        return
}
// 刷新atoken
func RefreshToken(c *gin.Context){
        rtoken := c.Query("rtoken")
        // 验证header中是否有Authorization
        authHeader := c.GetHeader("Authorization")
        if authHeader=="" || !strings.HasPrefix(authHeader,"Bearer "){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : "Token不存在",
                })
                c.Abort()
                return
        }
        // 解析atoken
        atoken := authHeader
        atoken,err := jwt.RefreshToken(atoken, rtoken)
        if err!=nil{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : "Token解析失败",
                })
                c.Abort()
                return
        }
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(errmsg.SUCCES),
                "atoken" : atoken,
        })
}
优化

暗码加密存储

package encrypt

import (
        "MY-FORUM/config"
        "encoding/base64"
        "fmt"

        "golang.org/x/crypto/scrypt"
)

func GenerateSpw(pw string) string {
        const KeyLen = 22
        const N = 1 << 10                           // CPU内存成本参数,必须是一个大于1的2的幂
        const r, p = 8, 1                           // r*p <2^30
        // salt长度为8比较好
        npw, err := scrypt.Key([]byte(pw), config.Salt, N, r, p, KeyLen)
        if err != nil {
                fmt.Println("密码加密失败", err)
        }
        return base64.StdEncoding.EncodeToString(npw) // 获取编码后的字符串
}
func (u *User)BeforeSave(_ *gorm.DB)(err error){
        u.PassWord = encrypt.GenerateSpw(u.PassWord)
        return nil
}
雪花算法生成用户ID

社区分类及详情模块

创建数据库

package models

import "gorm.io/gorm"

type Community struct {
        gorm.Model
        CommunityId int `gorm:"type:int(10)" json:"community_id"`
        CommunityName string `gorm:"type:varchar(64)" json:"community_name"`
        Introduction string `gorm:"type:varchar(256)" json:"introduction"`
}
// 模型迁移
db.AutoMigrate(&User{},&Community{})
package models

import (
        "MY-FORUM/utils/errmsg"
        "fmt"
        "time"

        "gorm.io/gorm"
)

type Community struct {
        gorm.Model
        CommunityName string `gorm:"type:varchar(64)" json:"community_name" validate:"required"`
        Introductionstring `gorm:"type:varchar(256)" json:"introduction" validate:"required"`
        Issure string `gorm:"type:varchar(256)" json:"issure" validate:"required"`
}

type CommunityDetail struct {
        ID            int       `json:"id" db:"id"`
        CommunityName string    `json:"community_name" db:"community_name"`
        Introductionstring    `json:"introduction" db:"introduction"`
        CreatedAt      time.Time `json:"created_at" db:"created_at"`
        UpdatedAt   time.Time `json:"updated_at" db:"updated_at"`
        Issure string `json:"issure" db:"issure"`
}

// 判断社区是否存在
func FindCommunity(name string) bool {
        var comm Community
        err := db.Where("community_name = ?", name).First(&comm).Error
        if err != nil {
                fmt.Println("社区查询失败", err)
        }
        if comm.ID > 0 {
                return true
        }
        return false
}

// 判断id是否存在
func FindCommunityByID(id int)(bool,string){
        var comm Community
        err := db.Where("id = ?", id).First(&comm).Error
        if err != nil {
                fmt.Println("社区查询失败", err)
        }
        if comm.ID > 0 {
                return true, comm.Issure
        }
        return false,""
}

// 添加社区
func AddCommunity(comm Community) int {
        err := db.Create(&comm).Error
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}

// 修改社区
func ChangeCommunity(comm Community)int{
        mp := mapinterface{}{}
        mp["Community_name"] = comm.CommunityName
        mp["introduction"] = comm.Introduction
        err := db.Model(&comm).Updates(mp).Error
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}

// 删除社区
func DeleteCommunity(id int)int{
        var comm Community
        err := db.Where("id = ?", id).Delete(&comm).Error
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}

// 查询社区列表(分页)
func GetCommunityList(pagesize, pagenum int)([]Community, int){
        var commList []Community
        err := db.Select("id", "community_name").Limit(pagesize).Offset((pagenum-1)*pagesize).Find(&commList).Error
        if err!=nil{
                return commList, errmsg.ERROR
        }
        return commList, errmsg.SUCCES
}

// 查询社区详情
func GetCommunityDetail(id int)(CommunityDetail, int){
        var comm CommunityDetail
        err := db.Model(&Community{}).Where("id = ?",id).First(&comm).Error
        if err!=nil{
                fmt.Println("社区详情查询失败", err)
                return comm, errmsg.ERROR
        }
        return comm, errmsg.SUCCES
}
增加社区种别(鉴权)

// 添加社区
func AddCommunity(c *gin.Context){
        var community models.Community
        _ = c.ShouldBind(&community)
        // 获取中间件的username
        username := c.GetString("username")
        community.Issure = username
        // 参数校验
        errs := Validate.Struct(community)
        if errs!=nil{
                msg := []string{}
                for _,err := range errs.(validator.ValidationErrors){
                        msg = append(msg, err.Translate(Trans))
                }
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : msg,
                        "data" : community,
                })
                c.Abort()
                return
        }
        // 判断社区是否存在
        if models.FindCommunity(community.CommunityName){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_EXIST),
                        "data" : community,
                })
                c.Abort()
                return
        }
        // 添加社区
        code := models.AddCommunity(community)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : community,
        })
}
获取社区分类列表

func GetCommunityList(c *gin.Context){
        pagesize,_ := strconv.Atoi(c.Query("pagesize"))
        pagenum,_ := strconv.Atoi(c.Query("pagenum"))
        if pagesize<=0{
                pagesize = -1
        }
        if pagenum<=1{
                pagenum = 1
        }
        commList, code := models.GetCommunityList(pagesize, pagenum)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : commList,
        })
}
根据社区ID查找分类详情

// 查询单个社区详情
func GetCommunityDetail(c *gin.Context){
        id,_ := strconv.Atoi(c.Query("id"))
        comm, code := models.GetCommunityDetail(id)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : comm,
        })
}
修改社区(鉴权)

// 修改社区
func ChangeCommunity(c *gin.Context){
        var community models.Community
        _ = c.ShouldBind(&community)
        // 获取中间件的username
        username := c.GetString("username")
        community.Issure = username
        // 参数校验
        errs := Validate.Struct(community)
        if errs!=nil{
                msg := []string{}
                for _,err := range errs.(validator.ValidationErrors){
                        msg = append(msg, err.Translate(Trans))
                }
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : msg,
                        "data" : community,
                })
                c.Abort()
                return
        }
        // 判断id是否存在
        flag, issure := models.FindCommunityByID(int(community.ID))
        if !flag{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
                        "data" : community,
                })
                c.Abort()
                return
        }
        // 判断是否为issure
        if issure!=c.GetString("username"){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
                        "data" : community,
                })
                c.Abort()
                return
        }
        // 判断社区名称是否存在
        if models.FindCommunity(community.CommunityName){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_EXIST),
                        "data" : community,
                })
                c.Abort()
                return
        }
        // 修改社区
        code := models.ChangeCommunity(community)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : community,
        })
}
删除社区(鉴权)

// 删除社区
func DeleteCommunity(c *gin.Context){
        id,_ :=strconv.Atoi(c.Query("id"))
        // 判断id是否存在
        flag, issure := models.FindCommunityByID(id)
        if !flag{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
                        "data" : id,
                })
                c.Abort()
                return
        }
        // 判断是否为issure
        if issure!=c.GetString("username"){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
                        "data" : id,
                })
                c.Abort()
                return
        }
        code := models.DeleteCommunity(id)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : id,
        })
}
论坛帖子模块

创建数据库

package models

import (
        "MY-FORUM/utils/errmsg"

        "gorm.io/gorm"
)

type Post struct {
        gorm.Model
        Title string `gorm:"type:varchar(64)" json:"title" validate:"required"`
        Content string `gorm:"type:longtext" json:"content" validate:"required"`
        AuthorName string `gorm:"type:varchar(64);not null" json:"author_name"`
        CommunityId int `gorm:"type:bigint;not null" json:"community_id" validate:"required"`
        Status int `gorm:"type:tinyint(4);default:0" json:"status"`
}

// 添加帖子数据
func AddPost(post Post)int{
        err := db.Create(&post).Error
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}

// 分页展示帖子列表
func GetPostList(pagesize, pagenum int)([]Post, int){
        var posts []Post
        err := db.Limit(pagesize).Offset((pagenum-1)*pagesize).Find(&posts).Error
        if err!=nil{
                return posts, errmsg.ERROR
        }
        return posts, errmsg.SUCCES
}

// 获取帖子详情
func GetPostDetail(id int)(Post, int){
        var post Post
        err := db.Where("id = ?", id).First(&post).Error
        if err!=nil{
                return post, errmsg.ERROR
        }
        return post, errmsg.SUCCES
}

// 修改帖子
func ChangePost(post Post)int{
        mp := mapinterface{}{}
        mp["title"] = post.Title
        mp["content"] = post.Content
        mp["community_id"] = post.CommunityId
        mp["status"] = post.Status
        err := db.Model(&Post{}).Where("id = ?", post.ID).Updates(mp).Error
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}

// 删除帖子
func DeletePost(id int)int{
        var post Post
        err := db.Where("id = ?", id).Delete(&post).Error
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}
// 模型迁移
        db.AutoMigrate(&User{},&Community{},&Post{})
创建帖子

func AddPost(c *gin.Context){
        var post models.Post
        _ = c.ShouldBind(&post)
        post.AuthorName = c.GetString("username")
        // 进行参数校验
        err := Validate.Struct(post)
        if err!=nil{
                msg := []string{}
                for _,err := range err.(validator.ValidationErrors){
                        msg = append(msg, err.Translate(Trans))
                }
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : msg,
                        "data" : post,
                })
                c.Abort()
                return
        }
        // 判断社区是否存在
        if flag,_ :=models.FindCommunityByID(post.CommunityId);!flag{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
                        "data" : post,
                })
                c.Abort()
                return
        }
        // 创建帖子
        code := models.AddPost(post)
        if code!=errmsg.SUCCES{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(code),
                        "data" : post,
                })
                c.Abort()
                return
        }
        // redis创建帖子
        code = redis.CreatePost(int(post.ID), post.CommunityId, post.Title, post.Content, post.AuthorName)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : post,
        })
}
分页展示帖子列表

func GetPostList(c *gin.Context){
        pagesize,_ := strconv.Atoi(c.Query("pagesize"))
        pagenum,_ := strconv.Atoi(c.Query("pagenum"))
        if pagesize <= 0{
                pagesize = -1
        }
        if pagenum < 1{
                pagenum = 1
        }
        posts, code := models.GetPostList(pagesize, pagenum)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : posts,
        })
}
获取帖子详情

func GetPostDetail(c *gin.Context){
        id,_ :=strconv.Atoi(c.Query("post_id"))
        _,code := models.GetPostDetail(id)
        if code==errmsg.ERROR{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
                })
                c.Abort()
                return
        }
        post, code := models.GetPostDetail(id)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : post,
        })
}
修改帖子

func ChangePost(c *gin.Context){
        var post models.Post
        _ = c.ShouldBind(&post)
        // 进行参数校验
        err := Validate.Struct(post)
        if err!=nil{
                msg := []string{}
                for _,err := range err.(validator.ValidationErrors){
                        msg = append(msg, err.Translate(Trans))
                }
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : msg,
                        "data" : post,
                })
                c.Abort()
                return
        }
        // 判断帖子是否存在
        findPost,code := models.GetPostDetail(int(post.ID))
        if code==errmsg.ERROR{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
                        "data" : post,
                })
                c.Abort()
                return
        }
        // 判断是否为自己的帖子
        if findPost.AuthorName != c.GetString("username"){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
                        "data" : post,
                })
                c.Abort()
                return
        }
        // 判断社区是否存在
        if flag,_ :=models.FindCommunityByID(post.CommunityId);!flag{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
                        "data" : post,
                })
                c.Abort()
                return
        }
        // 修改帖子内容
        code = models.ChangePost(post)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : post,
        })
}
删除帖子

func DeletePost(c *gin.Context){
        id,_ := strconv.Atoi(c.Query("post_id"))
        // 判断帖子是否存在
        findPost,code := models.GetPostDetail(id)
        if code==errmsg.ERROR{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
                        "post_id" : id,
                })
                c.Abort()
                return
        }
        // 判断是否为自己的帖子
        if findPost.AuthorName != c.GetString("username"){
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
                        "data" : findPost,
                })
                c.Abort()
                return
        }
        // 删除帖子
        code = models.DeletePost(id)
        c.JSON(http.StatusOK, gin.H{
                "status" : errmsg.GetErrMsg(code),
                "data" : findPost,
        })
}
按照时间or分数排序展示帖子列表

全部帖子

某一社区帖子

优化

redis缓存帖子列表

redis mysql 数据同等性

投票模块

redis数据库

package redis

import (
        "MY-FORUM/config"
        "fmt"
        "github.com/go-redis/redis"
)

var (
        client *redis.Client
)

func init(){
        client = redis.NewClient(&redis.Options{
                Addr : config.RedisHost+":"+config.RedisPort,
                Password : "",
                DB : 0,
                PoolSize:8,
                MinIdleConns:1,
        })
        _,err :=client.Ping().Result()
        if err!=nil{
                fmt.Println("redis连接失败", err)
        }
}

func Close(){
        _ = client.Close()
}
在redis中添加帖子相干信息

// redis存储帖子信息
func CreatePost(postid, community int, title, content, author_name string) int {
        now := time.Now().Unix()
        votedKey := PostVotedZSet+strconv.Itoa(postid)
        communityKey := CommunityPostSet+strconv.Itoa(community)
        // 事务操作
        // 创建一个事务管道
        pipeline := client.TxPipeline()
        // 设置投票过期时间
        pipeline.Expire(votedKey, time.Second*OneWekSecond)
        // 添加帖子时间排序
        pipeline.ZAdd(PostTimeZSet, redis.Z{
                Score: float64(now),
                Member: postid,
        })
        // 添加帖子分数排序
        pipeline.ZAdd(PostScoreZSet, redis.Z{
                Score: float64(0),
                Member: postid,
        })
        // 添加到对应的社区
        pipeline.SAdd(communityKey, postid)
        // 执行管道中所有命令
        _, err := pipeline.Exec()
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}
为帖子投票

func PostVoteHandler(c *gin.Context) {
        var item PostVote
        _ = c.ShouldBind(&item)
        // 进行参数校验
        errs := Validate.Struct(item)
        if errs!=nil{
                msg := []string{}
                for _,err := range errs.(validator.ValidationErrors){
                        msg = append(msg, err.Translate(Trans))
                }
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.ERROR),
                        "msg" : msg,
                        "data" : item,
                })
                c.Abort()
                return
        }
        // 判断帖子是否存在
        post_id,_ := strconv.Atoi(item.PostId)
        _,code := models.GetPostDetail(post_id)
        if code==errmsg.ERROR{
                c.JSON(http.StatusOK, gin.H{
                        "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
                        "data" : item,
                })
                c.Abort()
                return
        }
        // 进行投票
        code = redis.VoteForPost(c.GetString("username"), item.PostId, float64(item.Direction))
        c.JSON(http.StatusOK, gin.H{
                "status": errmsg.GetErrMsg(code),
                "data": item,
        })
}
// 为帖子投票
func VoteForPost(username, postid string, v float64)int {
        // 判断现在是否还在投票期
        // 获取帖子发布日期
        postTime := client.ZScore(PostTimeZSet, string(postid)).Val()
        if float64(time.Now().Unix()-int64(postTime))>OneWekSecond{
                return errmsg.POST_VOTE_IS_EXPIRE
        }
        key := PostVotedZSet+postid
        // 查看当前用户给帖子的投票记录
        ov := client.ZScore(key, username).Val()
        // 判断是否重复投票
        if ov==v{
                return errmsg.POST_USER_VOTED
        }
        // 计算两次投票的差值
        diff := v-ov
        // 创建一个事务管道
        pipeline := client.TxPipeline()
        // 更新帖子分数
        pipeline.ZIncrBy(PostScoreZSet, VoteScore*diff, postid)
        // 更新用户投票记录
        pipeline.ZRem(key, username)
        pipeline.ZAdd(key, redis.Z{
                Score: v,
                Member: username,
        })
        _,err := pipeline.Exec()
        if err!=nil{
                return errmsg.ERROR
        }
        return errmsg.SUCCES
}
优化

利用Reddit算法计算帖子排名分数

评论模块


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: go个人论坛项目