From 734ff85185169919ce50bdd9682d3397e065137f Mon Sep 17 00:00:00 2001 From: admin <1297598740@qq.com> Date: Thu, 29 Jan 2026 13:53:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E3=80=81=E4=BB=93=E5=BA=93=E3=80=81=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=92=8CAPI=E5=A4=84=E7=90=86=E5=99=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/notification_handler.go | 19 +++++++++ internal/models/notification.go | 14 +++++++ .../repository/notification_repository.go | 27 +++++++++++++ internal/service/notification_service.go | 40 ++++++++++++++++++- 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/internal/handler/notification_handler.go b/internal/handler/notification_handler.go index 938146f..692f85e 100644 --- a/internal/handler/notification_handler.go +++ b/internal/handler/notification_handler.go @@ -233,6 +233,24 @@ func (h *NotificationHandler) BroadcastNotification(c *gin.Context) { api.Success(c, gin.H{"message": "Notification broadcast started"}) } +// GetBroadcastHistory handles GET /api/v1/notifications/announcements +// Returns a list of system broadcast history +func (h *NotificationHandler) GetBroadcastHistory(c *gin.Context) { + limitStr := c.DefaultQuery("limit", "10") + offsetStr := c.DefaultQuery("offset", "0") + + limit, _ := strconv.Atoi(limitStr) + offset, _ := strconv.Atoi(offsetStr) + + result, err := h.notificationService.ListAnnouncements(limit, offset) + if err != nil { + api.InternalError(c, "Failed to get broadcast history: "+err.Error()) + return + } + + api.Success(c, result) +} + // RegisterUserRoutes registers notification routes for regular users func (h *NotificationHandler) RegisterUserRoutes(rg *gin.RouterGroup) { notifications := rg.Group("/notifications") @@ -251,5 +269,6 @@ func (h *NotificationHandler) RegisterAdminRoutes(rg *gin.RouterGroup) { { notifications.POST("", h.CreateNotification) notifications.POST("/broadcast", h.BroadcastNotification) + notifications.GET("/announcements", h.GetBroadcastHistory) } } diff --git a/internal/models/notification.go b/internal/models/notification.go index f57d333..8ca32c6 100644 --- a/internal/models/notification.go +++ b/internal/models/notification.go @@ -37,3 +37,17 @@ type Notification struct { func (Notification) TableName() string { return "notifications" } + +// SystemAnnouncement represents a broadcast sent to all users +type SystemAnnouncement struct { + BaseModel + Title string `gorm:"size:200;not null" json:"title"` + Content string `gorm:"type:text;not null" json:"content"` + Type NotificationType `gorm:"size:30;not null;default:'system'" json:"type"` + SentCount int `gorm:"default:0" json:"sent_count"` +} + +// TableName specifies the table name for SystemAnnouncement +func (SystemAnnouncement) TableName() string { + return "system_announcements" +} diff --git a/internal/repository/notification_repository.go b/internal/repository/notification_repository.go index f14a50c..b84b46f 100644 --- a/internal/repository/notification_repository.go +++ b/internal/repository/notification_repository.go @@ -150,3 +150,30 @@ func (r *NotificationRepository) GetUnreadCount(userID uint) (int64, error) { } return count, nil } + +// CreateAnnouncement creates a new system announcement in the database +func (r *NotificationRepository) CreateAnnouncement(announcement *models.SystemAnnouncement) error { + if err := r.db.Create(announcement).Error; err != nil { + return fmt.Errorf("failed to create announcement: %w", err) + } + return nil +} + +// ListAnnouncements retrieves system announcements with pagination +func (r *NotificationRepository) ListAnnouncements(limit, offset int) ([]models.SystemAnnouncement, int64, error) { + var announcements []models.SystemAnnouncement + var total int64 + + if err := r.db.Model(&models.SystemAnnouncement{}).Count(&total).Error; err != nil { + return nil, 0, fmt.Errorf("failed to count announcements: %w", err) + } + + if err := r.db.Order("created_at DESC"). + Limit(limit). + Offset(offset). + Find(&announcements).Error; err != nil { + return nil, 0, fmt.Errorf("failed to list announcements: %w", err) + } + + return announcements, total, nil +} diff --git a/internal/service/notification_service.go b/internal/service/notification_service.go index 2b463e2..1afa09b 100644 --- a/internal/service/notification_service.go +++ b/internal/service/notification_service.go @@ -167,13 +167,26 @@ type BroadcastNotificationInput struct { Content string `json:"content" binding:"required"` } -// BroadcastNotification sends a notification to all active users +// BroadcastNotification sends a notification to all active users and saves to history func (s *NotificationService) BroadcastNotification(input BroadcastNotificationInput) error { userIDs, err := s.userRepo.GetAllActiveUserIDs() if err != nil { return fmt.Errorf("failed to get active user IDs: %w", err) } + // Create announcement record + announcement := &models.SystemAnnouncement{ + Title: input.Title, + Content: input.Content, + Type: input.Type, + SentCount: len(userIDs), + } + if err := s.repo.CreateAnnouncement(announcement); err != nil { + // Log error but continue with broadcasting if possible? + // Actually, better to store the history first. + return fmt.Errorf("failed to save announcement history: %w", err) + } + for _, userID := range userIDs { // Ignore errors for individual users to ensure best-effort delivery _, _ = s.CreateNotification(CreateNotificationInput{ @@ -186,3 +199,28 @@ func (s *NotificationService) BroadcastNotification(input BroadcastNotificationI return nil } + +// AnnouncementListResult represents the result of a paginated announcement list query +type AnnouncementListResult struct { + Announcements []models.SystemAnnouncement `json:"announcements"` + Total int64 `json:"total"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} + +// ListAnnouncements retrieves system announcements with pagination +func (s *NotificationService) ListAnnouncements(limit, offset int) (*AnnouncementListResult, error) { + if limit <= 0 { + limit = 10 + } + announcements, total, err := s.repo.ListAnnouncements(limit, offset) + if err != nil { + return nil, err + } + return &AnnouncementListResult{ + Announcements: announcements, + Total: total, + Limit: limit, + Offset: offset, + }, nil +}