更新帖子相关功能

This commit is contained in:
一二三 2024-11-20 00:09:17 +08:00
parent 92b93e6037
commit 03172c5072
16 changed files with 363 additions and 48 deletions

View File

@ -1,3 +1,4 @@
// file name: post_controller.go
package controller package controller
import ( import (
@ -5,6 +6,7 @@ import (
"bbs-backend/api/request" "bbs-backend/api/request"
"bbs-backend/common/errcode" "bbs-backend/common/errcode"
"bbs-backend/common/response" "bbs-backend/common/response"
"bbs-backend/dal/model"
"bbs-backend/logic/appservice" "bbs-backend/logic/appservice"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
@ -18,7 +20,7 @@ func CreatePost(c *gin.Context) {
return return
} }
post, err := appservice.CreatePost(req) post, err := appservice.CreatePost(c, req)
if err != nil { if err != nil {
response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError) response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError)
return return
@ -50,15 +52,26 @@ func GetPost(c *gin.Context) {
} }
response.Success(c, reply.GetPostReply{ response.Success(c, reply.GetPostReply{
ID: post.ID, ID: post.ID,
Title: post.Title, Title: post.Title,
Content: post.Content, Content: post.Content,
Author: post.Author.Username, Author: post.Author.Username,
Category: post.Category.Name,
Tags: getTagNames(post.Tags),
LikesCount: post.LikesCount,
CommentsCount: post.CommentsCount,
ViewsCount: post.ViewsCount,
}) })
} }
func GetPosts(c *gin.Context) { func GetPosts(c *gin.Context) {
posts, err := appservice.GetPosts() var req request.GetPostsRequest
if err := c.ShouldBindQuery(&req); err != nil {
response.Error(c, http.StatusBadRequest, errcode.ErrBadRequest)
return
}
posts, err := appservice.GetPosts(req)
if err != nil { if err != nil {
response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError) response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError)
return return
@ -67,10 +80,15 @@ func GetPosts(c *gin.Context) {
var postReplies []reply.GetPostReply var postReplies []reply.GetPostReply
for _, post := range posts { for _, post := range posts {
postReplies = append(postReplies, reply.GetPostReply{ postReplies = append(postReplies, reply.GetPostReply{
ID: post.ID, ID: post.ID,
Title: post.Title, Title: post.Title,
Content: post.Content, Content: post.Content,
Author: post.Author.Username, Author: post.Author.Username,
Category: post.Category.Name,
Tags: getTagNames(post.Tags),
LikesCount: post.LikesCount,
CommentsCount: post.CommentsCount,
ViewsCount: post.ViewsCount,
}) })
} }
@ -78,3 +96,11 @@ func GetPosts(c *gin.Context) {
Posts: postReplies, Posts: postReplies,
}) })
} }
func getTagNames(tags []model.Tag) []string {
var tagNames []string
for _, tag := range tags {
tagNames = append(tagNames, tag.Name)
}
return tagNames
}

View File

@ -0,0 +1,66 @@
// file name: tag_controller.go
package controller
import (
"bbs-backend/api/request"
"bbs-backend/common/errcode"
"bbs-backend/common/response"
"bbs-backend/logic/appservice"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
)
func CreateTag(c *gin.Context) {
var req request.CreateTagRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, errcode.ErrBadRequest)
return
}
tag, err := appservice.CreateTag(req)
if err != nil {
response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError)
return
}
response.Success(c, gin.H{"id": tag.ID, "name": tag.Name})
}
func UpdateTag(c *gin.Context) {
tagID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.Error(c, http.StatusBadRequest, errcode.ErrBadRequest)
return
}
var req request.UpdateTagRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, errcode.ErrBadRequest)
return
}
err = appservice.UpdateTag(uint(tagID), req)
if err != nil {
response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError)
return
}
response.Success(c, gin.H{"message": "Tag updated successfully"})
}
func DeleteTag(c *gin.Context) {
tagID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.Error(c, http.StatusBadRequest, errcode.ErrBadRequest)
return
}
err = appservice.DeleteTag(uint(tagID))
if err != nil {
response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError)
return
}
response.Success(c, gin.H{"message": "Tag deleted successfully"})
}

View File

