Files
Novault-backend/internal/handler/notification_handler.go

199 lines
5.1 KiB
Go
Raw Normal View History

2026-01-27 18:42:35 +08:00
package handler
import (
"errors"
"strconv"
"accounting-app/internal/models"
"accounting-app/internal/service"
"accounting-app/pkg/api"
"github.com/gin-gonic/gin"
)
// NotificationHandler handles HTTP requests for notification operations
type NotificationHandler struct {
notificationService *service.NotificationService
}
// NewNotificationHandler creates a new NotificationHandler instance
func NewNotificationHandler(notificationService *service.NotificationService) *NotificationHandler {
return &NotificationHandler{
notificationService: notificationService,
}
}
// GetNotifications handles GET /api/v1/notifications
// Returns a list of notifications with pagination and filtering
func (h *NotificationHandler) GetNotifications(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
// Parse query parameters
input := service.NotificationListInput{}
// Parse type filter
if typeStr := c.Query("type"); typeStr != "" {
notifType := models.NotificationType(typeStr)
input.Type = &notifType
}
// Parse is_read filter
if isReadStr := c.Query("is_read"); isReadStr != "" {
isRead := isReadStr == "true"
input.IsRead = &isRead
}
// Parse pagination
if offsetStr := c.Query("offset"); offsetStr != "" {
offset, err := strconv.Atoi(offsetStr)
if err != nil || offset < 0 {
api.BadRequest(c, "Invalid offset")
return
}
input.Offset = offset
}
if limitStr := c.Query("limit"); limitStr != "" {
limit, err := strconv.Atoi(limitStr)
if err != nil || limit < 0 {
api.BadRequest(c, "Invalid limit")
return
}
input.Limit = limit
}
result, err := h.notificationService.ListNotifications(userID.(uint), input)
if err != nil {
api.InternalError(c, "Failed to get notifications: "+err.Error())
return
}
// Calculate total pages
totalPages := 0
if result.Limit > 0 {
totalPages = int((result.Total + int64(result.Limit) - 1) / int64(result.Limit))
}
// Calculate current page (1-indexed)
currentPage := 1
if result.Limit > 0 {
currentPage = (result.Offset / result.Limit) + 1
}
api.SuccessWithMeta(c, result.Notifications, &api.Meta{
Page: currentPage,
PageSize: result.Limit,
TotalCount: result.Total,
TotalPages: totalPages,
})
}
// GetUnreadCount handles GET /api/v1/notifications/unread-count
// Returns the count of unread notifications
func (h *NotificationHandler) GetUnreadCount(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
count, err := h.notificationService.GetUnreadCount(userID.(uint))
if err != nil {
api.InternalError(c, "Failed to get unread count: "+err.Error())
return
}
api.Success(c, gin.H{"count": count})
}
// MarkAsRead handles PUT /api/v1/notifications/:id/read
// Marks a notification as read
func (h *NotificationHandler) MarkAsRead(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid notification ID")
return
}
err = h.notificationService.MarkAsRead(userID.(uint), uint(id))
if err != nil {
if errors.Is(err, service.ErrNotificationNotFound) {
api.NotFound(c, "Notification not found")
return
}
api.InternalError(c, "Failed to mark notification as read: "+err.Error())
return
}
api.Success(c, gin.H{"message": "Notification marked as read"})
}
// MarkAllAsRead handles POST /api/v1/notifications/read-all
// Marks all notifications as read
func (h *NotificationHandler) MarkAllAsRead(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
err := h.notificationService.MarkAllAsRead(userID.(uint))
if err != nil {
api.InternalError(c, "Failed to mark all notifications as read: "+err.Error())
return
}
api.Success(c, gin.H{"message": "All notifications marked as read"})
}
// DeleteNotification handles DELETE /api/v1/notifications/:id
// Deletes a notification
func (h *NotificationHandler) DeleteNotification(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid notification ID")
return
}
err = h.notificationService.DeleteNotification(userID.(uint), uint(id))
if err != nil {
if errors.Is(err, service.ErrNotificationNotFound) {
api.NotFound(c, "Notification not found")
return
}
api.InternalError(c, "Failed to delete notification: "+err.Error())
return
}
api.NoContent(c)
}
// RegisterRoutes registers all notification routes to the given router group
func (h *NotificationHandler) RegisterRoutes(rg *gin.RouterGroup) {
notifications := rg.Group("/notifications")
{
notifications.GET("", h.GetNotifications)
notifications.GET("/unread-count", h.GetUnreadCount)
notifications.PUT("/:id/read", h.MarkAsRead)
notifications.POST("/read-all", h.MarkAllAsRead)
notifications.DELETE("/:id", h.DeleteNotification)
}
}