go个人论坛项目

打印 上一主题 下一主题

主题 934|帖子 934|积分 2802

搭建个人论坛


项目地址:MyForum: go+gin+vue搭建论坛 - Gitee.com
PS:有些地方没有写好,请选择性查看
初始化项目


  • 创建目录结构


  • 利用ini配置初始化框架
  1. [server]
  2. AppMode = debug
  3. HttpPort = :3000
  4. JwtKey = "dhjasdkajh321"
  5. [database]
  6. Db = mysql
  7. DbHost = localhost
  8. DbPort = 3306
  9. DbUser = root
  10. DbPassWord = flzx3qc
  11. DbName = gForum
复制代码
  1. package config
  2. import (
  3.         "fmt"
  4.         "gopkg.in/ini.v1"
  5. )
  6. var (
  7.         AppMode string
  8.         HttpPort  string
  9.         JwtKey string
  10.         Db string
  11.         DbHost string
  12.         DbPort string
  13.         DbUser string
  14.         DbPassWord string
  15.         DbName string
  16. )
  17. func init(){
  18.         file, err := ini.Load("config/config.ini")
  19.         if err!=nil{
  20.                 fmt.Println("参数初始化错误:",err)
  21.         }
  22.         LoadServer(file)
  23.         LoadDataBase(file)
  24. }
  25. func LoadServer(file *ini.File){
  26.         AppMode = file.Section("server").Key("AppMode").MustString("debug")
  27.         HttpPort = file.Section("server").Key("HttpPort").MustString("3000")
  28.         JwtKey = file.Section("server").Key("JwtKey").MustString("dhajsfkas")
  29. }
  30. func LoadDataBase(file *ini.File){
  31.         Db = file.Section("databse").Key("Db").MustString("")
  32.         DbHost = file.Section("databse").Key("DbHost").MustString("")
  33.         DbPort = file.Section("databse").Key("DbPort").MustString("")
  34.         DbUser = file.Section("databse").Key("DbUser").MustString("")
  35.         DbPassWord = file.Section("databse").Key("DbPassWord").MustString("")
  36.         DbName = file.Section("databse").Key("DbName").MustString("")
  37. }
复制代码

  • 搭建gin框架
  1. package myforum
  2. import "MY-FORUM/routers"
  3. func main() {
  4.         routers.InitRouter()
  5. }
复制代码
  1. package routers
  2. import (
  3.         "MY-FORUM/config"
  4.         "github.com/gin-gonic/gin"
  5. )
  6. func InitRouter() {
  7.         gin.SetMode(config.AppMode)
  8.         r := gin.Default()
  9.         r.Run(config.HttpPort)
  10. }
复制代码
注册登录模块

创建数据库

  1. package main
  2. import (
  3.         "MY-FORUM/models"
  4.         "MY-FORUM/routers"
  5. )
  6. func main() {
  7.         // 连接数据库
  8.         models.InitDb()
  9.         // 配置路由
  10.         routers.InitRouter()
  11. }
复制代码
  1. package models
  2. import (
  3.         "MY-FORUM/config"
  4.         "fmt"
  5.         "time"
  6.         "gorm.io/driver/mysql"
  7.         "gorm.io/gorm"
  8. )
  9. var db *gorm.DB
  10. var err error
  11. func InitDb(){
  12.         dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local",
  13.         config.DbUser, config.DbPassWord, config.DbHost, config.DbPort, config.DbName)
  14.         db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
  15.         if err!=nil{
  16.                 fmt.Println("数据库连接失败", err)
  17.         }
  18.         sqlDB, err := db.DB()
  19.         if err!=nil{
  20.                 fmt.Println("数据库连接失败:", err)
  21.         }
  22.         // 模型迁移
  23.         db.AutoMigrate(&User{})
  24.         // 设置连接池最大闲置连接数
  25.         sqlDB.SetMaxIdleConns(10)
  26.         // 设置数据库的最大连接数量
  27.         sqlDB.SetMaxOpenConns(100)
  28.         // 设置连接的最大可复用时间
  29.         sqlDB.SetConnMaxLifetime(10*time.Second)
  30. }
复制代码
  1. package models
  2. import "gorm.io/gorm"
  3. type User struct {
  4.         gorm.Model
  5.         UserId int64 `gorm:"type:bigint(20);primaryKey;autoIncrement" josn:"userid"`
  6.         UserName string `gorm:"type:varchar(64);not null" json:"username"`
  7.         PassWord string `gorm:"type:varchar(64);not null" json:"password"`
  8.         Email string `gorm:"type:varchar(64)" json:"email"`
  9.         Gender int `gorm:"type:tinyint(4)" json:"gender"`
  10.         Role int `gorm:"type:tinyint(4);default:1" json:"role"`
  11. }
复制代码
注册


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

  • 进行gorm利用
查询用户名是否已经存在
  1. // 判断username是否已经存在
  2. func FindUserNameExist(username string)bool{
  3.         var user User
  4.         err := db.Where("user_name = ?", username).First(&user).Error
  5.         if err!=nil{
  6.                 fmt.Println("用户名称查询失败", err)
  7.         }
  8.         if user.ID > 0{
  9.                 return true
  10.         }else{
  11.                 return false
  12.         }
  13. }
