zminay w alertach cenowych

This commit is contained in:
2026-08-10 18:34:26 +02:00
parent c464c5f708
commit b30a0b9b95
9 changed files with 2577 additions and 516 deletions
@@ -2,15 +2,19 @@ package pl.polskalokalnie.notification;
import jakarta.validation.Valid;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@RestController
@@ -45,6 +49,20 @@ public class NotificationController {
return ResponseEntity.noContent().build();
}
// Sprzatanie po skasowanym alercie: usuwa powiadomienia o kluczu zaczynajacym sie od prefiksu.
// Zakres to zawsze wlasne powiadomienia - odbiorce bierzemy z tokenu, nie z zadania.
@DeleteMapping
public ResponseEntity<Void> deleteByDedupePrefix(
@RequestParam("dedupePrefix") String dedupePrefix,
Authentication authentication
) {
if (dedupePrefix == null || dedupePrefix.isBlank()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Podaj prefiks klucza powiadomienia");
}
notificationService.deleteByDedupePrefix(authentication.getName(), dedupePrefix);
return ResponseEntity.noContent().build();
}
// Zdarzenia liczone po stronie klienta (alerty cenowe, spotkania, ulubione, weryfikacja telefonu,
// dopasowane oferty). Powiadomienie zawsze trafia do zalogowanego uzytkownika (serwer ustawia odbiorce).
@PostMapping
@@ -17,4 +17,11 @@ public interface NotificationRepository extends JpaRepository<Notification, Long
@Modifying
@Query("update Notification n set n.read = true where lower(n.userEmail) = lower(:email) and n.read = false")
int markAllRead(@Param("email") String email);
// Kasowanie powiadomien powiazanych z jednym alertem - klucze deduplikacji maja wspolny
// prefiks (np. "price-12-"). Zawsze zawezone do wlasciciela z tokenu.
@Modifying
@Query("delete from Notification n where lower(n.userEmail) = lower(:email) "
+ "and n.dedupeKey is not null and n.dedupeKey like :pattern escape '\\'")
int deleteByDedupeKeyPattern(@Param("email") String email, @Param("pattern") String pattern);
}
@@ -9,6 +9,9 @@ public record NotificationResponse(
String title,
String body,
String link,
// Klucz deduplikacji wraca do klienta, zeby po skasowaniu alertu mogl od razu
// zdjac powiazane powiadomienia z listy, nie czekajac na ponowne pobranie.
String dedupeKey,
boolean read,
Instant createdAt
) {
@@ -20,6 +23,7 @@ public record NotificationResponse(
n.getTitle(),
n.getBody(),
n.getLink(),
n.getDedupeKey(),
n.isRead(),
n.getCreatedAt()
);
@@ -76,6 +76,24 @@ public class NotificationService {
}
}
/**
* Usuwa powiadomienia uzytkownika, ktorych klucz deduplikacji zaczyna sie od podanego prefiksu.
* Sluzy do sprzatania po skasowanym alercie cenowym - po usunieciu alertu nie ma zostac po nim
* slad w centrum powiadomien. Znaki specjalne LIKE sa escapowane, wiec prefiks nie moze
* "rozlac sie" na inne powiadomienia.
*/
@Transactional
public int deleteByDedupePrefix(String email, String prefix) {
if (email == null || email.isBlank() || prefix == null || prefix.isBlank()) {
return 0;
}
String escaped = prefix.trim()
.replace("\\", "\\\\")
.replace("%", "\\%")
.replace("_", "\\_");
return repository.deleteByDedupeKeyPattern(email, escaped + "%");
}
@Transactional
public void markAllRead(String email) {
repository.markAllRead(email);