diff --git a/cmd/server/wire.go b/cmd/server/wire.go index a58c7a7..6594311 100644 --- a/cmd/server/wire.go +++ b/cmd/server/wire.go @@ -5,6 +5,7 @@ package main import ( "carrot_bbs/internal/handler" + "carrot_bbs/internal/repository" "carrot_bbs/internal/router" "carrot_bbs/internal/service" appwire "carrot_bbs/internal/wire" @@ -24,6 +25,7 @@ func InitializeApp() (*App, error) { // ProvideRouter 提供路由 func ProvideRouter( + userRepo repository.UserRepository, userHandler *handler.UserHandler, postHandler *handler.PostHandler, commentHandler *handler.CommentHandler, @@ -58,6 +60,7 @@ func ProvideRouter( wsHandler *handler.WSHandler, ) *router.Router { return router.New( + userRepo, userHandler, postHandler, commentHandler, diff --git a/cmd/server/wire_gen.go b/cmd/server/wire_gen.go index af64fec..7832846 100644 --- a/cmd/server/wire_gen.go +++ b/cmd/server/wire_gen.go @@ -132,8 +132,8 @@ func InitializeApp() (*App, error) { verificationHandler := handler.NewVerificationHandler(verificationService) adminVerificationService := wire.ProvideAdminVerificationService(verificationRepository, userRepository) adminVerificationHandler := wire.ProvideAdminVerificationHandler(adminVerificationService, userRepository) - wsHandler := wire.ProvideWSHandler(hub, chatService, groupService, jwtService, callService) - router := ProvideRouter(userHandler, postHandler, commentHandler, messageHandler, notificationHandler, uploadHandler, jwtService, pushHandler, systemMessageHandler, groupHandler, stickerHandler, voteHandler, channelHandler, scheduleHandler, roleHandler, adminUserHandler, adminPostHandler, adminCommentHandler, adminGroupHandler, adminDashboardHandler, adminLogHandler, qrCodeHandler, materialHandler, callHandler, reportHandler, adminReportHandler, verificationHandler, adminVerificationHandler, logService, userActivityService, casbinService, wsHandler) + wsHandler := wire.ProvideWSHandler(hub, chatService, groupService, jwtService, callService, userRepository) + router := ProvideRouter(userRepository, userHandler, postHandler, commentHandler, messageHandler, notificationHandler, uploadHandler, jwtService, pushHandler, systemMessageHandler, groupHandler, stickerHandler, voteHandler, channelHandler, scheduleHandler, roleHandler, adminUserHandler, adminPostHandler, adminCommentHandler, adminGroupHandler, adminDashboardHandler, adminLogHandler, qrCodeHandler, materialHandler, callHandler, reportHandler, adminReportHandler, verificationHandler, adminVerificationHandler, logService, userActivityService, casbinService, wsHandler) hotRankWorker := wire.ProvideHotRankWorker(config, postRepository, cache) app := NewApp(config, db, router, pushService, hotRankWorker, server, logger) return app, nil @@ -143,6 +143,7 @@ func InitializeApp() (*App, error) { // ProvideRouter 提供路由 func ProvideRouter( + userRepo repository.UserRepository, userHandler *handler.UserHandler, postHandler *handler.PostHandler, commentHandler *handler.CommentHandler, @@ -177,6 +178,7 @@ func ProvideRouter( wsHandler *handler.WSHandler, ) *router.Router { return router.New( + userRepo, userHandler, postHandler, commentHandler, diff --git a/internal/handler/ws_handler.go b/internal/handler/ws_handler.go index a0d8594..14366dc 100644 --- a/internal/handler/ws_handler.go +++ b/internal/handler/ws_handler.go @@ -20,6 +20,7 @@ import ( "carrot_bbs/internal/model" "carrot_bbs/internal/pkg/response" "carrot_bbs/internal/pkg/ws" + "carrot_bbs/internal/repository" "carrot_bbs/internal/service" ) @@ -71,6 +72,7 @@ type WSHandler struct { groupService service.GroupService jwtService *service.JWTService callService service.CallService + userRepo repository.UserRepository clientSeq uint64 } @@ -81,6 +83,7 @@ func NewWSHandler( groupService service.GroupService, jwtService *service.JWTService, callService service.CallService, + userRepo repository.UserRepository, ) *WSHandler { return &WSHandler{ wsHub: wsHub, @@ -88,6 +91,7 @@ func NewWSHandler( groupService: groupService, jwtService: jwtService, callService: callService, + userRepo: userRepo, } } @@ -118,6 +122,18 @@ func (h *WSHandler) HandleWebSocket(c *gin.Context) { } userID := claims.UserID + + // 检查用户认证状态 + user, err := h.userRepo.GetByID(userID) + if err != nil { + response.InternalServerError(c, "获取用户信息失败") + return + } + if user.VerificationStatus != model.VerificationStatusApproved { + response.ErrorWithStringCode(c, http.StatusForbidden, "VERIFICATION_REQUIRED", "请先完成身份认证") + return + } + zap.L().Info("WebSocket connection attempt", zap.String("user_id", userID), ) diff --git a/internal/middleware/verification.go b/internal/middleware/verification.go index c495e6c..1103c96 100644 --- a/internal/middleware/verification.go +++ b/internal/middleware/verification.go @@ -1,14 +1,16 @@ package middleware import ( - "github.com/gin-gonic/gin" + "net/http" "carrot_bbs/internal/model" "carrot_bbs/internal/pkg/response" "carrot_bbs/internal/repository" + + "github.com/gin-gonic/gin" ) -func RequireVerification(userRepo repository.UserRepository) gin.HandlerFunc { +func RequireVerified(userRepo repository.UserRepository) gin.HandlerFunc { return func(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { @@ -19,13 +21,13 @@ func RequireVerification(userRepo repository.UserRepository) gin.HandlerFunc { user, err := userRepo.GetByID(userID.(string)) if err != nil { - response.HandleError(c, err, "获取用户信息失败") + response.InternalServerError(c, "获取用户信息失败") c.Abort() return } if user.VerificationStatus != model.VerificationStatusApproved { - response.Forbidden(c, "需要完成身份认证") + response.ErrorWithStringCode(c, http.StatusForbidden, "VERIFICATION_REQUIRED", "请先完成身份认证") c.Abort() return } diff --git a/internal/pkg/response/response.go b/internal/pkg/response/response.go index 8684336..6884b4d 100644 --- a/internal/pkg/response/response.go +++ b/internal/pkg/response/response.go @@ -184,9 +184,9 @@ func statusCodeFromAppErrorCode(code string) int { // ErrorWithStringCode 带字符串错误码的错误响应 func ErrorWithStringCode(c *gin.Context, statusCode int, code string, message string) { - c.JSON(statusCode, ResponseSnakeCase{ - Code: statusCode, - Message: message, - Data: nil, + c.JSON(statusCode, gin.H{ + "code": statusCode, + "message": message, + "error_code": code, }) } diff --git a/internal/router/router.go b/internal/router/router.go index 72ef2f3..adb63f2 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -6,12 +6,14 @@ import ( "carrot_bbs/internal/handler" "carrot_bbs/internal/middleware" "carrot_bbs/internal/model" + "carrot_bbs/internal/repository" "carrot_bbs/internal/service" ) // Router 路由配置 type Router struct { engine *gin.Engine + userRepo repository.UserRepository userHandler *handler.UserHandler postHandler *handler.PostHandler commentHandler *handler.CommentHandler @@ -47,6 +49,7 @@ type Router struct { // New 创建路由 func New( + userRepo repository.UserRepository, userHandler *handler.UserHandler, postHandler *handler.PostHandler, commentHandler *handler.CommentHandler, @@ -87,6 +90,7 @@ func New( r := &Router{ engine: gin.Default(), + userRepo: userRepo, userHandler: userHandler, postHandler: postHandler, commentHandler: commentHandler, @@ -249,9 +253,9 @@ func (r *Router) setupRoutes() { posts.GET("", middleware.OptionalAuth(r.jwtService), r.postHandler.List) posts.GET("/search", middleware.OptionalAuth(r.jwtService), r.postHandler.Search) posts.GET("/:id", middleware.OptionalAuth(r.jwtService), r.postHandler.GetByID) - posts.POST("", authMiddleware, r.postHandler.Create) - posts.PUT("/:id", authMiddleware, r.postHandler.Update) - posts.DELETE("/:id", authMiddleware, r.postHandler.Delete) + posts.POST("", authMiddleware, middleware.RequireVerified(r.userRepo), r.postHandler.Create) + posts.PUT("/:id", authMiddleware, middleware.RequireVerified(r.userRepo), r.postHandler.Update) + posts.DELETE("/:id", authMiddleware, middleware.RequireVerified(r.userRepo), r.postHandler.Delete) // 浏览量记录(可选认证,允许游客浏览) posts.POST("/:id/view", middleware.OptionalAuth(r.jwtService), r.postHandler.RecordView) @@ -259,18 +263,18 @@ func (r *Router) setupRoutes() { posts.POST("/:id/share", middleware.OptionalAuth(r.jwtService), r.postHandler.RecordShare) // 点赞 - posts.POST("/:id/like", authMiddleware, r.postHandler.Like) - posts.DELETE("/:id/like", authMiddleware, r.postHandler.Unlike) + posts.POST("/:id/like", authMiddleware, middleware.RequireVerified(r.userRepo), r.postHandler.Like) + posts.DELETE("/:id/like", authMiddleware, middleware.RequireVerified(r.userRepo), r.postHandler.Unlike) // 收藏 - posts.POST("/:id/favorite", authMiddleware, r.postHandler.Favorite) - posts.DELETE("/:id/favorite", authMiddleware, r.postHandler.Unfavorite) + posts.POST("/:id/favorite", authMiddleware, middleware.RequireVerified(r.userRepo), r.postHandler.Favorite) + posts.DELETE("/:id/favorite", authMiddleware, middleware.RequireVerified(r.userRepo), r.postHandler.Unfavorite) // 投票相关路由 - posts.POST("/vote", authMiddleware, r.voteHandler.CreateVotePost) // 创建投票帖子 - posts.GET("/:id/vote", middleware.OptionalAuth(r.jwtService), r.voteHandler.GetVoteResult) // 获取投票结果 - posts.POST("/:id/vote", authMiddleware, r.voteHandler.Vote) // 投票 - posts.DELETE("/:id/vote", authMiddleware, r.voteHandler.Unvote) // 取消投票 + posts.POST("/vote", authMiddleware, middleware.RequireVerified(r.userRepo), r.voteHandler.CreateVotePost) // 创建投票帖子 + posts.GET("/:id/vote", middleware.OptionalAuth(r.jwtService), r.voteHandler.GetVoteResult) // 获取投票结果 + posts.POST("/:id/vote", authMiddleware, middleware.RequireVerified(r.userRepo), r.voteHandler.Vote) // 投票 + posts.DELETE("/:id/vote", authMiddleware, middleware.RequireVerified(r.userRepo), r.voteHandler.Unvote) // 取消投票 } // 频道路由(公开) @@ -322,15 +326,15 @@ func (r *Router) setupRoutes() { comments.GET("/post/:id", middleware.OptionalAuth(r.jwtService), r.commentHandler.GetByPostID) comments.GET("/post/:id/cursor", middleware.OptionalAuth(r.jwtService), r.commentHandler.GetByPostIDByCursor) // 帖子评论游标分页 comments.GET("/:id", middleware.OptionalAuth(r.jwtService), r.commentHandler.GetByID) - comments.POST("", authMiddleware, r.commentHandler.Create) - comments.PUT("/:id", authMiddleware, r.commentHandler.Update) - comments.DELETE("/:id", authMiddleware, r.commentHandler.Delete) + comments.POST("", authMiddleware, middleware.RequireVerified(r.userRepo), r.commentHandler.Create) + comments.PUT("/:id", authMiddleware, middleware.RequireVerified(r.userRepo), r.commentHandler.Update) + comments.DELETE("/:id", authMiddleware, middleware.RequireVerified(r.userRepo), r.commentHandler.Delete) comments.GET("/:id/replies", middleware.OptionalAuth(r.jwtService), r.commentHandler.GetReplies) comments.GET("/:id/replies/flat", middleware.OptionalAuth(r.jwtService), r.commentHandler.GetRepliesByRootID) // 扁平化分页获取回复 comments.GET("/:id/replies/cursor", middleware.OptionalAuth(r.jwtService), r.commentHandler.GetRepliesByRootIDByCursor) // 回复游标分页 // 评论点赞 - comments.POST("/:id/like", authMiddleware, r.commentHandler.Like) - comments.DELETE("/:id/like", authMiddleware, r.commentHandler.Unlike) + comments.POST("/:id/like", authMiddleware, middleware.RequireVerified(r.userRepo), r.commentHandler.Like) + comments.DELETE("/:id/like", authMiddleware, middleware.RequireVerified(r.userRepo), r.commentHandler.Unlike) } // 会话路由 @@ -340,18 +344,18 @@ func (r *Router) setupRoutes() { // ================================================================ // 新的 RESTful 风格路由(推荐使用) // ================================================================ - conversations.GET("", r.messageHandler.HandleGetConversationList) // 列表 - conversations.GET("/cursor", r.messageHandler.GetConversationsByCursor) // 会话列表游标分页 - conversations.POST("", r.messageHandler.HandleCreateConversation) // 创建 - conversations.GET("/:id", r.messageHandler.HandleGetConversation) // 详情 - conversations.GET("/:id/messages", r.messageHandler.HandleGetMessages) // 消息列表 - conversations.GET("/:id/messages/cursor", r.messageHandler.GetMessagesByCursor) // 消息列表游标分页 - conversations.POST("/:id/messages", r.messageHandler.HandleSendMessage) // 发送消息 - conversations.POST("/:id/read", r.messageHandler.HandleMarkRead) // 标记已读 - conversations.PUT("/:id/pinned", r.messageHandler.HandleSetConversationPinned) // 置顶设置 - conversations.GET("/unread/count", r.messageHandler.GetUnreadCount) // 未读数 - conversations.POST("/:id/typing", r.messageHandler.HandleTyping) // 输入状态 - conversations.DELETE("/:id/self", r.messageHandler.HandleDeleteConversationForSelf) // 删除会话(仅自己) + conversations.GET("", r.messageHandler.HandleGetConversationList) // 列表 + conversations.GET("/cursor", r.messageHandler.GetConversationsByCursor) // 会话列表游标分页 + conversations.POST("", middleware.RequireVerified(r.userRepo), r.messageHandler.HandleCreateConversation) // 创建 + conversations.GET("/:id", r.messageHandler.HandleGetConversation) // 详情 + conversations.GET("/:id/messages", r.messageHandler.HandleGetMessages) // 消息列表 + conversations.GET("/:id/messages/cursor", r.messageHandler.GetMessagesByCursor) // 消息列表游标分页 + conversations.POST("/:id/messages", middleware.RequireVerified(r.userRepo), r.messageHandler.HandleSendMessage) // 发送消息 + conversations.POST("/:id/read", r.messageHandler.HandleMarkRead) // 标记已读 + conversations.PUT("/:id/pinned", r.messageHandler.HandleSetConversationPinned) // 置顶设置 + conversations.GET("/unread/count", r.messageHandler.GetUnreadCount) // 未读数 + conversations.POST("/:id/typing", r.messageHandler.HandleTyping) // 输入状态 + conversations.DELETE("/:id/self", r.messageHandler.HandleDeleteConversationForSelf) // 删除会话(仅自己) } @@ -376,7 +380,7 @@ func (r *Router) setupRoutes() { messages.Use(authMiddleware) { // 撤回/删除消息(统一接口) - messages.POST("/delete_msg", r.messageHandler.HandleDeleteMsg) + messages.POST("/delete_msg", middleware.RequireVerified(r.userRepo), r.messageHandler.HandleDeleteMsg) } // 举报路由 diff --git a/internal/wire/handler.go b/internal/wire/handler.go index 5cbaca2..d62e283 100644 --- a/internal/wire/handler.go +++ b/internal/wire/handler.go @@ -85,8 +85,9 @@ func ProvideWSHandler( groupService service.GroupService, jwtService *service.JWTService, callService service.CallService, + userRepo repository.UserRepository, ) *handler.WSHandler { - return handler.NewWSHandler(wsHub, chatService, groupService, jwtService, callService) + return handler.NewWSHandler(wsHub, chatService, groupService, jwtService, callService, userRepo) } // ProvideAdminVerificationHandler 提供管理端身份认证处理器