diff --git a/internal/controller/admin/admin_controller.go b/internal/controller/admin/admin_controller.go index f32ec00..d1e3a8e 100644 --- a/internal/controller/admin/admin_controller.go +++ b/internal/controller/admin/admin_controller.go @@ -29,10 +29,17 @@ func (ac *AdminController) Dashboard(c *gin.Context) { if err != nil { totalUsers = 0 } + postStats, err := ac.adminService.GetPostStats() + if err != nil { + postStats = &service.PostStats{} + } storeMetrics := ac.adminService.GetStoreMetrics() c.HTML(http.StatusOK, "admin/dashboard/index.html", common.BuildAdminPageData(c, gin.H{ "Title": "管理首页", "TotalUsers": totalUsers, + "PostTotal": postStats.Total, + "PostApproved": postStats.Approved, + "PostPending": postStats.Pending, "StoreType": storeMetrics.Type, "StoreHealthy": storeMetrics.Healthy, "StoreDegraded": storeMetrics.Degraded, diff --git a/internal/controller/admin/interfaces.go b/internal/controller/admin/interfaces.go index cd12271..14e2337 100644 --- a/internal/controller/admin/interfaces.go +++ b/internal/controller/admin/interfaces.go @@ -6,12 +6,13 @@ import ( "metazone.cc/metalab/internal/session" ) -// adminUseCase AdminController 对 AdminService 的最小依赖(ISP:5 个方法) +// adminUseCase AdminController 对 AdminService 的最小依赖(ISP:6 个方法) type adminUseCase interface { ListUsers(params service.ListUsersParams) (*service.ListUsersResult, error) UpdateUserStatus(operatorUID, targetUID uint, newStatus string) error ResetUserToken(operatorUID, targetUID uint) error CountUsers() (int64, error) + GetPostStats() (*service.PostStats, error) GetStoreMetrics() session.StoreMetrics } diff --git a/internal/repository/post_repo.go b/internal/repository/post_repo.go index fece9d9..b60fc37 100644 --- a/internal/repository/post_repo.go +++ b/internal/repository/post_repo.go @@ -137,6 +137,24 @@ func (r *PostRepo) SoftDelete(id uint) error { return r.db.Delete(&model.Post{}, id).Error } +// CountPostsByStatus 统计帖子数量(status 为空则统计全部未删除帖子) +func (r *PostRepo) CountPostsByStatus(status string) (int64, error) { + query := r.buildQuery("", status) + var total int64 + err := query.Count(&total).Error + return total, err +} + +// CountPending 统计待审核帖子数(pending 状态 + approved 但有待审修订) +func (r *PostRepo) CountPending() (int64, error) { + var total int64 + err := r.db.Table("posts"). + Where("deleted_at IS NULL"). + Where("status = ? OR (status = ? AND pending_body != '')", model.PostStatusPending, model.PostStatusApproved). + Count(&total).Error + return total, err +} + // Restore 恢复软删除 func (r *PostRepo) Restore(id uint) error { result := r.db.Unscoped().Model(&model.Post{}).Where("id = ?", id).Update("deleted_at", nil) diff --git a/internal/router/deps_core.go b/internal/router/deps_core.go index 6c43439..3e834c6 100644 --- a/internal/router/deps_core.go +++ b/internal/router/deps_core.go @@ -34,7 +34,7 @@ type dependencies struct { func buildDepsCore(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSettings, redisClient *redis.Client) ( *repository.UserRepo, *session.Manager, *service.AuthService, *middleware.RateLimiter, *controller.AuthController, - *service.AvatarService, *middleware.AuthMiddleware, *adminCtrl.AdminController, + *service.AvatarService, *middleware.AuthMiddleware, ) { userRepo := repository.NewUserRepo(db) @@ -65,6 +65,5 @@ func buildDepsCore(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSet authCtrl := controller.NewAuthController(authService, sessionMgr, rateLimiter, cfg) avatarSvc := service.NewAvatarService(userRepo) authMdw := middleware.NewAuthMiddleware(cfg, sessionMgr) - adminController := adminCtrl.NewAdminController(service.NewAdminService(userRepo, sessionMgr)) - return userRepo, sessionMgr, authService, rateLimiter, authCtrl, avatarSvc, authMdw, adminController + return userRepo, sessionMgr, authService, rateLimiter, authCtrl, avatarSvc, authMdw } diff --git a/internal/router/deps_extra.go b/internal/router/deps_extra.go index 46fa32d..c53b8ce 100644 --- a/internal/router/deps_extra.go +++ b/internal/router/deps_extra.go @@ -13,7 +13,7 @@ import ( ) func buildDeps(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSettings, redisClient *redis.Client) *dependencies { - userRepo, sessionMgr, authService, _, authCtrl, avatarSvc, authMdw, adminController := buildDepsCore(db, cfg, siteSettings, redisClient) + userRepo, sessionMgr, authService, _, authCtrl, avatarSvc, authMdw := buildDepsCore(db, cfg, siteSettings, redisClient) auditRepo := repository.NewAuditRepo(db) notificationRepo := repository.NewNotificationRepo(db) @@ -37,6 +37,7 @@ func buildDeps(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSetting shortcodeSvc := service.NewShortcodeService() postCtrl := controller.NewPostController(postService, shortcodeSvc) adminPostCtrl := adminCtrl.NewAdminPostController(postService) + adminController := adminCtrl.NewAdminController(service.NewAdminService(userRepo, postRepo, sessionMgr)) studioCtrl := controller.NewStudioController(postService) // 用户空间 diff --git a/internal/service/admin_service.go b/internal/service/admin_service.go index aaa78ae..fece2b4 100644 --- a/internal/service/admin_service.go +++ b/internal/service/admin_service.go @@ -8,12 +8,20 @@ import ( type AdminService struct { userRepo userAdminStore + postRepo postAdminStatStore sessionManager *session.Manager } +// PostStats 帖子统计结果(管理首页用) +type PostStats struct { + Total int64 // 全部未删除帖子数 + Approved int64 // 已发布数 + Pending int64 // 待审核数(pending + 待审修订) +} + // NewAdminService 构造函数 -func NewAdminService(userRepo userAdminStore, sm *session.Manager) *AdminService { - return &AdminService{userRepo: userRepo, sessionManager: sm} +func NewAdminService(userRepo userAdminStore, postRepo postAdminStatStore, sm *session.Manager) *AdminService { + return &AdminService{userRepo: userRepo, postRepo: postRepo, sessionManager: sm} } // ListUsersParams 用户列表查询参数 @@ -112,6 +120,23 @@ func (s *AdminService) CountUsers() (int64, error) { return s.userRepo.CountSearchUsers("", "", "") } +// GetPostStats 获取帖子统计(管理首页用) +func (s *AdminService) GetPostStats() (*PostStats, error) { + total, err := s.postRepo.CountPostsByStatus("") + if err != nil { + return nil, err + } + approved, err := s.postRepo.CountPostsByStatus(model.PostStatusApproved) + if err != nil { + return nil, err + } + pending, err := s.postRepo.CountPending() + if err != nil { + return nil, err + } + return &PostStats{Total: total, Approved: approved, Pending: pending}, nil +} + // GetStoreMetrics 返回会话存储状态信息(管理首页用) func (s *AdminService) GetStoreMetrics() session.StoreMetrics { return s.sessionManager.GetStoreMetrics() diff --git a/internal/service/repository.go b/internal/service/repository.go index e643666..2257ffb 100644 --- a/internal/service/repository.go +++ b/internal/service/repository.go @@ -47,6 +47,12 @@ type spacePostStore interface { FindByUserID(userID uint, offset, limit int) ([]model.Post, int64, error) } +// postAdminStatStore AdminService 所需的帖子统计接口(ISP:2 个方法) +type postAdminStatStore interface { + CountPostsByStatus(status string) (int64, error) + CountPending() (int64, error) +} + // postNotifier PostService 所需的通知服务接口 type postNotifier interface { NotifyPostApproved(userID uint, postTitle string, postID uint) diff --git a/templates/admin/html/dashboard/index.html b/templates/admin/html/dashboard/index.html index 15c91d1..736f65d 100644 --- a/templates/admin/html/dashboard/index.html +++ b/templates/admin/html/dashboard/index.html @@ -11,7 +11,11 @@