@ -1,3 +1,4 @@
// file name: post_reply.go
package reply package reply
type CreatePostReply struct { type CreatePostReply struct {
@ -7,10 +8,15 @@ type CreatePostReply struct {
} }
type GetPostReply struct { type GetPostReply struct {
ID uint `json:"id"` ID uint `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Content string `json:"content"` Content string `json:"content"`
Author string `json:"author"` Author string `json:"author"`
Category string `json:"category"`
Tags []string `json:"tags"`
LikesCount int `json:"likes_count"`
CommentsCount int `json:"comments_count"`
ViewsCount int `json:"views_count"`
} }
type GetPostsReply struct { type GetPostsReply struct {

View File

@ -1,6 +1,15 @@
// file name: post_request.go
package request package request
type CreatePostRequest struct { type CreatePostRequest struct {
Title string `json:"title" binding:"required"` Title string `json:"title" binding:"required"`
Content string `json:"content" binding:"required"` Content string `json:"content" binding:"required"`
CategoryID uint `json:"category_id" binding:"required"`
Tags []string `json:"tags"`
}
type GetPostsRequest struct {
Page int `form:"page" binding:"required"`
PageSize int `form:"pageSize" binding:"required"`
Category string `form:"category"`
} }

View File

@ -0,0 +1,10 @@
// file name: tag_request.go
package request
type CreateTagRequest struct {
Name string `json:"name" binding:"required"`
}
type UpdateTagRequest struct {
Name string `json:"name" binding:"required"`
}

View File

@ -9,18 +9,28 @@ import (
func SetupRouter() *gin.Engine { func SetupRouter() *gin.Engine {
r := gin.Default() r := gin.Default()
// User 相关路由
r.POST("/register", controller.Register) r.POST("/register", controller.Register)
r.POST("/login", controller.Login) r.POST("/login", controller.Login)
r.GET("/users/:id", controller.GetUserInfo) r.GET("/users/:id", controller.GetUserInfo)
r.PUT("/update-user-info", controller.UpdateUserInfo)
r.POST("/upload-avatar", controller.UploadAvatar)
r.POST("/send-verification-code", controller.SendVerificationCode)
r.POST("/verify-email", controller.VerifyEmail)
// Post 相关路由
r.POST("/posts", controller.CreatePost) r.POST("/posts", controller.CreatePost)
r.GET("/posts", controller.GetPosts) r.GET("/posts", controller.GetPosts)
r.GET("/posts/:id", controller.GetPost) r.GET("/posts/:id", controller.GetPost)
r.PUT("/update-user-info", controller.UpdateUserInfo) // 更新用户信息
r.POST("/upload-avatar", controller.UploadAvatar) // 上传头像 // BBS 相关路由
r.POST("/send-verification-code", controller.SendVerificationCode) // 发送验证码 r.GET("/bbs-info", controller.GetBBSInfo)
r.POST("/verify-email", controller.VerifyEmail) // 验证邮箱 r.PUT("/bbs-info", controller.UpdateBBSInfo)
r.GET("/bbs-info", controller.GetBBSInfo) // 获取论坛信息
r.PUT("/bbs-info", controller.UpdateBBSInfo) // 更新论坛信息 // Tag 相关路由
r.POST("/tags", controller.CreateTag)
r.PUT("/tags/:id", controller.UpdateTag)
r.DELETE("/tags/:id", controller.DeleteTag)
return r return r
} }

View File

@ -1,6 +1,8 @@
// file name: post_dao.go
package dao package dao
import ( import (
"bbs-backend/api/request"
"bbs-backend/dal" "bbs-backend/dal"
"bbs-backend/dal/model" "bbs-backend/dal/model"
) )
@ -11,16 +13,23 @@ func CreatePost(post *model.Post) error {
func GetPostByID(postID uint) (*model.Post, error) { func GetPostByID(postID uint) (*model.Post, error) {
var post model.Post var post model.Post
err := dal.DB.Preload("Author").First(&post, postID).Error err := dal.DB.Preload("Author").Preload("Category").Preload("Tags").First(&post, postID).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &post, nil return &post, nil
} }
func GetAllPosts() ([]*model.Post, error) { func GetPosts(req request.GetPostsRequest) ([]*model.Post, error) {
var posts []*model.Post var posts []*model.Post
err := dal.DB.Preload("Author").Find(&posts).Error query := dal.DB.Preload("Author").Preload("Category").Preload("Tags")
if req.Category != "" {
query = query.Joins("JOIN categories ON posts.category_id = categories.id").Where("categories.name = ?", req.Category)
}
offset := (req.Page - 1) * req.PageSize
err := query.Offset(offset).Limit(req.PageSize).Find(&posts).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }

19
dal/dao/tag_dao.go Normal file
View File

@ -0,0 +1,19 @@
// file name: tag_dao.go
package dao
import (
"bbs-backend/dal"
"bbs-backend/dal/model"
)
func CreateTag(tag *model.Tag) error {
return dal.DB.Create(tag).Error
}
func UpdateTag(tag *model.Tag) error {
return dal.DB.Save(tag).Error
}
func DeleteTag(tagID uint) error {
return dal.DB.Delete(&model.Tag{}, tagID).Error
}

View File

@ -1,3 +1,4 @@
// file name: init_db.go
package dal package dal
import ( import (
@ -18,7 +19,7 @@ func MigrateAndSeedDB() {
InitDB() InitDB()
// Migrate the schema // Migrate the schema
err := DB.AutoMigrate(&model.User{}, &model.Post{}, &model.Comment{}, &model.Category{}, &model.Role{}, &model.Session{}, &model.BBSConfig{}) err := DB.AutoMigrate(&model.User{}, &model.Post{}, &model.Comment{}, &model.Category{}, &model.Role{}, &model.Session{}, &model.BBSConfig{}, &model.Tag{})
if err != nil { if err != nil {
log.Fatalf("Failed to migrate database schema: %v", err) log.Fatalf("Failed to migrate database schema: %v", err)
} }
@ -39,9 +40,11 @@ func MigrateAndSeedDB() {
var userCount int64 var userCount int64
var categoryCount int64 var categoryCount int64
var configCount int64 var configCount int64
var tagCount int64
DB.Model(&model.User{}).Where("username = ?", "admin").Count(&userCount) DB.Model(&model.User{}).Where("username = ?", "admin").Count(&userCount)
DB.Model(&model.Category{}).Where("name = ?", "General").Count(&categoryCount) DB.Model(&model.Category{}).Where("name = ?", "General").Count(&categoryCount)
DB.Model(&model.BBSConfig{}).Count(&configCount) DB.Model(&model.BBSConfig{}).Count(&configCount)
DB.Model(&model.Tag{}).Where("name = ?", "default").Count(&tagCount)
if userCount == 0 { if userCount == 0 {
// Seed initial user data // Seed initial user data
@ -103,4 +106,12 @@ func MigrateAndSeedDB() {
} }
} }
} }
if tagCount == 0 {
// Seed initial tag data
tag := model.Tag{Name: "default"}
if err := DB.FirstOrCreate(&tag, model.Tag{Name: tag.Name}).Error; err != nil {
log.Fatalf("Failed to seed tag: %v", err)
}
}
} }

View File

@ -1,3 +1,4 @@
// file name: category_model.go
package model package model
import ( import (

View File

@ -1,3 +1,4 @@
// file name: post_model.go
package model package model
import ( import (
@ -6,11 +7,15 @@ import (
type Post struct { type Post struct {
gorm.Model gorm.Model
Title string Title string
Content string Content string
AuthorID uint AuthorID uint
Author User Author User
CategoryID uint CategoryID uint
Category Category Category Category
Comments []Comment Tags []Tag `gorm:"many2many:post_tags;"`
LikesCount int `gorm:"default:0"`
CommentsCount int `gorm:"default:0"`
ViewsCount int `gorm:"default:0"`
Comments []Comment
} }

12
dal/model/tag_model.go Normal file
View File

@ -0,0 +1,12 @@
// file name: tag_model.go
package model
import (
"gorm.io/gorm"
)
type Tag struct {
gorm.Model
Name string `gorm:"type:varchar(100);unique;not null" json:"name"`
Posts []Post `gorm:"many2many:post_tags;" json:"posts"`
}

View File

@ -1,20 +1,44 @@
// file name: post_service.go
package appservice package appservice
import ( import (
"bbs-backend/api/request" "bbs-backend/api/request"
"bbs-backend/common/errcode" "bbs-backend/common/errcode"
"bbs-backend/dal"
"bbs-backend/dal/model" "bbs-backend/dal/model"
"bbs-backend/logic/domainservice" "bbs-backend/logic/domainservice"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"strings"
) )
func CreatePost(req request.CreatePostRequest) (*model.Post, error) { func CreatePost(c *gin.Context, req request.CreatePostRequest) (*model.Post, error) {
post := &model.Post{ // 获取当前用户的 ID
Title: req.Title, userID, err := getUserIDFromToken(c)
Content: req.Content, if err != nil {
AuthorID: 1, // 假设当前用户ID为1 return nil, errcode.ErrUnauthorized
} }
err := domainservice.CreatePostDomainService(post) post := &model.Post{
Title: req.Title,
Content: req.Content,
AuthorID: userID,
CategoryID: req.CategoryID,
}
// 处理标签
var tags []model.Tag
for _, tagName := range req.Tags {
var tag model.Tag
// 检查标签是否存在,如果不存在则创建
if err := dal.DB.Where("name = ?", tagName).FirstOrCreate(&tag, model.Tag{Name: tagName}).Error; err != nil {
return nil, errcode.ErrInternalServerError
}
tags = append(tags, tag)
}
post.Tags = tags
err = domainservice.CreatePostDomainService(post)
if err != nil { if err != nil {
return nil, errcode.ErrInternalServerError return nil, errcode.ErrInternalServerError
} }
@ -22,6 +46,34 @@ func CreatePost(req request.CreatePostRequest) (*model.Post, error) {
return post, nil return post, nil
} }
func getUserIDFromToken(c *gin.Context) (uint, error) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
return 0, errcode.ErrUnauthorized
}
tokenString := strings.Replace(authHeader, "Bearer ", "", 1)
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("your_secret_key"), nil
})
if err != nil {
return 0, errcode.ErrUnauthorized
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid {
return 0, errcode.ErrUnauthorized
}
userID, ok := claims["user_id"].(float64)
if !ok {
return 0, errcode.ErrUnauthorized
}
return uint(userID), nil
}
func GetPost(postID uint) (*model.Post, error) { func GetPost(postID uint) (*model.Post, error) {
post, err := domainservice.GetPostDomainService(postID) post, err := domainservice.GetPostDomainService(postID)
if err != nil { if err != nil {
@ -30,8 +82,8 @@ func GetPost(postID uint) (*model.Post, error) {
return post, nil return post, nil
} }
func GetPosts() ([]*model.Post, error) { func GetPosts(req request.GetPostsRequest) ([]*model.Post, error) {
posts, err := domainservice.GetPostsDomainService() posts, err := domainservice.GetPostsDomainService(req)
if err != nil { if err != nil {
return nil, errcode.ErrInternalServerError return nil, errcode.ErrInternalServerError
} }

View File

@ -0,0 +1,46 @@
// file name: tag_service.go
package appservice
import (
"bbs-backend/api/request"
"bbs-backend/common/errcode"
"bbs-backend/dal/model"
"bbs-backend/logic/domainservice"
"gorm.io/gorm"
)
func CreateTag(req request.CreateTagRequest) (*model.Tag, error) {
tag := &model.Tag{
Name: req.Name,
}
err := domainservice.CreateTagDomainService(tag)
if err != nil {
return nil, errcode.ErrInternalServerError
}
return tag, nil
}
func UpdateTag(tagID uint, req request.UpdateTagRequest) error {
tag := &model.Tag{
Model: gorm.Model{ID: tagID},
Name: req.Name,
}
err := domainservice.UpdateTagDomainService(tag)
if err != nil {
return errcode.ErrInternalServerError
}
return nil
}
func DeleteTag(tagID uint) error {
err := domainservice.DeleteTagDomainService(tagID)
if err != nil {
return errcode.ErrInternalServerError
}
return nil
}

View File

@ -1,11 +1,11 @@
// file name: post_domain_service.go
package domainservice package domainservice
import ( import (
"bbs-backend/api/request"
"bbs-backend/common/errcode" "bbs-backend/common/errcode"
"bbs-backend/dal/dao" "bbs-backend/dal/dao"
"bbs-backend/dal/model" "bbs-backend/dal/model"
"errors"
"gorm.io/gorm"
) )
func CreatePostDomainService(post *model.Post) error { func CreatePostDomainService(post *model.Post) error {
@ -17,6 +17,7 @@ func CreatePostDomainService(post *model.Post) error {
return errcode.ErrInvalidContent return errcode.ErrInvalidContent
} }
// 创建帖子
err := dao.CreatePost(post) err := dao.CreatePost(post)
if err != nil { if err != nil {
return errcode.ErrInternalServerError return errcode.ErrInternalServerError
@ -28,17 +29,14 @@ func CreatePostDomainService(post *model.Post) error {
func GetPostDomainService(postID uint) (*model.Post, error) { func GetPostDomainService(postID uint) (*model.Post, error) {
post, err := dao.GetPostByID(postID) post, err := dao.GetPostByID(postID)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errcode.ErrPostNotFound
}
return nil, errcode.ErrInternalServerError return nil, errcode.ErrInternalServerError
} }
return post, nil return post, nil
} }
func GetPostsDomainService() ([]*model.Post, error) { func GetPostsDomainService(req request.GetPostsRequest) ([]*model.Post, error) {
posts, err := dao.GetAllPosts() posts, err := dao.GetPosts(req)
if err != nil { if err != nil {
return nil, errcode.ErrInternalServerError return nil, errcode.ErrInternalServerError
} }

View File

@ -0,0 +1,35 @@
// file name: tag_domain_service.go
package domainservice
import (
"bbs-backend/common/errcode"
"bbs-backend/dal/dao"
"bbs-backend/dal/model"
)
func CreateTagDomainService(tag *model.Tag) error {
err := dao.CreateTag(tag)
if err != nil {
return errcode.ErrInternalServerError
}
return nil
}
func UpdateTagDomainService(tag *model.Tag) error {
err := dao.UpdateTag(tag)
if err != nil {
return errcode.ErrInternalServerError
}
return nil
}
func DeleteTagDomainService(tagID uint) error {
err := dao.DeleteTag(tagID)
if err != nil {
return errcode.ErrInternalServerError
}
return nil
}