复制代码
添加用户
  1. // 添加用户
  2. func AddUser(user User)int{
  3.         err := db.Create(&user).Error
  4.         if err!=nil{
  5.                 return errmsg.ERROR
  6.         }
  7.         return errmsg.SUCCES
  8. }
复制代码

  • 进行api函数的编写
  1. func SignUp(c *gin.Context){
  2.         var userSignUp models.UserSignUp
  3.         _ = c.ShouldBind(&userSignUp)
  4.         err := translater.Validate.Struct(userSignUp)
  5.         // 参数校验有问题
  6.         if err!=nil{
  7.                 msg := []string{}
  8.                 for _, err := range err.(validator.ValidationErrors) {
  9.             msg = append(msg, err.Translate(translater.Trans))
  10.         }
  11.                 fmt.Println(msg)
  12.                 c.JSON(http.StatusOK, gin.H{
  13.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  14.                         "msg" : msg,
  15.                         "data" : userSignUp,
  16.                 })
  17.                 c.Abort()
  18.                 return
  19.         }
  20.         username := userSignUp.UserName
  21.         password := userSignUp.PassWord
  22.         if models.FindUserNameExist(username){
  23.                 c.JSON(http.StatusOK, gin.H{
  24.                         "status" : errmsg.GetErrMsg(errmsg.USERNAME_IS_EXIST),
  25.                         "data" : userSignUp,
  26.                 })
  27.                 c.Abort()
  28.                 return
  29.         }
  30.         user := models.User{
  31.                 UserName: username,
  32.                 PassWord: password,
  33.         }
  34.         code := models.AddUser(user)
  35.         c.JSON(http.StatusOK, gin.H{
  36.                 "status" : errmsg.GetErrMsg(code),
  37.                 "data" : userSignUp,
  38.         })
  39. }
复制代码

  • 注册路由
  1. func InitUserRouter(r *gin.Engine){
  2.         UserRouter := r.Group("v1/user")
  3.         UserRouter.POST("add", controller.SignUp) // 用户注册
  4. }
复制代码
登录


  • 定义接收登录表达信息的结构体
  1. type UserLogin struct{
  2.         UserName string `json:"username" validate:"required"`
  3.         PassWord string  `json:"password" validate:"required"`
  4.         AccessToken string
  5.         RefreshToken string
  6. }
复制代码

  • 在库中查询暗码
  1. // 获取用户密码
  2. func GetPassWord(username string)string{
  3.         var user User
  4.         err := db.Where("user_name = ?", username).First(&user).Error
  5.         if err!=nil{
  6.                 fmt.Println("密码获取失败", err)
  7.         }
  8.         return user.PassWord
  9. }
复制代码

  • 获取Token
  1. package jwt
  2. import (
  3.         "MY-FORUM/config"
  4.         "errors"
  5.         "time"
  6.         "github.com/dgrijalva/jwt-go"
  7. )
  8. type MyClaims struct {
  9.         UserName string `json:"username"`
  10.         jwt.StandardClaims
  11. }
  12. var mySecret = []byte(config.JwtKey)
  13. func keyFunc(_ *jwt.Token) (i interface{}, err error) {
  14.         return mySecret, nil
  15. }
  16. const tokenExpire = 2*time.Hour
  17. // 生成accessToken refreshToken
  18. func GenToken(username string) (aToken, rToken string, err error) {
  19.         // 创建一个自己的声明
  20.         c := MyClaims{
  21.                 username,
  22.                 jwt.StandardClaims{        // JWT规定的7个官方字段
  23.                         ExpiresAt: time.Now().Add(tokenExpire).Unix(), // 过期时间
  24.                         Issuer:    "why",                                 // 签发人
  25.                 },
  26.         }
  27.         // 加密并获得完整的编码后的字符串token
  28.         aToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(mySecret)
  29.         // refresh token 不需要存任何自定义数据
  30.         rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
  31.                 ExpiresAt: time.Now().Add(time.Second * 30).Unix(), // 过期时间
  32.                 Issuer:    "bluebell",                              // 签发人
  33.         }).SignedString(mySecret)
  34.         // 使用指定的secret签名并获得完整的编码后的字符串token
  35.         return
  36. }
