2026-05-04 11:20:53 +07:00

97 lines
3.8 KiB
Java

package com.walkguide.service;
import com.walkguide.dto.request.SendNotificationRequest;
import com.walkguide.dto.response.NotificationResponse;
import com.walkguide.entity.GuardianNotification;
import com.walkguide.enums.NotificationType;
import com.walkguide.enums.PairingStatus;
import com.walkguide.exception.PairingException;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.GuardianNotificationRepository;
import com.walkguide.repository.PairingRelationRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class NotificationService {
private final GuardianNotificationRepository notifRepository;
private final PairingRelationRepository pairingRelationRepository;
private final FcmService fcmService;
@Transactional
public NotificationResponse sendNotification(Long guardianId, SendNotificationRequest req) {
var pairing = pairingRelationRepository
.findByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)
.orElseThrow(() -> new PairingException("Tidak ada user yang dipair"));
Long userId = pairing.getUser().getId();
NotificationType type = "VOICE_NOTE".equalsIgnoreCase(req.getNotifType())
? NotificationType.VOICE_NOTE : NotificationType.TEXT;
GuardianNotification notif = GuardianNotification.builder()
.guardianId(guardianId)
.userId(userId)
.notifType(type)
.content(req.getContent())
.voiceNoteUrl(req.getVoiceNoteUrl())
.voiceNoteDuration(req.getVoiceNoteDuration())
.build();
notif = notifRepository.save(notif);
// FCM ke user
String fcmToken = pairing.getUser().getFcmToken();
String fcmBody = type == NotificationType.TEXT
? req.getContent() : "Voice note from Guardian";
fcmService.sendToToken(fcmToken,
"Pesan dari Guardian",
fcmBody,
Map.of("type", "NOTIFICATION",
"notifId", String.valueOf(notif.getId()),
"notifType", type.name(),
"voiceNoteUrl", req.getVoiceNoteUrl() != null ? req.getVoiceNoteUrl() : ""));
return toResponse(notif);
}
public Page<NotificationResponse> getNotifications(Long userId, Pageable pageable) {
return notifRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable)
.map(this::toResponse);
}
public long getUnreadCount(Long userId) {
return notifRepository.countByUserIdAndIsReadFalse(userId);
}
@Transactional
public void markAllRead(Long userId) {
var unread = notifRepository.findByUserIdAndIsReadFalseOrderByCreatedAtAsc(userId);
LocalDateTime now = LocalDateTime.now();
unread.forEach(n -> { n.setIsRead(true); n.setReadAt(now); });
notifRepository.saveAll(unread);
}
@Transactional
public void markOneRead(Long notifId) {
notifRepository.findById(notifId).ifPresent(n -> {
n.setIsRead(true);
n.setReadAt(LocalDateTime.now());
notifRepository.save(n);
});
}
private NotificationResponse toResponse(GuardianNotification n) {
return NotificationResponse.builder()
.id(n.getId()).notifType(n.getNotifType().name()).content(n.getContent())
.voiceNoteUrl(n.getVoiceNoteUrl()).voiceNoteDuration(n.getVoiceNoteDuration())
.isRead(n.getIsRead()).readAt(n.getReadAt()).createdAt(n.getCreatedAt()).build();
}
}