From 03172c5072a24f0d78f3f3339d95feac448384fc Mon Sep 17 00:00:00 2001 From: Crimson Date: Wed, 20 Nov 2024 00:09:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B8=96=E5=AD=90=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/controller/post_controller.go | 46 +++++++++++---- api/controller/tag_controller.go | 66 +++++++++++++++++++++ api/reply/post_reply.go | 14 +++-- api/request/post_request.go | 13 ++++- api/request/tag_request.go | 10 ++++ api/router/router.go | 22 +++++-- dal/dao/post_dao.go | 15 ++++- dal/dao/tag_dao.go | 19 ++++++ dal/init_db.go | 13 ++++- dal/model/category_model.go | 1 + dal/model/post_model.go | 19 +++--- dal/model/tag_model.go | 12 ++++ logic/appservice/post_service.go | 68 +++++++++++++++++++--- logic/appservice/tag_service.go | 46 +++++++++++++++ logic/domainservice/post_domain_service.go | 12 ++-- logic/domainservice/tag_domain_service.go | 35 +++++++++++ 16 files changed, 363 insertions(+), 48 deletions(-) create mode 100644 api/controller/tag_controller.go create mode 100644 api/request/tag_request.go create mode 100644 dal/dao/tag_dao.go create mode 100644 dal/model/tag_model.go create mode 100644 logic/appservice/tag_service.go create mode 100644 logic/domainservice/tag_domain_service.go diff --git a/api/controller/post_controller.go b/api/controller/post_controller.go index de0956c..82873c3 100644 --- a/api/controller/post_controller.go +++ b/api/controller/post_controller.go @@ -1,3 +1,4 @@ +// file name: post_controller.go package controller import ( @@ -5,6 +6,7 @@ import ( "bbs-backend/api/request" "bbs-backend/common/errcode" "bbs-backend/common/response" + "bbs-backend/dal/model" "bbs-backend/logic/appservice" "github.com/gin-gonic/gin" "net/http" @@ -18,7 +20,7 @@ func CreatePost(c *gin.Context) { return } - post, err := appservice.CreatePost(req) + post, err := appservice.CreatePost(c, req) if err != nil { response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError) return @@ -50,15 +52,26 @@ func GetPost(c *gin.Context) { } response.Success(c, reply.GetPostReply{ - ID: post.ID, - Title: post.Title, - Content: post.Content, - Author: post.Author.Username, + ID: post.ID, + Title: post.Title, + Content: post.Content, + 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) { - 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 { response.Error(c, http.StatusInternalServerError, errcode.ErrInternalServerError) return @@ -67,10 +80,15 @@ func GetPosts(c *gin.Context) { var postReplies []reply.GetPostReply for _, post := range posts { postReplies = append(postReplies, reply.GetPostReply{ - ID: post.ID, - Title: post.Title, - Content: post.Content, - Author: post.Author.Username, + ID: post.ID, + Title: post.Title, + Content: post.Content, + 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, }) } + +func getTagNames(tags []model.Tag) []string { + var tagNames []string + for _, tag := range tags { + tagNames = append(tagNames, tag.Name) + } + return tagNames +} diff --git a/api/controller/tag_controller.go b/api/controller/tag_controller.go new file mode 100644 index 0000000..eecd893 --- /dev/null +++ b/api/controller/tag_controller.go @@ -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"}) +} diff --git a/api/reply/post_reply.go b/api/reply/post_reply.go index 1c1fc60..0216329 100644 --- a/api/reply/post_reply.go +++ b/api/reply/post_reply.go @@ -1,3 +1,4 @@ +// file name: post_reply.go package reply type CreatePostReply struct { @@ -7,10 +8,15 @@ type CreatePostReply struct { } type GetPostReply struct { - ID uint `json:"id"` - Title string `json:"title"` - Content string `json:"content"` - Author string `json:"author"` + ID uint `json:"id"` + Title string `json:"title"` + Content string `json:"content"` + 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 { diff --git a/api/request/post_request.go b/api/request/post_request.go index f1554fe..32d46d3 100644 --- a/api/request/post_request.go +++ b/api/request/post_request.go @@ -1,6 +1,15 @@ +// file name: post_request.go package request type CreatePostRequest struct { - Title string `json:"title" binding:"required"` - Content string `json:"content" binding:"required"` + Title string `json:"title" 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"` } diff --git a/api/request/tag_request.go b/api/request/tag_request.go new file mode 100644 index 0000000..389ef2e --- /dev/null +++ b/api/request/tag_request.go @@ -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"` +} diff --git a/api/router/router.go b/api/router/router.go index e1e501b..748e8de 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -9,18 +9,28 @@ import ( func SetupRouter() *gin.Engine { r := gin.Default() + // User 相关路由 r.POST("/register", controller.Register) r.POST("/login", controller.Login) 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.GET("/posts", controller.GetPosts) r.GET("/posts/:id", controller.GetPost) - 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) // 验证邮箱 - r.GET("/bbs-info", controller.GetBBSInfo) // 获取论坛信息 - r.PUT("/bbs-info", controller.UpdateBBSInfo) // 更新论坛信息 + + // BBS 相关路由 + 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 } diff --git a/dal/dao/post_dao.go b/dal/dao/post_dao.go index 629c170..2d24c08 100644 --- a/dal/dao/post_dao.go +++ b/dal/dao/post_dao.go @@ -1,6 +1,8 @@ +// file name: post_dao.go package dao import ( + "bbs-backend/api/request" "bbs-backend/dal" "bbs-backend/dal/model" ) @@ -11,16 +13,23 @@ func CreatePost(post *model.Post) error { func GetPostByID(postID uint) (*model.Post, error) { 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 { return nil, err } return &post, nil } -func GetAllPosts() ([]*model.Post, error) { +func GetPosts(req request.GetPostsRequest) ([]*model.Post, error) { 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 { return nil, err } diff --git a/dal/dao/tag_dao.go b/dal/dao/tag_dao.go new file mode 100644 index 0000000..643ff7f --- /dev/null +++ b/dal/dao/tag_dao.go @@ -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 +} diff --git a/dal/init_db.go b/dal/init_db.go index f07c186..fa85d35 100644 --- a/dal/init_db.go +++ b/dal/init_db.go @@ -1,3 +1,4 @@ +// file name: init_db.go package dal import ( @@ -18,7 +19,7 @@ func MigrateAndSeedDB() { InitDB() // 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 { log.Fatalf("Failed to migrate database schema: %v", err) } @@ -39,9 +40,11 @@ func MigrateAndSeedDB() { var userCount int64 var categoryCount int64 var configCount int64 + var tagCount int64 DB.Model(&model.User{}).Where("username = ?", "admin").Count(&userCount) DB.Model(&model.Category{}).Where("name = ?", "General").Count(&categoryCount) DB.Model(&model.BBSConfig{}).Count(&configCount) + DB.Model(&model.Tag{}).Where("name = ?", "default").Count(&tagCount) if userCount == 0 { // 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) + } + } } diff --git a/dal/model/category_model.go b/dal/model/category_model.go index c1bbc55..e7f2e42 100644 --- a/dal/model/category_model.go +++ b/dal/model/category_model.go @@ -1,3 +1,4 @@ +// file name: category_model.go package model import ( diff --git a/dal/model/post_model.go b/dal/model/post_model.go index f27e2d5..6fb7413 100644 --- a/dal/model/post_model.go +++ b/dal/model/post_model.go @@ -1,3 +1,4 @@ +// file name: post_model.go package model import ( @@ -6,11 +7,15 @@ import ( type Post struct { gorm.Model - Title string - Content string - AuthorID uint - Author User - CategoryID uint - Category Category - Comments []Comment + Title string + Content string + AuthorID uint + Author User + CategoryID uint + Category Category + Tags []Tag `gorm:"many2many:post_tags;"` + LikesCount int `gorm:"default:0"` + CommentsCount int `gorm:"default:0"` + ViewsCount int `gorm:"default:0"` + Comments []Comment } diff --git a/dal/model/tag_model.go b/dal/model/tag_model.go new file mode 100644 index 0000000..e009c05 --- /dev/null +++ b/dal/model/tag_model.go @@ -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"` +} diff --git a/logic/appservice/post_service.go b/logic/appservice/post_service.go index 6f11fdc..fb690fe 100644 --- a/logic/appservice/post_service.go +++ b/logic/appservice/post_service.go @@ -1,20 +1,44 @@ +// file name: post_service.go package appservice import ( "bbs-backend/api/request" "bbs-backend/common/errcode" + "bbs-backend/dal" "bbs-backend/dal/model" "bbs-backend/logic/domainservice" + "github.com/dgrijalva/jwt-go" + "github.com/gin-gonic/gin" + "strings" ) -func CreatePost(req request.CreatePostRequest) (*model.Post, error) { - post := &model.Post{ - Title: req.Title, - Content: req.Content, - AuthorID: 1, // 假设当前用户ID为1 +func CreatePost(c *gin.Context, req request.CreatePostRequest) (*model.Post, error) { + // 获取当前用户的 ID + userID, err := getUserIDFromToken(c) + if err != nil { + 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 { return nil, errcode.ErrInternalServerError } @@ -22,6 +46,34 @@ func CreatePost(req request.CreatePostRequest) (*model.Post, error) { 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) { post, err := domainservice.GetPostDomainService(postID) if err != nil { @@ -30,8 +82,8 @@ func GetPost(postID uint) (*model.Post, error) { return post, nil } -func GetPosts() ([]*model.Post, error) { - posts, err := domainservice.GetPostsDomainService() +func GetPosts(req request.GetPostsRequest) ([]*model.Post, error) { + posts, err := domainservice.GetPostsDomainService(req) if err != nil { return nil, errcode.ErrInternalServerError } diff --git a/logic/appservice/tag_service.go b/logic/appservice/tag_service.go new file mode 100644 index 0000000..0c6ee47 --- /dev/null +++ b/logic/appservice/tag_service.go @@ -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 +} diff --git a/logic/domainservice/post_domain_service.go b/logic/domainservice/post_domain_service.go index 56fc236..e7e8f24 100644 --- a/logic/domainservice/post_domain_service.go +++ b/logic/domainservice/post_domain_service.go @@ -1,11 +1,11 @@ +// file name: post_domain_service.go package domainservice import ( + "bbs-backend/api/request" "bbs-backend/common/errcode" "bbs-backend/dal/dao" "bbs-backend/dal/model" - "errors" - "gorm.io/gorm" ) func CreatePostDomainService(post *model.Post) error { @@ -17,6 +17,7 @@ func CreatePostDomainService(post *model.Post) error { return errcode.ErrInvalidContent } + // 创建帖子 err := dao.CreatePost(post) if err != nil { return errcode.ErrInternalServerError @@ -28,17 +29,14 @@ func CreatePostDomainService(post *model.Post) error { func GetPostDomainService(postID uint) (*model.Post, error) { post, err := dao.GetPostByID(postID) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, errcode.ErrPostNotFound - } return nil, errcode.ErrInternalServerError } return post, nil } -func GetPostsDomainService() ([]*model.Post, error) { - posts, err := dao.GetAllPosts() +func GetPostsDomainService(req request.GetPostsRequest) ([]*model.Post, error) { + posts, err := dao.GetPosts(req) if err != nil { return nil, errcode.ErrInternalServerError } diff --git a/logic/domainservice/tag_domain_service.go b/logic/domainservice/tag_domain_service.go new file mode 100644 index 0000000..e3dd3d5 --- /dev/null +++ b/logic/domainservice/tag_domain_service.go @@ -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 +}