269 lines
7.2 KiB
Go
269 lines
7.2 KiB
Go
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 = ¬ifType
|
|
}
|
|
|
|
// 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 current page (1-indexed)
|
|
currentPage := 1
|
|
if result.Limit > 0 {
|
|
currentPage = (result.Offset / result.Limit) + 1
|
|
}
|
|
|
|
api.Success(c, gin.H{
|
|
"notifications": result.Notifications,
|
|
"total": result.Total,
|
|
"page": currentPage,
|
|
"limit": result.Limit,
|
|
})
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// CreateNotification handles POST /api/v1/notifications
|
|
// Creates a new notification (Admin/System use)
|
|
func (h *NotificationHandler) CreateNotification(c *gin.Context) {
|
|
// TODO: Add admin authorization check
|
|
|
|
var input service.CreateNotificationInput
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
api.BadRequest(c, "Invalid request body: "+err.Error())
|
|
return
|
|
}
|
|
|
|
notification, err := h.notificationService.CreateNotification(input)
|
|
if err != nil {
|
|
api.InternalError(c, "Failed to create notification: "+err.Error())
|
|
return
|
|
}
|
|
|
|
api.Created(c, notification)
|
|
}
|
|
|
|
// BroadcastNotification handles POST /api/v1/notifications/broadcast
|
|
// Sends a notification to all active users
|
|
func (h *NotificationHandler) BroadcastNotification(c *gin.Context) {
|
|
// TODO: Add strict admin authorization check
|
|
|
|
var input service.BroadcastNotificationInput
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
api.BadRequest(c, "Invalid request body: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Validate input
|
|
if input.Title == "" || input.Content == "" {
|
|
api.BadRequest(c, "Title and content are required")
|
|
return
|
|
}
|
|
if input.Type == "" {
|
|
input.Type = models.NotificationTypeSystem
|
|
}
|
|
|
|
if err := h.notificationService.BroadcastNotification(input); err != nil {
|
|
api.InternalError(c, "Failed to broadcast notification: "+err.Error())
|
|
return
|
|
}
|
|
|
|
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")
|
|
{
|
|
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)
|
|
}
|
|
}
|
|
|
|
// RegisterAdminRoutes registers notification routes for admin/system use
|
|
func (h *NotificationHandler) RegisterAdminRoutes(rg *gin.RouterGroup) {
|
|
notifications := rg.Group("/notifications")
|
|
{
|
|
notifications.POST("", h.CreateNotification)
|
|
notifications.POST("/broadcast", h.BroadcastNotification)
|
|
notifications.GET("/announcements", h.GetBroadcastHistory)
|
|
}
|
|
}
|