复制代码

  • 编写登录api
  1. // 登录
  2. func Login(c *gin.Context){
  3.         var userLogin models.UserLogin
  4.         _ = c.ShouldBind(&userLogin)
  5.         err := Validate.Struct(userLogin)
  6.         // 参数校验错误
  7.         if err!=nil{
  8.                 msg := []string{}
  9.                 for _, err := range err.(validator.ValidationErrors){
  10.                         msg = append(msg, err.Translate(Trans))
  11.                 }
  12.                 c.JSON(http.StatusOK, gin.H{
  13.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  14.                         "msg" : msg,
  15.                         "data" : userLogin,
  16.                 })
  17.                 c.Abort()
  18.                 return
  19.         }
  20.         // 判断用户是否存在
  21.         if !models.FindUserNameExist(userLogin.UserName){
  22.                 c.JSON(http.StatusOK, gin.H{
  23.                         "status" : errmsg.GetErrMsg(errmsg.USER_NOT_EXIST),
  24.                         "data" : userLogin,
  25.                 })
  26.                 c.Abort()
  27.                 return
  28.         }
  29.         // 验证密码是否正确
  30.         password := encrypt.GenerateSpw(userLogin.PassWord)
  31.         truePassword := models.GetPassWord(userLogin.UserName)
  32.         if password!=truePassword{
  33.                 c.JSON(http.StatusOK, gin.H{
  34.                         "status" : errmsg.GetErrMsg(errmsg.PASSWORD_IS_WRONG),
  35.                         "data" : userLogin,
  36.                 })
  37.                 c.Abort()
  38.                 return
  39.         }
  40.         // 获取token
  41.         atoken,rtoken, err := jwt.GenToken(userLogin.UserName)
  42.         if err != nil {
  43.                 c.JSON(http.StatusOK, gin.H{
  44.                         "status" : errmsg.GetErrMsg(errmsg.TOKEN_GET_ERROR),
  45.                         "data" : userLogin,
  46.                 })
  47.                 c.Abort()
  48.                 return
  49.         }
  50.         userLogin.AccessToken = atoken
  51.         userLogin.RefreshToken = rtoken
  52.         c.JSON(http.StatusOK, gin.H{
  53.                 "status" : errmsg.GetErrMsg(errmsg.SUCCES),
  54.                 "data" : userLogin,
  55.         })
  56. }
复制代码
鉴权

jwt鉴权中心件
  1. func JwtAuthMiddle() func(c *gin.Context){
  2.         return func(c *gin.Context){
  3.                 // 验证header中是否有Authorization
  4.                 authHeader := c.GetHeader("Authorization")
  5.                 if authHeader=="" || !strings.HasPrefix(authHeader,"Bearer "){
  6.                         c.JSON(http.StatusOK, gin.H{
  7.                                 "status" : errmsg.GetErrMsg(errmsg.ERROR),
  8.                                 "msg" : "用户权限不足",
  9.                         })
  10.                         c.Abort()
  11.                         return
  12.                 }
  13.                 // 解析atoken
  14.                 atoken := authHeader[7:]
  15.                 claims, err := jwt.ParseToken(atoken)
  16.                 if err!=nil{
  17.                         c.JSON(http.StatusOK, gin.H{
  18.                                 "status" : errmsg.GetErrMsg(errmsg.TOKEN_PARSE_ERROR),
  19.                                 "data" : atoken,
  20.                         })
  21.                         c.Abort()
  22.                         return
  23.                 }
  24.                 // 验证用户
  25.                 username := claims.UserName
  26.                 if !models.FindUserNameExist(username){
  27.                         c.JSON(http.StatusOK, gin.H{
  28.                                 "status" : errmsg.GetErrMsg(errmsg.USER_NOT_EXIST),
  29.                                 "data" : username,
  30.                         })
  31.                         c.Abort()
  32.                         return
  33.                 }
  34.                 // 将username写入上下文
  35.                 c.Set("username", username)
  36.                 c.Next()
  37.         }
  38. }
复制代码
rtoken refresh atoken
  1. // rtoken刷新atoken
  2. func RefreshToken(atoken, rtoken string)(token string, err error){
  3.         // 判断rtoken是否有效
  4.         if _,err = jwt.Parse(rtoken, keyFunc);err!=nil{
  5.                 return
  6.         }
  7.         // 从旧的atoken中解析出来username
  8.         claims, err := ParseToken(atoken)
  9.         // 如果还未过期,则返回开始的atoken
  10.         if err==nil{
  11.                 token = atoken
  12.                 return
  13.         }
  14.         // 判断atoken是否为过期类型的错误
  15.         v,_ := err.(*jwt.ValidationError)
  16.         if v.Errors == jwt.ValidationErrorExpired{
  17.                 token,err = GetaToken(claims.UserName)
  18.                 err = nil
  19.         }
  20.         return
  21. }
复制代码
  1. // 刷新atoken
  2. func RefreshToken(c *gin.Context){
  3.         rtoken := c.Query("rtoken")
  4.         // 验证header中是否有Authorization
  5.         authHeader := c.GetHeader("Authorization")
  6.         if authHeader=="" || !strings.HasPrefix(authHeader,"Bearer "){
  7.                 c.JSON(http.StatusOK, gin.H{
  8.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  9.                         "msg" : "Token不存在",
  10.                 })
  11.                 c.Abort()
  12.                 return
  13.         }
  14.         // 解析atoken
  15.         atoken := authHeader[7:]
  16.         atoken,err := jwt.RefreshToken(atoken, rtoken)
  17.         if err!=nil{
  18.                 c.JSON(http.StatusOK, gin.H{
  19.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  20.                         "msg" : "Token解析失败",
  21.                 })
  22.                 c.Abort()
  23.                 return
  24.         }
  25.         c.JSON(http.StatusOK, gin.H{
  26.                 "status" : errmsg.GetErrMsg(errmsg.SUCCES),
  27.                 "atoken" : atoken,
  28.         })
  29. }
