feat(auth): implement session-based token management with revocation support
All checks were successful
Build Backend / build (push) Successful in 2m19s
Build Backend / build-docker (push) Successful in 46s

Add Session model, SessionService, and SessionRepository to track user login
sessions and enable token revocation on auth-critical events.

- Introduce explicit TokenType (access/refresh) in JWT claims to prevent
  refresh token misuse via access token endpoints
- Add SessionID field to JWT claims, enabling stateless JWT validation
  against revoked sessions
- Replace legacy Auth middleware with RequireAuth/OptionalAuth pipeline
  that validates token type, account status, and session validity
- Implement session revocation on password change, reset, user ban/inactive,
  and explicit logout
- Add Principal cache with active invalidation for banned/role-changed users
- Fix IDOR vulnerability: GetMessagesByCursor now validates currentUserID
  is conversation participant via GetParticipantStrict
- Add group member visibility checks: announcements, group info, member list
  now require group membership
- Simplify Casbin policy: remove g grouping, use r.sub == p.sub matcher
  with globMatch; user_roles table is single source of truth for roles
- Add migration logic to clean legacy casbin g rules and migrate old p rules
  from path-style to admin/<domain> resource naming
This commit is contained in:
lafay
2026-07-05 18:28:08 +08:00
parent d240485d0e
commit eb931bf1c6
41 changed files with 2544 additions and 360 deletions

View File

