Implements announcements end-to-end (admin CRUD + read status, user list + mark read) with OR-of-AND targeting. Also breaks the ent<->service import cycle by moving schema-facing constants/targeting into a new domain package.
83 lines
2.1 KiB
Go
83 lines
2.1 KiB
Go
package handler
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/handler/dto"
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
|
|
middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// AnnouncementHandler handles user announcement operations
|
|
type AnnouncementHandler struct {
|
|
announcementService *service.AnnouncementService
|
|
}
|
|
|
|
// NewAnnouncementHandler creates a new user announcement handler
|
|
func NewAnnouncementHandler(announcementService *service.AnnouncementService) *AnnouncementHandler {
|
|
return &AnnouncementHandler{
|
|
announcementService: announcementService,
|
|
}
|
|
}
|
|
|
|
// List handles listing announcements visible to current user
|
|
// GET /api/v1/announcements
|
|
func (h *AnnouncementHandler) List(c *gin.Context) {
|
|
subject, ok := middleware2.GetAuthSubjectFromContext(c)
|
|
if !ok {
|
|
response.Unauthorized(c, "User not found in context")
|
|
return
|
|
}
|
|
|
|
unreadOnly := parseBoolQuery(c.Query("unread_only"))
|
|
|
|
items, err := h.announcementService.ListForUser(c.Request.Context(), subject.UserID, unreadOnly)
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
|
|
out := make([]dto.UserAnnouncement, 0, len(items))
|
|
for i := range items {
|
|
out = append(out, *dto.UserAnnouncementFromService(&items[i]))
|
|
}
|
|
response.Success(c, out)
|
|
}
|
|
|
|
// MarkRead marks an announcement as read for current user
|
|
// POST /api/v1/announcements/:id/read
|
|
func (h *AnnouncementHandler) MarkRead(c *gin.Context) {
|
|
subject, ok := middleware2.GetAuthSubjectFromContext(c)
|
|
if !ok {
|
|
response.Unauthorized(c, "User not found in context")
|
|
return
|
|
}
|
|
|
|
announcementID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil || announcementID <= 0 {
|
|
response.BadRequest(c, "Invalid announcement ID")
|
|
return
|
|
}
|
|
|
|
if err := h.announcementService.MarkRead(c.Request.Context(), subject.UserID, announcementID); err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
|
|
response.Success(c, gin.H{"message": "ok"})
|
|
}
|
|
|
|
func parseBoolQuery(v string) bool {
|
|
switch strings.TrimSpace(strings.ToLower(v)) {
|
|
case "1", "true", "yes", "y", "on":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|