复制代码
优化

暗码加密存储

  1. package encrypt
  2. import (
  3.         "MY-FORUM/config"
  4.         "encoding/base64"
  5.         "fmt"
  6.         "golang.org/x/crypto/scrypt"
  7. )
  8. func GenerateSpw(pw string) string {
  9.         const KeyLen = 22
  10.         const N = 1 << 10                             // CPU内存成本参数,必须是一个大于1的2的幂
  11.         const r, p = 8, 1                             // r*p <2^30
  12.         // salt长度为8比较好
  13.         npw, err := scrypt.Key([]byte(pw), config.Salt, N, r, p, KeyLen)
  14.         if err != nil {
  15.                 fmt.Println("密码加密失败", err)
  16.         }
  17.         return base64.StdEncoding.EncodeToString(npw) // 获取编码后的字符串
  18. }
复制代码
  1. func (u *User)BeforeSave(_ *gorm.DB)(err error){
  2.         u.PassWord = encrypt.GenerateSpw(u.PassWord)
  3.         return nil
  4. }
复制代码
雪花算法生成用户ID

社区分类及详情模块

创建数据库

  1. package models
  2. import "gorm.io/gorm"
  3. type Community struct {
  4.         gorm.Model
  5.         CommunityId int `gorm:"type:int(10)" json:"community_id"`
  6.         CommunityName string `gorm:"type:varchar(64)" json:"community_name"`
  7.         Introduction string `gorm:"type:varchar(256)" json:"introduction"`
  8. }
复制代码
  1. // 模型迁移
  2. db.AutoMigrate(&User{},&Community{})
复制代码
  1. package models
  2. import (
  3.         "MY-FORUM/utils/errmsg"
  4.         "fmt"
  5.         "time"
  6.         "gorm.io/gorm"
  7. )
  8. type Community struct {
  9.         gorm.Model
  10.         CommunityName string `gorm:"type:varchar(64)" json:"community_name" validate:"required"`
  11.         Introduction  string `gorm:"type:varchar(256)" json:"introduction" validate:"required"`
  12.         Issure string `gorm:"type:varchar(256)" json:"issure" validate:"required"`
  13. }
  14. type CommunityDetail struct {
  15.         ID            int       `json:"id" db:"id"`
  16.         CommunityName string    `json:"community_name" db:"community_name"`
  17.         Introduction  string    `json:"introduction" db:"introduction"`
  18.         CreatedAt      time.Time `json:"created_at" db:"created_at"`
  19.         UpdatedAt     time.Time `json:"updated_at" db:"updated_at"`
  20.         Issure string `json:"issure" db:"issure"`
  21. }
  22. // 判断社区是否存在
  23. func FindCommunity(name string) bool {
  24.         var comm Community
  25.         err := db.Where("community_name = ?", name).First(&comm).Error
  26.         if err != nil {
  27.                 fmt.Println("社区查询失败", err)
  28.         }
  29.         if comm.ID > 0 {
  30.                 return true
  31.         }
  32.         return false
  33. }
  34. // 判断id是否存在
  35. func FindCommunityByID(id int)(bool,string){
  36.         var comm Community
  37.         err := db.Where("id = ?", id).First(&comm).Error
  38.         if err != nil {
  39.                 fmt.Println("社区查询失败", err)
  40.         }
  41.         if comm.ID > 0 {
  42.                 return true, comm.Issure
  43.         }
  44.         return false,""
  45. }
  46. // 添加社区
  47. func AddCommunity(comm Community) int {
  48.         err := db.Create(&comm).Error
  49.         if err!=nil{
  50.                 return errmsg.ERROR
  51.         }
  52.         return errmsg.SUCCES
  53. }
  54. // 修改社区
  55. func ChangeCommunity(comm Community)int{
  56.         mp := map[string]interface{}{}
  57.         mp["Community_name"] = comm.CommunityName
  58.         mp["introduction"] = comm.Introduction
  59.         err := db.Model(&comm).Updates(mp).Error
  60.         if err!=nil{
  61.                 return errmsg.ERROR
  62.         }
  63.         return errmsg.SUCCES
  64. }
  65. // 删除社区
  66. func DeleteCommunity(id int)int{
  67.         var comm Community
  68.         err := db.Where("id = ?", id).Delete(&comm).Error
  69.         if err!=nil{
  70.                 return errmsg.ERROR
  71.         }
  72.         return errmsg.SUCCES
  73. }
  74. // 查询社区列表(分页)
  75. func GetCommunityList(pagesize, pagenum int)([]Community, int){
  76.         var commList []Community
  77.         err := db.Select("id", "community_name").Limit(pagesize).Offset((pagenum-1)*pagesize).Find(&commList).Error
  78.         if err!=nil{
  79.                 return commList, errmsg.ERROR
  80.         }
  81.         return commList, errmsg.SUCCES
  82. }
  83. // 查询社区详情
  84. func GetCommunityDetail(id int)(CommunityDetail, int){
  85.         var comm CommunityDetail
  86.         err := db.Model(&Community{}).Where("id = ?",id).First(&comm).Error
  87.         if err!=nil{
  88.                 fmt.Println("社区详情查询失败", err)
  89.                 return comm, errmsg.ERROR
  90.         }
  91.         return comm, errmsg.SUCCES
  92. }