@@ -3,9 +3,9 @@ package router
import (
"github.com/gin-gonic/gin"
"with_you/internal/cache"
"with_you/internal/handler"
"with_you/internal/middleware"
"with_you/internal/model"
"with_you/internal/repository"
"with_you/internal/service"
)
@@ -49,6 +49,9 @@ type RouterDeps struct {
TradeHandler *handler.TradeHandler
WSHandler *handler.WSHandler
JWTService service.JWTService
SessionService service.SessionService
IdentityProvider middleware.IdentityProvider
Cache cache.Cache
LogService *service.LogService
ActivityService service.UserActivityService
CasbinService service.CasbinService
@@ -67,6 +70,8 @@ type Router struct {
func New(deps RouterDeps) *Router {
deps.UserHandler.SetJWTService(deps.JWTService)
deps.UserHandler.SetActivityService(deps.ActivityService)
deps.UserHandler.SetSessionService(deps.SessionService)
deps.UserHandler.SetCache(deps.Cache)
r := &Router{
RouterDeps: &deps,
@@ -92,17 +97,17 @@ func (r *Router) setupRoutes() {
v1 := r.engine.Group("/api/v1")
{
// 认证路由(公开,按接口类型设置不同速率限制)
auth := v1.Group("/auth")
auth.Use(middleware.GlobalAuthRateLimit())
auth.Use(middleware.IPBanMiddleware())
// 注意:登出端点在下面 authMiddleware 定义后单独挂载,避免前向引用。
authPublic := v1.Group("/auth")
authPublic.Use(middleware.GlobalAuthRateLimit())
authPublic.Use(middleware.IPBanMiddleware())
{
auth.POST("/register", middleware.RegisterRateLimit(), r.UserHandler.Register)
auth.POST("/register/send-code", middleware.SendCodeRateLimit(), r.UserHandler.SendRegisterCode)
auth.POST("/login", middleware.LoginRateLimit(), r.UserHandler.Login)
auth.POST("/password/send-code", middleware.PasswordResetRateLimit(), r.UserHandler.SendPasswordResetCode)
auth.POST("/password/reset", middleware.PasswordResetRateLimit(), r.UserHandler.ResetPassword)
auth.POST("/logout", r.UserHandler.Logout)
auth.POST("/refresh", r.UserHandler.RefreshToken)
authPublic.POST("/register", middleware.RegisterRateLimit(), r.UserHandler.Register)
authPublic.POST("/register/send-code", middleware.SendCodeRateLimit(), r.UserHandler.SendRegisterCode)
authPublic.POST("/login", middleware.LoginRateLimit(), r.UserHandler.Login)
authPublic.POST("/password/send-code", middleware.PasswordResetRateLimit(), r.UserHandler.SendPasswordResetCode)
authPublic.POST("/password/reset", middleware.PasswordResetRateLimit(), r.UserHandler.ResetPassword)
authPublic.POST("/refresh", r.UserHandler.RefreshToken)
}
// 初始化超级管理员(公开接口,只能使用一次)
@@ -111,14 +116,21 @@ func (r *Router) setupRoutes() {
}
// 需要认证的路由
authMiddleware := middleware.Auth(r.JWTService)
// 统一认证管道:令牌解析 → 加载 Principal → 校验账户状态 → 校验会话有效性。
// 取代旧 middleware.Auth避免 refresh token 误当 access token、封禁用户旧 token 仍可用等问题。
// 注入 cache.Cache 启用 Principal/Session 缓存30s TTL封禁/改密/登出时由 auth.InvalidatePrincipalCache 主动失效。
authMiddleware := middleware.RequireAuth(r.JWTService, r.IdentityProvider, r.Cache)
optionalAuth := middleware.OptionalAuth(r.JWTService, r.IdentityProvider, r.Cache)
// 登出挂在公开 auth 组上,但单独加认证中间件。
v1.POST("/auth/logout", authMiddleware, r.UserHandler.Logout)
// === 访问日志中间件 ===
if r.AdminLogHandler != nil && r.LogService != nil {
accessLogConfig := &middleware.AccessLogConfig{
Enable: true,
SkipPaths: []string{"/health", "/api/v1/health"},
ServerIP: "0.0.0.0",
Enable: true,
SkipPaths: []string{"/health", "/api/v1/health"},
ServerIP: "0.0.0.0",
ServerPort: 8080,
}
r.engine.Use(middleware.AccessLogMiddleware(
@@ -155,10 +167,10 @@ func (r *Router) setupRoutes() {
}
// 搜索用户
users.GET("/search", middleware.OptionalAuth(r.JWTService), r.UserHandler.Search)
users.GET("/search", optionalAuth, r.UserHandler.Search)
// 其他用户
users.GET("/:id", middleware.OptionalAuth(r.JWTService), r.UserHandler.GetUserByID)
users.GET("/:id", optionalAuth, r.UserHandler.GetUserByID)
users.POST("/:id/follow", authMiddleware, r.UserHandler.FollowUser)
users.DELETE("/:id/follow", authMiddleware, r.UserHandler.UnfollowUser)
users.POST("/:id/block", authMiddleware, r.UserHandler.BlockUser)
@@ -169,8 +181,8 @@ func (r *Router) setupRoutes() {
users.GET("/:id/followers", authMiddleware, r.UserHandler.GetFollowersList)
// 用户帖子 - 使用 OptionalAuth 获取当前用户点赞/收藏状态
users.GET("/:id/posts", middleware.OptionalAuth(r.JWTService), r.PostHandler.GetUserPosts)
users.GET("/:id/favorites", middleware.OptionalAuth(r.JWTService), r.PostHandler.GetFavorites)
users.GET("/:id/posts", optionalAuth, r.PostHandler.GetUserPosts)
users.GET("/:id/favorites", optionalAuth, r.PostHandler.GetFavorites)
}
// 身份认证路由
@@ -203,32 +215,32 @@ func (r *Router) setupRoutes() {
}
// 认证路由(公开)
authPublic := v1.Group("/auth")
authPublicCheck := v1.Group("/auth")
{
authPublic.GET("/check-username", r.UserHandler.CheckUsername)
authPublicCheck.GET("/check-username", r.UserHandler.CheckUsername)
}
// 帖子路由
posts := v1.Group("/posts")
{
// 使用 OptionalAuth 中间件来获取用户登录状态
posts.GET("", middleware.OptionalAuth(r.JWTService), r.PostHandler.List)
posts.GET("/search", middleware.OptionalAuth(r.JWTService), r.PostHandler.Search)
posts.GET("/suggest", middleware.OptionalAuth(r.JWTService), r.PostHandler.Suggest)
posts.GET("/:id", middleware.OptionalAuth(r.JWTService), r.PostHandler.GetByID)
posts.GET("", optionalAuth, r.PostHandler.List)
posts.GET("/search", optionalAuth, r.PostHandler.Search)
posts.GET("/suggest", optionalAuth, r.PostHandler.Suggest)
posts.GET("/:id", optionalAuth, r.PostHandler.GetByID)
posts.POST("", authMiddleware, middleware.RequireVerified(r.UserService), r.PostHandler.Create)
posts.PUT("/:id", authMiddleware, middleware.RequireVerified(r.UserService), r.PostHandler.Update)
posts.DELETE("/:id", authMiddleware, middleware.RequireVerified(r.UserService), r.PostHandler.Delete)
// 浏览量记录(可选认证,允许游客浏览)
posts.POST("/:id/view", middleware.OptionalAuth(r.JWTService), r.PostHandler.RecordView)
posts.POST("/:id/view", optionalAuth, r.PostHandler.RecordView)
// 分享计数(可选认证;仅已发布帖子生效)
posts.POST("/:id/share", middleware.OptionalAuth(r.JWTService), r.PostHandler.RecordShare)
posts.POST("/:id/share", optionalAuth, r.PostHandler.RecordShare)
// 内链相关路由
posts.GET("/:id/related", middleware.OptionalAuth(r.JWTService), r.PostHandler.GetRelatedPosts)
posts.GET("/:id/refs", middleware.OptionalAuth(r.JWTService), r.PostHandler.GetReferencedPosts)
posts.POST("/:id/ref-click", middleware.OptionalAuth(r.JWTService), r.PostHandler.RecordRefClick)
posts.GET("/:id/related", optionalAuth, r.PostHandler.GetRelatedPosts)
posts.GET("/:id/refs", optionalAuth, r.PostHandler.GetReferencedPosts)
posts.POST("/:id/ref-click", optionalAuth, r.PostHandler.RecordRefClick)
// 点赞
posts.POST("/:id/like", authMiddleware, middleware.RequireVerified(r.UserService), r.PostHandler.Like)
@@ -240,7 +252,7 @@ func (r *Router) setupRoutes() {
// 投票相关路由
posts.POST("/vote", authMiddleware, middleware.RequireVerified(r.UserService), r.VoteHandler.CreateVotePost) // 创建投票帖子
posts.GET("/:id/vote", middleware.OptionalAuth(r.JWTService), r.VoteHandler.GetVoteResult) // 获取投票结果
posts.GET("/:id/vote", optionalAuth, r.VoteHandler.GetVoteResult) // 获取投票结果
posts.POST("/:id/vote", authMiddleware, middleware.RequireVerified(r.UserService), r.VoteHandler.Vote) // 投票
posts.DELETE("/:id/vote", authMiddleware, middleware.RequireVerified(r.UserService), r.VoteHandler.Unvote) // 取消投票
}
@@ -257,13 +269,13 @@ func (r *Router) setupRoutes() {
if r.TradeHandler != nil {
trade := v1.Group("/trade")
{
trade.GET("", middleware.OptionalAuth(r.JWTService), r.TradeHandler.List)
trade.GET("/:id", middleware.OptionalAuth(r.JWTService), r.TradeHandler.GetByID)
trade.GET("", optionalAuth, r.TradeHandler.List)
trade.GET("/:id", optionalAuth, r.TradeHandler.GetByID)
trade.POST("", authMiddleware, middleware.RequireVerified(r.UserService), r.TradeHandler.Create)
trade.PUT("/:id", authMiddleware, middleware.RequireVerified(r.UserService), r.TradeHandler.Update)
trade.DELETE("/:id", authMiddleware, middleware.RequireVerified(r.UserService), r.TradeHandler.Delete)
trade.PUT("/:id/status", authMiddleware, middleware.RequireVerified(r.UserService), r.TradeHandler.UpdateStatus)
trade.POST("/:id/view", middleware.OptionalAuth(r.JWTService), r.TradeHandler.RecordView)
trade.POST("/:id/view", optionalAuth, r.TradeHandler.RecordView)
trade.POST("/:id/favorite", authMiddleware, middleware.RequireVerified(r.UserService), r.TradeHandler.Favorite)
trade.DELETE("/:id/favorite", authMiddleware, middleware.RequireVerified(r.UserService), r.TradeHandler.Unfavorite)
}
@@ -337,15 +349,15 @@ func (r *Router) setupRoutes() {
// 评论路由
comments := v1.Group("/comments")
{
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.GET("/post/:id", optionalAuth, r.CommentHandler.GetByPostID)
comments.GET("/post/:id/cursor", optionalAuth, r.CommentHandler.GetByPostIDByCursor) // 帖子评论游标分页
comments.GET("/:id", optionalAuth, r.CommentHandler.GetByID)
comments.POST("", authMiddleware, middleware.RequireVerified(r.UserService), r.CommentHandler.Create)
comments.PUT("/:id", authMiddleware, middleware.RequireVerified(r.UserService), r.CommentHandler.Update)
comments.DELETE("/:id", authMiddleware, middleware.RequireVerified(r.UserService), 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.GET("/:id/replies", optionalAuth, r.CommentHandler.GetReplies)
comments.GET("/:id/replies/flat", optionalAuth, r.CommentHandler.GetRepliesByRootID) // 扁平化分页获取回复
comments.GET("/:id/replies/cursor", optionalAuth, r.CommentHandler.GetRepliesByRootIDByCursor) // 回复游标分页
// 评论点赞
comments.POST("/:id/like", authMiddleware, middleware.RequireVerified(r.UserService), r.CommentHandler.Like)
comments.DELETE("/:id/like", authMiddleware, middleware.RequireVerified(r.UserService), r.CommentHandler.Unlike)
@@ -527,129 +539,129 @@ func (r *Router) setupRoutes() {
}
}
// 管理路由(需要管理员权限)
// 管理路由
// 鉴权链RequireAuth → RequireActive拒绝 pending_deletion 访问 admin→ 路由级 Authorize。
// 不再使用 RequireRole 粗粒度,改用每路由 Authorize(casbin, resource, action)
// super_admin 通过 admin.* 通配策略获得所有能力admin 仅获得业务管理能力,
// 角色与权限管理admin.roles.*、admin.users.roles.*与日志导出admin.logs.export仅 super_admin 拥有。
if r.RoleHandler != nil {
admin := v1.Group("/admin")
admin.Use(authMiddleware)
admin.Use(middleware.RequireRole(r.CasbinService, model.RoleAdmin, model.RoleSuperAdmin))
{
// 角色管理
admin.GET("/roles", r.RoleHandler.GetAllRoles)
admin.GET("/roles/:name", r.RoleHandler.GetRoleDetail)
admin.GET("/roles/:name/permissions", r.RoleHandler.GetRolePermissions)
admin.PUT("/roles/:name/permissions", r.RoleHandler.UpdateRolePermissions)
admin.GET("/permissions", r.RoleHandler.GetAllPermissions)
admin.GET("/users/:id/roles", r.RoleHandler.GetUserRoles)
admin.POST("/users/:id/roles", r.RoleHandler.AssignRole)
admin.DELETE("/users/:id/roles/:role", r.RoleHandler.RemoveRole)
admin.Use(middleware.RequireActive())
// 用户管理
if r.AdminUserHandler != nil {
admin.GET("/users", r.AdminUserHandler.GetUserList)
admin.GET("/users/:id", r.AdminUserHandler.GetUserDetail)
admin.PUT("/users/:id/status", r.AdminUserHandler.UpdateUserStatus)
admin.GET("/users/:id/devices", r.AdminUserHandler.GetUserDevices)
}
// 角色与权限管理(仅 super_admin
admin.GET("/roles", middleware.Authorize(r.CasbinService, "admin/roles", "read"), r.RoleHandler.GetAllRoles)
admin.GET("/roles/:name", middleware.Authorize(r.CasbinService, "admin/roles", "read"), r.RoleHandler.GetRoleDetail)
admin.GET("/roles/:name/permissions", middleware.Authorize(r.CasbinService, "admin/roles/permissions", "read"), r.RoleHandler.GetRolePermissions)
admin.PUT("/roles/:name/permissions", middleware.Authorize(r.CasbinService, "admin/roles/permissions", "write"), r.RoleHandler.UpdateRolePermissions)
admin.GET("/permissions", middleware.Authorize(r.CasbinService, "admin/roles", "read"), r.RoleHandler.GetAllPermissions)
admin.GET("/users/:id/roles", middleware.Authorize(r.CasbinService, "admin/users/roles", "read"), r.RoleHandler.GetUserRoles)
admin.POST("/users/:id/roles", middleware.Authorize(r.CasbinService, "admin/users/roles", "write"), r.RoleHandler.AssignRole)
admin.DELETE("/users/:id/roles/:role", middleware.Authorize(r.CasbinService, "admin/users/roles", "write"), r.RoleHandler.RemoveRole)
// 帖子管理
if r.AdminPostHandler != nil {
admin.GET("/posts", r.AdminPostHandler.GetPostList)
admin.GET("/posts/:id", r.AdminPostHandler.GetPostDetail)
admin.DELETE("/posts/:id", r.AdminPostHandler.DeletePost)
admin.PUT("/posts/:id/moderate", r.AdminPostHandler.ModeratePost)
admin.POST("/posts/batch-delete", r.AdminPostHandler.BatchDeletePosts)
admin.POST("/posts/batch-moderate", r.AdminPostHandler.BatchModeratePosts)
admin.PUT("/posts/:id/pin", r.AdminPostHandler.SetPostPin)
admin.PUT("/posts/:id/feature", r.AdminPostHandler.SetPostFeature)
}
// 用户管理
if r.AdminUserHandler != nil {
admin.GET("/users", middleware.Authorize(r.CasbinService, "admin/users", "read"), r.AdminUserHandler.GetUserList)
admin.GET("/users/:id", middleware.Authorize(r.CasbinService, "admin/users", "read"), r.AdminUserHandler.GetUserDetail)
admin.PUT("/users/:id/status", middleware.Authorize(r.CasbinService, "admin/users/status", "write"), r.AdminUserHandler.UpdateUserStatus)
admin.GET("/users/:id/devices", middleware.Authorize(r.CasbinService, "admin/users/devices", "read"), r.AdminUserHandler.GetUserDevices)
}
// 频道管理
if r.ChannelHandler != nil {
admin.GET("/channels", r.ChannelHandler.AdminList)
admin.POST("/channels", r.ChannelHandler.AdminCreate)
admin.PUT("/channels/:id", r.ChannelHandler.AdminUpdate)
admin.DELETE("/channels/:id", r.ChannelHandler.AdminDelete)
}
// 帖子管理
if r.AdminPostHandler != nil {
admin.GET("/posts", middleware.Authorize(r.CasbinService, "admin/posts", "read"), r.AdminPostHandler.GetPostList)
admin.GET("/posts/:id", middleware.Authorize(r.CasbinService, "admin/posts", "read"), r.AdminPostHandler.GetPostDetail)
admin.DELETE("/posts/:id", middleware.Authorize(r.CasbinService, "admin/posts", "write"), r.AdminPostHandler.DeletePost)
admin.PUT("/posts/:id/moderate", middleware.Authorize(r.CasbinService, "admin/posts", "write"), r.AdminPostHandler.ModeratePost)
admin.POST("/posts/batch-delete", middleware.Authorize(r.CasbinService, "admin/posts", "write"), r.AdminPostHandler.BatchDeletePosts)
admin.POST("/posts/batch-moderate", middleware.Authorize(r.CasbinService, "admin/posts", "write"), r.AdminPostHandler.BatchModeratePosts)
admin.PUT("/posts/:id/pin", middleware.Authorize(r.CasbinService, "admin/posts", "write"), r.AdminPostHandler.SetPostPin)
admin.PUT("/posts/:id/feature", middleware.Authorize(r.CasbinService, "admin/posts", "write"), r.AdminPostHandler.SetPostFeature)
}
// 评论管理
if r.AdminCommentHandler != nil {
admin.GET("/comments", r.AdminCommentHandler.GetCommentList)
admin.GET("/comments/:id", r.AdminCommentHandler.GetCommentDetail)
admin.DELETE("/comments/:id", r.AdminCommentHandler.DeleteComment)
admin.PUT("/comments/:id/moderate", r.AdminCommentHandler.ModerateComment)
admin.POST("/comments/batch-delete", r.AdminCommentHandler.BatchDeleteComments)
admin.POST("/comments/batch-moderate", r.AdminCommentHandler.BatchModerateComments)
admin.GET("/comments/post/:postId", r.AdminCommentHandler.GetPostComments)
}
// 频道管理
if r.ChannelHandler != nil {
admin.GET("/channels", middleware.Authorize(r.CasbinService, "admin/channels", "read"), r.ChannelHandler.AdminList)
admin.POST("/channels", middleware.Authorize(r.CasbinService, "admin/channels", "write"), r.ChannelHandler.AdminCreate)
admin.PUT("/channels/:id", middleware.Authorize(r.CasbinService, "admin/channels", "write"), r.ChannelHandler.AdminUpdate)
admin.DELETE("/channels/:id", middleware.Authorize(r.CasbinService, "admin/channels", "write"), r.ChannelHandler.AdminDelete)
}
// 群组管理
if r.AdminGroupHandler != nil {
admin.GET("/groups", r.AdminGroupHandler.GetGroupList)
admin.GET("/groups/:id", r.AdminGroupHandler.GetGroupDetail)
admin.GET("/groups/:id/members", r.AdminGroupHandler.GetGroupMembers)
admin.DELETE("/groups/:id", r.AdminGroupHandler.DeleteGroup)
admin.PUT("/groups/:id/status", r.AdminGroupHandler.UpdateGroupStatus)
admin.DELETE("/groups/:id/members/:userId", r.AdminGroupHandler.RemoveMember)
admin.PUT("/groups/:id/transfer", r.AdminGroupHandler.TransferOwner)
}
// 评论管理
if r.AdminCommentHandler != nil {
admin.GET("/comments", middleware.Authorize(r.CasbinService, "admin/comments", "read"), r.AdminCommentHandler.GetCommentList)
admin.GET("/comments/:id", middleware.Authorize(r.CasbinService, "admin/comments", "read"), r.AdminCommentHandler.GetCommentDetail)
admin.DELETE("/comments/:id", middleware.Authorize(r.CasbinService, "admin/comments", "write"), r.AdminCommentHandler.DeleteComment)
admin.PUT("/comments/:id/moderate", middleware.Authorize(r.CasbinService, "admin/comments", "write"), r.AdminCommentHandler.ModerateComment)
admin.POST("/comments/batch-delete", middleware.Authorize(r.CasbinService, "admin/comments", "write"), r.AdminCommentHandler.BatchDeleteComments)
admin.POST("/comments/batch-moderate", middleware.Authorize(r.CasbinService, "admin/comments", "write"), r.AdminCommentHandler.BatchModerateComments)
admin.GET("/comments/post/:postId", middleware.Authorize(r.CasbinService, "admin/comments", "read"), r.AdminCommentHandler.GetPostComments)
}
// 仪表盘
if r.AdminDashboardHandler != nil {
admin.GET("/dashboard/stats", r.AdminDashboardHandler.GetStats)
admin.GET("/dashboard/user-activity", r.AdminDashboardHandler.GetUserActivityTrend)
admin.GET("/dashboard/content-stats", r.AdminDashboardHandler.GetContentStats)
admin.GET("/dashboard/pending-content", r.AdminDashboardHandler.GetPendingContent)
admin.GET("/dashboard/online-users", r.AdminDashboardHandler.GetOnlineUsers)
}
// 群组管理
if r.AdminGroupHandler != nil {
admin.GET("/groups", middleware.Authorize(r.CasbinService, "admin/groups", "read"), r.AdminGroupHandler.GetGroupList)
admin.GET("/groups/:id", middleware.Authorize(r.CasbinService, "admin/groups", "read"), r.AdminGroupHandler.GetGroupDetail)
admin.GET("/groups/:id/members", middleware.Authorize(r.CasbinService, "admin/groups", "read"), r.AdminGroupHandler.GetGroupMembers)
admin.DELETE("/groups/:id", middleware.Authorize(r.CasbinService, "admin/groups", "write"), r.AdminGroupHandler.DeleteGroup)
admin.PUT("/groups/:id/status", middleware.Authorize(r.CasbinService, "admin/groups", "write"), r.AdminGroupHandler.UpdateGroupStatus)
admin.DELETE("/groups/:id/members/:userId", middleware.Authorize(r.CasbinService, "admin/groups", "write"), r.AdminGroupHandler.RemoveMember)
admin.PUT("/groups/:id/transfer", middleware.Authorize(r.CasbinService, "admin/groups", "write"), r.AdminGroupHandler.TransferOwner)
}
// 日志管理
if r.AdminLogHandler != nil {
logs := admin.Group("/logs")
{
logs.GET("/operations", r.AdminLogHandler.GetOperationLogs)
logs.GET("/logins", r.AdminLogHandler.GetLoginLogs)
logs.GET("/data-changes", r.AdminLogHandler.GetDataChangeLogs)
logs.GET("/statistics", r.AdminLogHandler.GetLogStatistics)
logs.GET("/export", r.AdminLogHandler.ExportLogs)
}
}
// 仪表盘
if r.AdminDashboardHandler != nil {
admin.GET("/dashboard/stats", middleware.Authorize(r.CasbinService, "admin/dashboard", "read"), r.AdminDashboardHandler.GetStats)
admin.GET("/dashboard/user-activity", middleware.Authorize(r.CasbinService, "admin/dashboard", "read"), r.AdminDashboardHandler.GetUserActivityTrend)
admin.GET("/dashboard/content-stats", middleware.Authorize(r.CasbinService, "admin/dashboard", "read"), r.AdminDashboardHandler.GetContentStats)
admin.GET("/dashboard/pending-content", middleware.Authorize(r.CasbinService, "admin/dashboard", "read"), r.AdminDashboardHandler.GetPendingContent)
admin.GET("/dashboard/online-users", middleware.Authorize(r.CasbinService, "admin/dashboard", "read"), r.AdminDashboardHandler.GetOnlineUsers)
}
// 学习资料管理
if r.MaterialHandler != nil {
// 学科管理
admin.GET("/materials/subjects", r.MaterialHandler.AdminListSubjects)
admin.POST("/materials/subjects", r.MaterialHandler.AdminCreateSubject)
admin.PUT("/materials/subjects/:id", r.MaterialHandler.AdminUpdateSubject)
admin.DELETE("/materials/subjects/:id", r.MaterialHandler.AdminDeleteSubject)
// 资料管理
admin.GET("/materials", r.MaterialHandler.AdminListMaterials)
admin.GET("/materials/:id", r.MaterialHandler.AdminGetMaterial)
admin.POST("/materials", r.MaterialHandler.AdminCreateMaterial)
admin.PUT("/materials/:id", r.MaterialHandler.AdminUpdateMaterial)
admin.DELETE("/materials/:id", r.MaterialHandler.AdminDeleteMaterial)
}
// 日志管理(导出仅 super_admin
if r.AdminLogHandler != nil {
admin.GET("/logs/operations", middleware.Authorize(r.CasbinService, "admin/logs", "read"), r.AdminLogHandler.GetOperationLogs)
admin.GET("/logs/logins", middleware.Authorize(r.CasbinService, "admin/logs", "read"), r.AdminLogHandler.GetLoginLogs)
admin.GET("/logs/data-changes", middleware.Authorize(r.CasbinService, "admin/logs", "read"), r.AdminLogHandler.GetDataChangeLogs)
admin.GET("/logs/statistics", middleware.Authorize(r.CasbinService, "admin/logs", "read"), r.AdminLogHandler.GetLogStatistics)
admin.GET("/logs/export", middleware.Authorize(r.CasbinService, "admin/logs/export", "read"), r.AdminLogHandler.ExportLogs)
}
// 举报管理
if r.AdminReportHandler != nil {
admin.GET("/reports", r.AdminReportHandler.List)
admin.GET("/reports/:id", r.AdminReportHandler.Detail)
admin.PUT("/reports/:id/handle", r.AdminReportHandler.Handle)
admin.POST("/reports/batch-handle", r.AdminReportHandler.BatchHandle)
}
// 学习资料管理
if r.MaterialHandler != nil {
// 学科管理
admin.GET("/materials/subjects", middleware.Authorize(r.CasbinService, "admin/materials", "read"), r.MaterialHandler.AdminListSubjects)
admin.POST("/materials/subjects", middleware.Authorize(r.CasbinService, "admin/materials", "write"), r.MaterialHandler.AdminCreateSubject)
admin.PUT("/materials/subjects/:id", middleware.Authorize(r.CasbinService, "admin/materials", "write"), r.MaterialHandler.AdminUpdateSubject)
admin.DELETE("/materials/subjects/:id", middleware.Authorize(r.CasbinService, "admin/materials", "write"), r.MaterialHandler.AdminDeleteSubject)
// 资料管理
admin.GET("/materials", middleware.Authorize(r.CasbinService, "admin/materials", "read"), r.MaterialHandler.AdminListMaterials)
admin.GET("/materials/:id", middleware.Authorize(r.CasbinService, "admin/materials", "read"), r.MaterialHandler.AdminGetMaterial)
admin.POST("/materials", middleware.Authorize(r.CasbinService, "admin/materials", "write"), r.MaterialHandler.AdminCreateMaterial)
admin.PUT("/materials/:id", middleware.Authorize(r.CasbinService, "admin/materials", "write"), r.MaterialHandler.AdminUpdateMaterial)
admin.DELETE("/materials/:id", middleware.Authorize(r.CasbinService, "admin/materials", "write"), r.MaterialHandler.AdminDeleteMaterial)
}
// 身份认证管理
if r.AdminVerificationHandler != nil {
admin.GET("/verifications", r.AdminVerificationHandler.ListVerifications)
admin.GET("/verifications/:id", r.AdminVerificationHandler.GetVerificationDetail)
admin.PUT("/verifications/:id/review", r.AdminVerificationHandler.ReviewVerification)
}
// 举报管理
if r.AdminReportHandler != nil {
admin.GET("/reports", middleware.Authorize(r.CasbinService, "admin/reports", "read"), r.AdminReportHandler.List)
admin.GET("/reports/:id", middleware.Authorize(r.CasbinService, "admin/reports", "read"), r.AdminReportHandler.Detail)
admin.PUT("/reports/:id/handle", middleware.Authorize(r.CasbinService, "admin/reports", "write"), r.AdminReportHandler.Handle)
admin.POST("/reports/batch-handle", middleware.Authorize(r.CasbinService, "admin/reports", "write"), r.AdminReportHandler.BatchHandle)
}
// 用户资料审核管理
if r.AdminProfileAuditHandler != nil {
admin.GET("/profile-audits", r.AdminProfileAuditHandler.GetPendingList)
admin.PUT("/profile-audits/:id/moderate", r.AdminProfileAuditHandler.ModerateAudit)
admin.POST("/profile-audits/batch-moderate", r.AdminProfileAuditHandler.BatchModerate)
}
// 身份认证管理
if r.AdminVerificationHandler != nil {
admin.GET("/verifications", middleware.Authorize(r.CasbinService, "admin/verifications", "read"), r.AdminVerificationHandler.ListVerifications)
admin.GET("/verifications/:id", middleware.Authorize(r.CasbinService, "admin/verifications", "read"), r.AdminVerificationHandler.GetVerificationDetail)
admin.PUT("/verifications/:id/review", middleware.Authorize(r.CasbinService, "admin/verifications", "write"), r.AdminVerificationHandler.ReviewVerification)
}
// 用户资料审核管理
if r.AdminProfileAuditHandler != nil {
admin.GET("/profile-audits", middleware.Authorize(r.CasbinService, "admin/profile_audits", "read"), r.AdminProfileAuditHandler.GetPendingList)
admin.PUT("/profile-audits/:id/moderate", middleware.Authorize(r.CasbinService, "admin/profile_audits", "write"), r.AdminProfileAuditHandler.ModerateAudit)
admin.POST("/profile-audits/batch-moderate", middleware.Authorize(r.CasbinService, "admin/profile_audits", "write"), r.AdminProfileAuditHandler.BatchModerate)
}
}
}