refactor(ocr): route block lifecycle through TranscriptionService

OcrAsyncRunner was bypassing TranscriptionService — building blocks
directly and calling blockRepository.save(), skipping sanitizeText()
and saveVersion(). Also replaced N individual deleteBlock() calls with
a single bulk deleteAllBlocksByDocument() for OCR re-runs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-13 12:27:01 +02:00
parent 6a0fd25662
commit caae2ead81
4 changed files with 134 additions and 41 deletions

View File

@@ -6,7 +6,6 @@ import org.raddatz.familienarchiv.dto.CreateAnnotationDTO;
import org.raddatz.familienarchiv.model.*;
import org.raddatz.familienarchiv.repository.OcrJobDocumentRepository;
import org.raddatz.familienarchiv.repository.OcrJobRepository;
import org.raddatz.familienarchiv.repository.TranscriptionBlockRepository;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@@ -26,7 +25,6 @@ public class OcrAsyncRunner {
private final DocumentService documentService;
private final TranscriptionService transcriptionService;
private final AnnotationService annotationService;
private final TranscriptionBlockRepository blockRepository;
private final FileService fileService;
private final OcrJobRepository ocrJobRepository;
private final OcrJobDocumentRepository ocrJobDocumentRepository;
@@ -194,10 +192,7 @@ public class OcrAsyncRunner {
}
private void clearExistingBlocks(UUID documentId) {
List<TranscriptionBlock> existing = transcriptionService.listBlocks(documentId);
for (TranscriptionBlock block : existing) {
transcriptionService.deleteBlock(documentId, block.getId());
}
transcriptionService.deleteAllBlocksByDocument(documentId);
}
private void createTranscriptionBlocks(UUID documentId, List<OcrBlockResult> blocks,
@@ -216,15 +211,7 @@ public class OcrAsyncRunner {
DocumentAnnotation annotation = annotationService.createOcrAnnotation(
documentId, annotationDTO, userId, fileHash, block.polygon());
TranscriptionBlock transcriptionBlock = TranscriptionBlock.builder()
.annotationId(annotation.getId())
.documentId(documentId)
.text(block.text() != null ? block.text() : "")
.sortOrder(sortOrder)
.source(BlockSource.OCR)
.createdBy(userId)
.updatedBy(userId)
.build();
blockRepository.save(transcriptionBlock);
transcriptionService.createOcrBlock(documentId, annotation.getId(),
block.text(), sortOrder, userId);
}
}

View File

@@ -8,6 +8,7 @@ import org.raddatz.familienarchiv.dto.ReorderTranscriptionBlocksDTO;
import org.raddatz.familienarchiv.dto.UpdateTranscriptionBlockDTO;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.model.BlockSource;
import org.raddatz.familienarchiv.model.Document;
import org.raddatz.familienarchiv.model.DocumentAnnotation;
import org.raddatz.familienarchiv.model.TranscriptionBlock;
@@ -75,6 +76,24 @@ public class TranscriptionService {
return saved;
}
@Transactional
public TranscriptionBlock createOcrBlock(UUID documentId, UUID annotationId,
String text, int sortOrder, UUID userId) {
String sanitized = sanitizeText(text);
TranscriptionBlock block = TranscriptionBlock.builder()
.annotationId(annotationId)
.documentId(documentId)
.text(sanitized)
.sortOrder(sortOrder)
.source(BlockSource.OCR)
.createdBy(userId)
.updatedBy(userId)
.build();
TranscriptionBlock saved = blockRepository.save(block);
saveVersion(saved, userId);
return saved;
}
@Transactional
public TranscriptionBlock updateBlock(UUID documentId, UUID blockId,
UpdateTranscriptionBlockDTO dto, UUID userId) {
@@ -106,6 +125,21 @@ public class TranscriptionService {
blockId, annotationId, documentId);
}
@Transactional
public void deleteAllBlocksByDocument(UUID documentId) {
List<TranscriptionBlock> blocks = blockRepository.findByDocumentIdOrderBySortOrderAsc(documentId);
if (blocks.isEmpty()) return;
List<UUID> annotationIds = blocks.stream()
.map(TranscriptionBlock::getAnnotationId)
.toList();
blockRepository.deleteAll(blocks);
blockRepository.flush();
annotationRepository.deleteAllById(annotationIds);
log.info("Bulk-deleted {} transcription blocks for document {}", blocks.size(), documentId);
}
@Transactional
public void reorderBlocks(UUID documentId, ReorderTranscriptionBlocksDTO dto) {
List<UUID> blockIds = dto.getBlockIds();