复制代码
增加社区种别(鉴权)

  1. // 添加社区
  2. func AddCommunity(c *gin.Context){
  3.         var community models.Community
  4.         _ = c.ShouldBind(&community)
  5.         // 获取中间件的username
  6.         username := c.GetString("username")
  7.         community.Issure = username
  8.         // 参数校验
  9.         errs := Validate.Struct(community)
  10.         if errs!=nil{
  11.                 msg := []string{}
  12.                 for _,err := range errs.(validator.ValidationErrors){
  13.                         msg = append(msg, err.Translate(Trans))
  14.                 }
  15.                 c.JSON(http.StatusOK, gin.H{
  16.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  17.                         "msg" : msg,
  18.                         "data" : community,
  19.                 })
  20.                 c.Abort()
  21.                 return
  22.         }
  23.         // 判断社区是否存在
  24.         if models.FindCommunity(community.CommunityName){
  25.                 c.JSON(http.StatusOK, gin.H{
  26.                         "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_EXIST),
  27.                         "data" : community,
  28.                 })
  29.                 c.Abort()
  30.                 return
  31.         }
  32.         // 添加社区
  33.         code := models.AddCommunity(community)
  34.         c.JSON(http.StatusOK, gin.H{
  35.                 "status" : errmsg.GetErrMsg(code),
  36.                 "data" : community,
  37.         })
  38. }
复制代码
获取社区分类列表

  1. func GetCommunityList(c *gin.Context){
  2.         pagesize,_ := strconv.Atoi(c.Query("pagesize"))
  3.         pagenum,_ := strconv.Atoi(c.Query("pagenum"))
  4.         if pagesize<=0{
  5.                 pagesize = -1
  6.         }
  7.         if pagenum<=1{
  8.                 pagenum = 1
  9.         }
  10.         commList, code := models.GetCommunityList(pagesize, pagenum)
  11.         c.JSON(http.StatusOK, gin.H{
  12.                 "status" : errmsg.GetErrMsg(code),
  13.                 "data" : commList,
  14.         })
  15. }
复制代码
根据社区ID查找分类详情

  1. // 查询单个社区详情
  2. func GetCommunityDetail(c *gin.Context){
  3.         id,_ := strconv.Atoi(c.Query("id"))
  4.         comm, code := models.GetCommunityDetail(id)
  5.         c.JSON(http.StatusOK, gin.H{
  6.                 "status" : errmsg.GetErrMsg(code),
  7.                 "data" : comm,
  8.         })
  9. }
复制代码
修改社区(鉴权)

  1. // 修改社区
  2. func ChangeCommunity(c *gin.Context){
  3.         var community models.Community
  4.         _ = c.ShouldBind(&community)
  5.         // 获取中间件的username
  6.         username := c.GetString("username")
  7.         community.Issure = username
  8.         // 参数校验
  9.         errs := Validate.Struct(community)
  10.         if errs!=nil{
  11.                 msg := []string{}
  12.                 for _,err := range errs.(validator.ValidationErrors){
  13.                         msg = append(msg, err.Translate(Trans))
  14.                 }
  15.                 c.JSON(http.StatusOK, gin.H{
  16.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  17.                         "msg" : msg,
  18.                         "data" : community,
  19.                 })
  20.                 c.Abort()
  21.                 return
  22.         }
  23.         // 判断id是否存在
  24.         flag, issure := models.FindCommunityByID(int(community.ID))
  25.         if !flag{
  26.                 c.JSON(http.StatusOK, gin.H{
  27.                         "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
  28.                         "data" : community,
  29.                 })
  30.                 c.Abort()
  31.                 return
  32.         }
  33.         // 判断是否为issure
  34.         if issure!=c.GetString("username"){
  35.                 c.JSON(http.StatusOK, gin.H{
  36.                         "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
  37.                         "data" : community,
  38.                 })
  39.                 c.Abort()
  40.                 return
  41.         }
  42.         // 判断社区名称是否存在
  43.         if models.FindCommunity(community.CommunityName){
  44.                 c.JSON(http.StatusOK, gin.H{
  45.                         "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_EXIST),
  46.                         "data" : community,
  47.                 })
  48.                 c.Abort()
  49.                 return
  50.         }
  51.         // 修改社区
  52.         code := models.ChangeCommunity(community)
  53.         c.JSON(http.StatusOK, gin.H{
  54.                 "status" : errmsg.GetErrMsg(code),
  55.                 "data" : community,
  56.         })
  57. }
