refactor(ocr): move async training dispatch out of controller into SenderModelService
Controller was deciding when to fire runSenderTraining based on the returned run status — a business rule that belongs in the service. Introduces @Lazy self-reference to preserve @Async proxy dispatch without self-invocation bypassing Spring AOP. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,6 @@ import org.raddatz.familienarchiv.dto.TriggerSenderTrainingDTO;
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.raddatz.familienarchiv.model.OcrJob;
|
||||
import org.raddatz.familienarchiv.model.OcrTrainingRun;
|
||||
import org.raddatz.familienarchiv.model.TrainingStatus;
|
||||
import org.raddatz.familienarchiv.security.Permission;
|
||||
import org.raddatz.familienarchiv.security.RequirePermission;
|
||||
import org.raddatz.familienarchiv.service.OcrBatchService;
|
||||
@@ -156,11 +155,7 @@ public class OcrController {
|
||||
@ResponseStatus(HttpStatus.ACCEPTED)
|
||||
@RequirePermission(Permission.ADMIN)
|
||||
public OcrTrainingRun triggerSenderTraining(@Valid @RequestBody TriggerSenderTrainingDTO dto) {
|
||||
OcrTrainingRun run = senderModelService.triggerManualSenderTraining(dto.personId());
|
||||
if (run.getStatus() == TrainingStatus.RUNNING) {
|
||||
senderModelService.runSenderTraining(dto.personId());
|
||||
}
|
||||
return run;
|
||||
return senderModelService.triggerManualSenderTraining(dto.personId());
|
||||
}
|
||||
|
||||
private UUID resolveUserId(Authentication authentication) {
|
||||
|
||||
@@ -9,7 +9,9 @@ import org.raddatz.familienarchiv.repository.OcrTrainingRunRepository;
|
||||
import org.raddatz.familienarchiv.repository.SenderModelRepository;
|
||||
import org.raddatz.familienarchiv.repository.TranscriptionBlockRepository;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -35,6 +37,11 @@ public class SenderModelService {
|
||||
private final TrainingDataExportService trainingDataExportService;
|
||||
private final PersonService personService;
|
||||
|
||||
// Self-reference through the Spring proxy so @Async is honoured on self-calls.
|
||||
@Lazy
|
||||
@Autowired
|
||||
private SenderModelService self;
|
||||
|
||||
@Value("${ocr.sender-model.activation-threshold:100}")
|
||||
private int activationThreshold;
|
||||
|
||||
@@ -56,9 +63,13 @@ public class SenderModelService {
|
||||
long correctedLines = blockRepository.countManualKurrentBlocksByPerson(personId);
|
||||
boolean runNow = runOrQueueSenderTraining(personId, (int) correctedLines);
|
||||
TrainingStatus targetStatus = runNow ? TrainingStatus.RUNNING : TrainingStatus.QUEUED;
|
||||
return trainingRunRepository.findFirstByPersonIdAndStatus(personId, targetStatus)
|
||||
OcrTrainingRun run = trainingRunRepository.findFirstByPersonIdAndStatus(personId, targetStatus)
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"Expected " + targetStatus + " row for person " + personId));
|
||||
if (runNow) {
|
||||
self.runSenderTraining(personId);
|
||||
}
|
||||
return run;
|
||||
}
|
||||
|
||||
@Async
|
||||
|
||||
Reference in New Issue
Block a user