搭建个人论坛
项目地址:MyForum: go+gin+vue搭建论坛 - Gitee.com
PS:有些地方没有写好,请选择性查看
初始化项目
- [server]
- AppMode = debug
- HttpPort = :3000
- JwtKey = "dhjasdkajh321"
- [database]
- Db = mysql
- DbHost = localhost
- DbPort = 3306
- DbUser = root
- DbPassWord = flzx3qc
- DbName = gForum
复制代码- package config
- import (
- "fmt"
- "gopkg.in/ini.v1"
- )
- var (
- AppMode string
- HttpPort string
- 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("")
- }
复制代码- 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"`
- }
复制代码 查询用户名是否已经存在
- // 判断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
- }
复制代码- 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
- }
复制代码- 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
- }
复制代码- // 登录
- 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[7:]
- 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[7:]
- 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"`
- Introduction string `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"`
- Introduction string `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 := map[string]interface{}{}
- 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 := map[string]interface{}{}
- 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企服之家,中国第一个企服评测及商务社交产业平台。 |