复制代码
删除社区(鉴权)

  1. // 删除社区
  2. func DeleteCommunity(c *gin.Context){
  3.         id,_ :=strconv.Atoi(c.Query("id"))
  4.         // 判断id是否存在
  5.         flag, issure := models.FindCommunityByID(id)
  6.         if !flag{
  7.                 c.JSON(http.StatusOK, gin.H{
  8.                         "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
  9.                         "data" : id,
  10.                 })
  11.                 c.Abort()
  12.                 return
  13.         }
  14.         // 判断是否为issure
  15.         if issure!=c.GetString("username"){
  16.                 c.JSON(http.StatusOK, gin.H{
  17.                         "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
  18.                         "data" : id,
  19.                 })
  20.                 c.Abort()
  21.                 return
  22.         }
  23.         code := models.DeleteCommunity(id)
  24.         c.JSON(http.StatusOK, gin.H{
  25.                 "status" : errmsg.GetErrMsg(code),
  26.                 "data" : id,
  27.         })
  28. }
复制代码
论坛帖子模块

创建数据库

  1. package models
  2. import (
  3.         "MY-FORUM/utils/errmsg"
  4.         "gorm.io/gorm"
  5. )
  6. type Post struct {
  7.         gorm.Model
  8.         Title string `gorm:"type:varchar(64)" json:"title" validate:"required"`
  9.         Content string `gorm:"type:longtext" json:"content" validate:"required"`
  10.         AuthorName string `gorm:"type:varchar(64);not null" json:"author_name"`
  11.         CommunityId int `gorm:"type:bigint;not null" json:"community_id" validate:"required"`
  12.         Status int `gorm:"type:tinyint(4);default:0" json:"status"`
  13. }
  14. // 添加帖子数据
  15. func AddPost(post Post)int{
  16.         err := db.Create(&post).Error
  17.         if err!=nil{
  18.                 return errmsg.ERROR
  19.         }
  20.         return errmsg.SUCCES
  21. }
  22. // 分页展示帖子列表
  23. func GetPostList(pagesize, pagenum int)([]Post, int){
  24.         var posts []Post
  25.         err := db.Limit(pagesize).Offset((pagenum-1)*pagesize).Find(&posts).Error
  26.         if err!=nil{
  27.                 return posts, errmsg.ERROR
  28.         }
  29.         return posts, errmsg.SUCCES
  30. }
  31. // 获取帖子详情
  32. func GetPostDetail(id int)(Post, int){
  33.         var post Post
  34.         err := db.Where("id = ?", id).First(&post).Error
  35.         if err!=nil{
  36.                 return post, errmsg.ERROR
  37.         }
  38.         return post, errmsg.SUCCES
  39. }
  40. // 修改帖子
  41. func ChangePost(post Post)int{
  42.         mp := map[string]interface{}{}
  43.         mp["title"] = post.Title
  44.         mp["content"] = post.Content
  45.         mp["community_id"] = post.CommunityId
  46.         mp["status"] = post.Status
  47.         err := db.Model(&Post{}).Where("id = ?", post.ID).Updates(mp).Error
  48.         if err!=nil{
  49.                 return errmsg.ERROR
  50.         }
  51.         return errmsg.SUCCES
  52. }
  53. // 删除帖子
  54. func DeletePost(id int)int{
  55.         var post Post
  56.         err := db.Where("id = ?", id).Delete(&post).Error
  57.         if err!=nil{
  58.                 return errmsg.ERROR
  59.         }
  60.         return errmsg.SUCCES
  61. }
复制代码
  1. // 模型迁移
  2.         db.AutoMigrate(&User{},&Community{},&Post{})
复制代码
创建帖子

  1. func AddPost(c *gin.Context){
  2.         var post models.Post
  3.         _ = c.ShouldBind(&post)
  4.         post.AuthorName = c.GetString("username")
  5.         // 进行参数校验
  6.         err := Validate.Struct(post)
  7.         if err!=nil{
  8.                 msg := []string{}
  9.                 for _,err := range err.(validator.ValidationErrors){
  10.                         msg = append(msg, err.Translate(Trans))
  11.                 }
  12.                 c.JSON(http.StatusOK, gin.H{
  13.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  14.                         "msg" : msg,
  15.                         "data" : post,
  16.                 })
  17.                 c.Abort()
  18.                 return
  19.         }
  20.         // 判断社区是否存在
  21.         if flag,_ :=models.FindCommunityByID(post.CommunityId);!flag{
  22.                 c.JSON(http.StatusOK, gin.H{
  23.                         "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
  24.                         "data" : post,
  25.                 })
  26.                 c.Abort()
  27.                 return
  28.         }
  29.         // 创建帖子
  30.         code := models.AddPost(post)
  31.         if code!=errmsg.SUCCES{
  32.                 c.JSON(http.StatusOK, gin.H{
  33.                         "status" : errmsg.GetErrMsg(code),
  34.                         "data" : post,
  35.                 })
  36.                 c.Abort()
  37.                 return
  38.         }
  39.         // redis创建帖子
  40.         code = redis.CreatePost(int(post.ID), post.CommunityId, post.Title, post.Content, post.AuthorName)
  41.         c.JSON(http.StatusOK, gin.H{
  42.                 "status" : errmsg.GetErrMsg(code),
  43.                 "data" : post,
  44.         })
  45. }
