feat(notifications): add documentTitle to NotificationDTO via DocumentService lookup
Notification rows in the history page need the document title. Added findTitlesByIds(Collection<UUID>) to DocumentService (one query via a new JPQL projection on DocumentRepository). NotificationService.getNotifications() now fetches all titles for the page in a single extra query and maps them into the DTO. documentTitle is null when the document has been deleted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,5 +14,6 @@ public record NotificationDTO(
|
|||||||
UUID annotationId,
|
UUID annotationId,
|
||||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) boolean read,
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) boolean read,
|
||||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) LocalDateTime createdAt,
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) LocalDateTime createdAt,
|
||||||
String actorName
|
String actorName,
|
||||||
|
String documentTitle
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import org.springframework.data.repository.query.Param;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -44,6 +46,9 @@ public interface DocumentRepository extends JpaRepository<Document, UUID>, JpaSp
|
|||||||
|
|
||||||
List<Document> findByFileHashIsNullAndFilePathIsNotNull();
|
List<Document> findByFileHashIsNullAndFilePathIsNotNull();
|
||||||
|
|
||||||
|
@Query("SELECT d.id, d.title FROM Document d WHERE d.id IN :ids")
|
||||||
|
List<Object[]> findIdAndTitleByIdIn(@Param("ids") Collection<UUID> ids);
|
||||||
|
|
||||||
long countByMetadataCompleteFalse();
|
long countByMetadataCompleteFalse();
|
||||||
|
|
||||||
List<Document> findByMetadataCompleteFalse(Sort sort);
|
List<Document> findByMetadataCompleteFalse(Sort sort);
|
||||||
|
|||||||
@@ -25,8 +25,11 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -46,6 +49,15 @@ public class DocumentService {
|
|||||||
|
|
||||||
public record StoreResult(Document document, boolean isNew) {}
|
public record StoreResult(Document document, boolean isNew) {}
|
||||||
|
|
||||||
|
public Map<UUID, String> findTitlesByIds(Collection<UUID> ids) {
|
||||||
|
if (ids.isEmpty()) return Map.of();
|
||||||
|
Map<UUID, String> titles = new HashMap<>();
|
||||||
|
for (Object[] row : documentRepository.findIdAndTitleByIdIn(ids)) {
|
||||||
|
titles.put((UUID) row[0], (String) row[1]);
|
||||||
|
}
|
||||||
|
return titles;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt eine Datei hoch.
|
* Lädt eine Datei hoch.
|
||||||
* - Prüft, ob ein Eintrag (aus Excel) schon existiert.
|
* - Prüft, ob ein Eintrag (aus Excel) schon existiert.
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class NotificationControllerTest {
|
|||||||
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
AppUser user = AppUser.builder().id(USER_ID).username("testuser").build();
|
||||||
NotificationDTO dto = new NotificationDTO(
|
NotificationDTO dto = new NotificationDTO(
|
||||||
UUID.randomUUID(), NotificationType.REPLY, UUID.randomUUID(),
|
UUID.randomUUID(), NotificationType.REPLY, UUID.randomUUID(),
|
||||||
UUID.randomUUID(), null, false, LocalDateTime.now(), "Anna Smith");
|
UUID.randomUUID(), null, false, LocalDateTime.now(), "Anna Smith", "Testdokument");
|
||||||
|
|
||||||
when(userService.findByUsername("testuser")).thenReturn(user);
|
when(userService.findByUsername("testuser")).thenReturn(user);
|
||||||
when(notificationService.getNotifications(eq(USER_ID), any(), any(), any()))
|
when(notificationService.getNotifications(eq(USER_ID), any(), any(), any()))
|
||||||
@@ -123,6 +123,20 @@ class NotificationControllerTest {
|
|||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||||
|
void getNotifications_returns400_whenSizeExceedsMaximum() throws Exception {
|
||||||
|
mockMvc.perform(get("/api/notifications").param("size", "200"))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(username = "testuser", authorities = {"READ_ALL"})
|
||||||
|
void getNotifications_returns400_whenSizeIsZero() throws Exception {
|
||||||
|
mockMvc.perform(get("/api/notifications").param("size", "0"))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
}
|
||||||
|
|
||||||
// ─── POST /api/notifications/read-all ────────────────────────────────────
|
// ─── POST /api/notifications/read-all ────────────────────────────────────
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user