diff --git a/pom.xml b/pom.xml index 5d37f08..85d43dc 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,10 @@ spring-boot-docker-compose runtime + + org.springframework.boot + spring-boot-starter-validation + diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java new file mode 100644 index 0000000..3992be2 --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/AssignVetRequest.java @@ -0,0 +1,13 @@ +package org.example.vet1177.dto.request.medicalrecord; + + + +import jakarta.validation.constraints.NotNull; + +import java.util.UUID; + +// Tilldela handläggare +public record AssignVetRequest( + @NotNull(message = "Veterinär måste anges") + UUID vetId +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java new file mode 100644 index 0000000..6db18b9 --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/CreateMedicalRecordRequest.java @@ -0,0 +1,24 @@ +package org.example.vet1177.dto.request.medicalrecord; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import java.util.UUID; + +// Skapa nytt ärende +public record CreateMedicalRecordRequest( + @NotBlank(message = "Titel får inte vara tom") + @Size(max = 500, message = "Titel får max vara 500 tecken") + String title, + + @Size(max = 5000, message = "Beskrivning får max vara 5000 tecken") + String description, + + @NotNull(message = "Djur måste anges") + UUID petId, + + @NotNull(message = "Klinik måste anges") + UUID clinicId + +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java new file mode 100644 index 0000000..7437acb --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateMedicalRecordRequest.java @@ -0,0 +1,14 @@ +package org.example.vet1177.dto.request.medicalrecord; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +// Uppdatera titel och beskrivning +public record UpdateMedicalRecordRequest( + @NotBlank(message = "Titel får inte vara tom") + @Size(max = 500, message = "Titel får max vara 500 tecken") + String title, + + @Size(max = 5000, message = "Beskrivning får max vara 5000 tecken") + String description +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java new file mode 100644 index 0000000..d3a53ad --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/request/medicalrecord/UpdateStatusRequest.java @@ -0,0 +1,11 @@ +package org.example.vet1177.dto.request.medicalrecord; + + +import jakarta.validation.constraints.NotNull; +import org.example.vet1177.entities.RecordStatus; + +// Uppdatera status +public record UpdateStatusRequest( + @NotNull(message = "Status måste anges") + RecordStatus status +) {} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java new file mode 100644 index 0000000..a75febb --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordResponse.java @@ -0,0 +1,52 @@ +package org.example.vet1177.dto.response.medicalrecord; + +import org.example.vet1177.entities.MedicalRecord; +import org.example.vet1177.entities.RecordStatus; + +import java.time.Instant; +import java.util.UUID; + +//fullständigt svar för getByID: +public record MedicalRecordResponse( + UUID id, + String title, + String description, + RecordStatus status, + UUID petId, + String petName, + String petSpecies, + UUID ownerId, + String ownerName, + UUID clinicId, + String clinicName, + UUID assignedVetId, + String assignedVetName, + UUID createdById, + String createdByName, + Instant createdAt, + Instant updatedAt, + Instant closedAt +) { + public static MedicalRecordResponse from(MedicalRecord record) { + return new MedicalRecordResponse( + record.getId(), + record.getTitle(), + record.getDescription(), + record.getStatus(), + record.getPet().getId(), + record.getPet().getName(), + record.getPet().getSpecies(), + record.getOwner().getId(), + record.getOwner().getName(), + record.getClinic().getId(), + record.getClinic().getName(), + record.getAssignedVet() != null ? record.getAssignedVet().getId() : null, + record.getAssignedVet() != null ? record.getAssignedVet().getName() : null, + record.getCreatedBy().getId(), + record.getCreatedBy().getName(), + record.getCreatedAt(), + record.getUpdatedAt(), + record.getClosedAt() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java new file mode 100644 index 0000000..89daff4 --- /dev/null +++ b/src/main/java/org/example/vet1177/dto/response/medicalrecord/MedicalRecordSummaryResponse.java @@ -0,0 +1,30 @@ +package org.example.vet1177.dto.response.medicalrecord; + +import org.example.vet1177.entities.MedicalRecord; +import org.example.vet1177.entities.RecordStatus; + +import java.time.Instant; +import java.util.UUID; + +// Förenklat svar som används i listor +public record MedicalRecordSummaryResponse( + UUID id, + String title, + RecordStatus status, + String petName, + String ownerName, + String assignedVetName, + Instant createdAt +) { + public static MedicalRecordSummaryResponse from(MedicalRecord record) { + return new MedicalRecordSummaryResponse( + record.getId(), + record.getTitle(), + record.getStatus(), + record.getPet().getName(), + record.getOwner().getName(), + record.getAssignedVet() != null ? record.getAssignedVet().getName() : null, + record.getCreatedAt() + ); + } +} diff --git a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java index 621224f..88507aa 100644 --- a/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java +++ b/src/main/java/org/example/vet1177/repository/MedicalRecordRepository.java @@ -2,30 +2,34 @@ import org.example.vet1177.entities.MedicalRecord; import org.example.vet1177.entities.RecordStatus; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; import java.util.UUID; @Repository public interface MedicalRecordRepository extends JpaRepository { - // Hämta alla ärenden för ett specifikt djur + @EntityGraph(attributePaths = { + "pet", "owner", "clinic", "assignedVet", "createdBy" + }) + Optional findById(UUID id); + + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByPetId(UUID petId); - // Hämta alla ärenden för en ägare + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByOwnerId(UUID ownerId); - // Hämta alla ärenden tilldelade en specifik vet - List findByAssignedVetId(UUID vetId); - - // Hämta alla ärenden för en klinik + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByClinicId(UUID clinicId); - // Hämta ärenden filtrerat på status - List findByStatus(RecordStatus status); - - // Kombinerat — ärenden för en klinik med specifik status (användbart för ABAC) + @EntityGraph(attributePaths = {"pet", "owner", "clinic", "assignedVet", "createdBy"}) List findByClinicIdAndStatus(UUID clinicId, RecordStatus status); + + List findByAssignedVetId(UUID vetId); + List findByStatus(RecordStatus status); } \ No newline at end of file diff --git a/src/main/java/org/example/vet1177/services/MedicalRecordService.java b/src/main/java/org/example/vet1177/services/MedicalRecordService.java index 2924400..65b0aea 100644 --- a/src/main/java/org/example/vet1177/services/MedicalRecordService.java +++ b/src/main/java/org/example/vet1177/services/MedicalRecordService.java @@ -1,6 +1,8 @@ package org.example.vet1177.services; import org.example.vet1177.entities.*; +import org.example.vet1177.exception.BusinessRuleException; +import org.example.vet1177.exception.ResourceNotFoundException; import org.example.vet1177.repository.MedicalRecordRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -46,7 +48,7 @@ public MedicalRecord create( @Transactional(readOnly = true) public MedicalRecord getById(UUID id) { return medicalRecordRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Ärende hittades inte: " + id)); + .orElseThrow(() -> new ResourceNotFoundException("MedicalRecord", id)); // ← rad 49 } @Transactional(readOnly = true) @@ -73,6 +75,11 @@ public List getByClinicAndStatus(UUID clinicId, RecordStatus stat public MedicalRecord update(UUID id, String title, String description, User updatedBy) { MedicalRecord record = getById(id); + + if (record.getStatus().isFinal()) { // ← mellan rad 75-76 + throw new BusinessRuleException("Stängda ärenden kan inte uppdateras"); + } + record.setTitle(title); record.setDescription(description); record.setUpdatedBy(updatedBy); @@ -104,8 +111,8 @@ public MedicalRecord updateStatus(UUID recordId, RecordStatus newStatus, User up public MedicalRecord close(UUID recordId, User closedBy) { MedicalRecord record = getById(recordId); - if (record.getStatus() == RecordStatus.CLOSED) { - throw new RuntimeException("Ärendet är redan stängt"); + if (record.getStatus().isFinal()) { // ← rad 108 + throw new BusinessRuleException("Ärendet är redan stängt"); } record.setStatus(RecordStatus.CLOSED);