复制代码
分页展示帖子列表

  1. func GetPostList(c *gin.Context){
  2.         pagesize,_ := strconv.Atoi(c.Query("pagesize"))
  3.         pagenum,_ := strconv.Atoi(c.Query("pagenum"))
  4.         if pagesize <= 0{
  5.                 pagesize = -1
  6.         }
  7.         if pagenum < 1{
  8.                 pagenum = 1
  9.         }
  10.         posts, code := models.GetPostList(pagesize, pagenum)
  11.         c.JSON(http.StatusOK, gin.H{
  12.                 "status" : errmsg.GetErrMsg(code),
  13.                 "data" : posts,
  14.         })
  15. }
复制代码
获取帖子详情

  1. func GetPostDetail(c *gin.Context){
  2.         id,_ :=strconv.Atoi(c.Query("post_id"))
  3.         _,code := models.GetPostDetail(id)
  4.         if code==errmsg.ERROR{
  5.                 c.JSON(http.StatusOK, gin.H{
  6.                         "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
  7.                 })
  8.                 c.Abort()
  9.                 return
  10.         }
  11.         post, code := models.GetPostDetail(id)
  12.         c.JSON(http.StatusOK, gin.H{
  13.                 "status" : errmsg.GetErrMsg(code),
  14.                 "data" : post,
  15.         })
  16. }
复制代码
修改帖子

  1. func ChangePost(c *gin.Context){
  2.         var post models.Post
  3.         _ = c.ShouldBind(&post)
  4.         // 进行参数校验
  5.         err := Validate.Struct(post)
  6.         if err!=nil{
  7.                 msg := []string{}
  8.                 for _,err := range err.(validator.ValidationErrors){
  9.                         msg = append(msg, err.Translate(Trans))
  10.                 }
  11.                 c.JSON(http.StatusOK, gin.H{
  12.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  13.                         "msg" : msg,
  14.                         "data" : post,
  15.                 })
  16.                 c.Abort()
  17.                 return
  18.         }
  19.         // 判断帖子是否存在
  20.         findPost,code := models.GetPostDetail(int(post.ID))
  21.         if code==errmsg.ERROR{
  22.                 c.JSON(http.StatusOK, gin.H{
  23.                         "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
  24.                         "data" : post,
  25.                 })
  26.                 c.Abort()
  27.                 return
  28.         }
  29.         // 判断是否为自己的帖子
  30.         if findPost.AuthorName != c.GetString("username"){
  31.                 c.JSON(http.StatusOK, gin.H{
  32.                         "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
  33.                         "data" : post,
  34.                 })
  35.                 c.Abort()
  36.                 return
  37.         }
  38.         // 判断社区是否存在
  39.         if flag,_ :=models.FindCommunityByID(post.CommunityId);!flag{
  40.                 c.JSON(http.StatusOK, gin.H{
  41.                         "status" : errmsg.GetErrMsg(errmsg.COMMUNITY_IS_NOT_EXIST),
  42.                         "data" : post,
  43.                 })
  44.                 c.Abort()
  45.                 return
  46.         }
  47.         // 修改帖子内容
  48.         code = models.ChangePost(post)
  49.         c.JSON(http.StatusOK, gin.H{
  50.                 "status" : errmsg.GetErrMsg(code),
  51.                 "data" : post,
  52.         })
  53. }
复制代码
删除帖子

  1. func DeletePost(c *gin.Context){
  2.         id,_ := strconv.Atoi(c.Query("post_id"))
  3.         // 判断帖子是否存在
  4.         findPost,code := models.GetPostDetail(id)
  5.         if code==errmsg.ERROR{
  6.                 c.JSON(http.StatusOK, gin.H{
  7.                         "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
  8.                         "post_id" : id,
  9.                 })
  10.                 c.Abort()
  11.                 return
  12.         }
  13.         // 判断是否为自己的帖子
  14.         if findPost.AuthorName != c.GetString("username"){
  15.                 c.JSON(http.StatusOK, gin.H{
  16.                         "status" : errmsg.GetErrMsg(errmsg.USER_NOT_TRUE),
  17.                         "data" : findPost,
  18.                 })
  19.                 c.Abort()
  20.                 return
  21.         }
  22.         // 删除帖子
  23.         code = models.DeletePost(id)
  24.         c.JSON(http.StatusOK, gin.H{
  25.                 "status" : errmsg.GetErrMsg(code),
  26.                 "data" : findPost,
  27.         })
  28. }
复制代码
按照时间or分数排序展示帖子列表

全部帖子

某一社区帖子

优化

redis缓存帖子列表

redis mysql 数据同等性

投票模块

