feat: 实现通知管理功能,包括模型、仓库、服务和API处理器。
This commit is contained in:
@@ -233,6 +233,24 @@ func (h *NotificationHandler) BroadcastNotification(c *gin.Context) {
|
|||||||
api.Success(c, gin.H{"message": "Notification broadcast started"})
|
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
|
// RegisterUserRoutes registers notification routes for regular users
|
||||||
func (h *NotificationHandler) RegisterUserRoutes(rg *gin.RouterGroup) {
|
func (h *NotificationHandler) RegisterUserRoutes(rg *gin.RouterGroup) {
|
||||||
notifications := rg.Group("/notifications")
|
notifications := rg.Group("/notifications")
|
||||||
@@ -251,5 +269,6 @@ func (h *NotificationHandler) RegisterAdminRoutes(rg *gin.RouterGroup) {
|
|||||||
{
|
{
|
||||||
notifications.POST("", h.CreateNotification)
|
notifications.POST("", h.CreateNotification)
|
||||||
notifications.POST("/broadcast", h.BroadcastNotification)
|
notifications.POST("/broadcast", h.BroadcastNotification)
|
||||||
|
notifications.GET("/announcements", h.GetBroadcastHistory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,3 +37,17 @@ type Notification struct {
|
|||||||
func (Notification) TableName() string {
|
func (Notification) TableName() string {
|
||||||
return "notifications"
|
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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -150,3 +150,30 @@ func (r *NotificationRepository) GetUnreadCount(userID uint) (int64, error) {
|
|||||||
}
|
}
|
||||||
return count, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -167,13 +167,26 @@ type BroadcastNotificationInput struct {
|
|||||||
Content string `json:"content" binding:"required"`
|
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 {
|
func (s *NotificationService) BroadcastNotification(input BroadcastNotificationInput) error {
|
||||||
userIDs, err := s.userRepo.GetAllActiveUserIDs()
|
userIDs, err := s.userRepo.GetAllActiveUserIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get active user IDs: %w", err)
|
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 {
|
for _, userID := range userIDs {
|
||||||
// Ignore errors for individual users to ensure best-effort delivery
|
// Ignore errors for individual users to ensure best-effort delivery
|
||||||
_, _ = s.CreateNotification(CreateNotificationInput{
|
_, _ = s.CreateNotification(CreateNotificationInput{
|
||||||
@@ -186,3 +199,28 @@ func (s *NotificationService) BroadcastNotification(input BroadcastNotificationI
|
|||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user