redis数据库

  1. package redis
  2. import (
  3.         "MY-FORUM/config"
  4.         "fmt"
  5.         "github.com/go-redis/redis"
  6. )
  7. var (
  8.         client *redis.Client
  9. )
  10. func init(){
  11.         client = redis.NewClient(&redis.Options{
  12.                 Addr : config.RedisHost+":"+config.RedisPort,
  13.                 Password : "",
  14.                 DB : 0,
  15.                 PoolSize:8,
  16.                 MinIdleConns:1,
  17.         })
  18.         _,err :=client.Ping().Result()
  19.         if err!=nil{
  20.                 fmt.Println("redis连接失败", err)
  21.         }
  22. }
  23. func Close(){
  24.         _ = client.Close()
  25. }
复制代码
在redis中添加帖子相干信息

  1. // redis存储帖子信息
  2. func CreatePost(postid, community int, title, content, author_name string) int {
  3.         now := time.Now().Unix()
  4.         votedKey := PostVotedZSet+strconv.Itoa(postid)
  5.         communityKey := CommunityPostSet+strconv.Itoa(community)
  6.         // 事务操作
  7.         // 创建一个事务管道
  8.         pipeline := client.TxPipeline()
  9.         // 设置投票过期时间
  10.         pipeline.Expire(votedKey, time.Second*OneWekSecond)
  11.         // 添加帖子时间排序
  12.         pipeline.ZAdd(PostTimeZSet, redis.Z{
  13.                 Score: float64(now),
  14.                 Member: postid,
  15.         })
  16.         // 添加帖子分数排序
  17.         pipeline.ZAdd(PostScoreZSet, redis.Z{
  18.                 Score: float64(0),
  19.                 Member: postid,
  20.         })
  21.         // 添加到对应的社区
  22.         pipeline.SAdd(communityKey, postid)
  23.         // 执行管道中所有命令
  24.         _, err := pipeline.Exec()
  25.         if err!=nil{
  26.                 return errmsg.ERROR
  27.         }
  28.         return errmsg.SUCCES
  29. }
复制代码
为帖子投票

  1. func PostVoteHandler(c *gin.Context) {
  2.         var item PostVote
  3.         _ = c.ShouldBind(&item)
  4.         // 进行参数校验
  5.         errs := Validate.Struct(item)
  6.         if errs!=nil{
  7.                 msg := []string{}
  8.                 for _,err := range errs.(validator.ValidationErrors){
  9.                         msg = append(msg, err.Translate(Trans))
  10.                 }
  11.                 c.JSON(http.StatusOK, gin.H{
  12.                         "status" : errmsg.GetErrMsg(errmsg.ERROR),
  13.                         "msg" : msg,
  14.                         "data" : item,
  15.                 })
  16.                 c.Abort()
  17.                 return
  18.         }
  19.         // 判断帖子是否存在
  20.         post_id,_ := strconv.Atoi(item.PostId)
  21.         _,code := models.GetPostDetail(post_id)
  22.         if code==errmsg.ERROR{
  23.                 c.JSON(http.StatusOK, gin.H{
  24.                         "status" : errmsg.GetErrMsg(errmsg.POST_IS_NOT_EXIST),
  25.                         "data" : item,
  26.                 })
  27.                 c.Abort()
  28.                 return
  29.         }
  30.         // 进行投票
  31.         code = redis.VoteForPost(c.GetString("username"), item.PostId, float64(item.Direction))
  32.         c.JSON(http.StatusOK, gin.H{
  33.                 "status": errmsg.GetErrMsg(code),
  34.                 "data": item,
  35.         })
  36. }
复制代码
  1. // 为帖子投票
  2. func VoteForPost(username, postid string, v float64)int {
  3.         // 判断现在是否还在投票期
  4.         // 获取帖子发布日期
  5.         postTime := client.ZScore(PostTimeZSet, string(postid)).Val()
  6.         if float64(time.Now().Unix()-int64(postTime))>OneWekSecond{
  7.                 return errmsg.POST_VOTE_IS_EXPIRE
  8.         }
  9.         key := PostVotedZSet+postid
  10.         // 查看当前用户给帖子的投票记录
  11.         ov := client.ZScore(key, username).Val()
  12.         // 判断是否重复投票
  13.         if ov==v{
  14.                 return errmsg.POST_USER_VOTED
  15.         }
  16.         // 计算两次投票的差值
  17.         diff := v-ov
  18.         // 创建一个事务管道
  19.         pipeline := client.TxPipeline()
  20.         // 更新帖子分数
  21.         pipeline.ZIncrBy(PostScoreZSet, VoteScore*diff, postid)
  22.         // 更新用户投票记录
  23.         pipeline.ZRem(key, username)
  24.         pipeline.ZAdd(key, redis.Z{
  25.                 Score: v,
  26.                 Member: username,
  27.         })
  28.         _,err := pipeline.Exec()
  29.         if err!=nil{
  30.                 return errmsg.ERROR
  31.         }
  32.         return errmsg.SUCCES
  33. }
复制代码
优化

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

评论模块


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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

雁过留声

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表