From c59e8f40e5406fec2681d7fb2410ee14efd8aef2 Mon Sep 17 00:00:00 2001 From: RogerCll06 Date: Tue, 30 Sep 2025 02:36:30 -0500 Subject: [PATCH 1/5] END POINT CLASS --- .../classes/controllers/ClassController.java | 45 ++++++++++++ src/main/java/com/classes/dtos/ClassDTO.java | 23 ++++++ .../java/com/classes/dtos/TrainerDTO.java | 4 +- .../com/classes/entities/ClassEntity.java | 17 +++-- .../java/com/classes/mappers/ClassMapper.java | 24 +++++++ .../classes/repositories/ClassRepository.java | 15 ++++ .../com/classes/services/ClassService.java | 14 ++++ .../services/Impl/ClassServiceImpl.java | 70 +++++++++++++++++++ .../services/Impl/LocationServiceImpl.java | 9 +++ .../services/Impl/TrainerServiceImpl.java | 15 ++++ .../classes/services/TrainerServiceImpl.java | 4 -- 11 files changed, 228 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/classes/controllers/ClassController.java create mode 100644 src/main/java/com/classes/dtos/ClassDTO.java create mode 100644 src/main/java/com/classes/mappers/ClassMapper.java create mode 100644 src/main/java/com/classes/repositories/ClassRepository.java create mode 100644 src/main/java/com/classes/services/ClassService.java create mode 100644 src/main/java/com/classes/services/Impl/ClassServiceImpl.java delete mode 100644 src/main/java/com/classes/services/TrainerServiceImpl.java diff --git a/src/main/java/com/classes/controllers/ClassController.java b/src/main/java/com/classes/controllers/ClassController.java new file mode 100644 index 0000000..cbc1051 --- /dev/null +++ b/src/main/java/com/classes/controllers/ClassController.java @@ -0,0 +1,45 @@ +package com.classes.controllers; + +import com.classes.dtos.ClassDTO; +import com.classes.services.ClassService; +import org.springframework.web.bind.annotation.PostMapping; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/classes") +@RequiredArgsConstructor +public class ClassController { + + private final ClassService classService; + + + @PostMapping + public ResponseEntity createClass(@RequestBody ClassDTO dto) { + ClassDTO created = classService.createClass(dto); + return new ResponseEntity<>(created, HttpStatus.CREATED); + } + + @GetMapping + public ResponseEntity> findAllClasses() { + List list = classService.findAll(); + return ResponseEntity.ok(list); + } + + @PutMapping("/{id}") + public ResponseEntity updateClass(@PathVariable UUID id, @RequestBody ClassDTO dto) { + ClassDTO updated = classService.updateClass(id, dto); + return ResponseEntity.ok(updated); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteClass(@PathVariable UUID id) { + classService.deleteClass(id); + return ResponseEntity.ok("Clase eliminada correctamente"); + } +} diff --git a/src/main/java/com/classes/dtos/ClassDTO.java b/src/main/java/com/classes/dtos/ClassDTO.java new file mode 100644 index 0000000..bfa1099 --- /dev/null +++ b/src/main/java/com/classes/dtos/ClassDTO.java @@ -0,0 +1,23 @@ +package com.classes.dtos; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.UUID; + +@Data +public class ClassDTO { + private UUID id; + private String className; + private UUID locationId; + @JsonFormat(pattern = "dd-MM-yyyy") + private LocalDate classDate; + private int duration; + private UUID trainerId; + private int maxCapacity; + private LocalTime startTime; + private boolean active; + private String description; +} diff --git a/src/main/java/com/classes/dtos/TrainerDTO.java b/src/main/java/com/classes/dtos/TrainerDTO.java index f202d8f..b27a23c 100644 --- a/src/main/java/com/classes/dtos/TrainerDTO.java +++ b/src/main/java/com/classes/dtos/TrainerDTO.java @@ -18,7 +18,7 @@ public class TrainerDTO { private String lastName; private String dni; - @JsonFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "dd-MM-yyyy") private LocalDate birthDate; private Gender gender; @@ -32,7 +32,7 @@ public class TrainerDTO { private List certifications; private Set availability; - @JsonFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "dd-MM-yyyy") private LocalDate hireDate; private TrainerStatus status; diff --git a/src/main/java/com/classes/entities/ClassEntity.java b/src/main/java/com/classes/entities/ClassEntity.java index 4ca9111..a52b982 100644 --- a/src/main/java/com/classes/entities/ClassEntity.java +++ b/src/main/java/com/classes/entities/ClassEntity.java @@ -5,6 +5,8 @@ import jakarta.persistence.*; import lombok.*; +import java.time.LocalDate; +import java.time.LocalTime; import java.util.UUID; @Entity @@ -18,13 +20,16 @@ public class ClassEntity { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; - private String title; - @Column(length = 2000) - private String description; - @Column(name = "trainer_id") + private String name; + private UUID LocationId; + private String className; + private int duration; private UUID trainerId; - @Column(name = "max_capacity") - private Integer maxCapacity; + private int maxCapacity; + private LocalTime startTime; + private boolean active; + private String description; + @Embedded private Audit audit; } diff --git a/src/main/java/com/classes/mappers/ClassMapper.java b/src/main/java/com/classes/mappers/ClassMapper.java new file mode 100644 index 0000000..a477bd5 --- /dev/null +++ b/src/main/java/com/classes/mappers/ClassMapper.java @@ -0,0 +1,24 @@ +package com.classes.mappers; + +import com.classes.config.MapStructConfig; +import com.classes.dtos.ClassDTO; +import com.classes.dtos.LocationDTO; +import com.classes.entities.ClassEntity; +import com.classes.entities.LocationEntity; +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; + +import java.util.List; + +@Mapper(config = MapStructConfig.class) +public interface ClassMapper { + ClassEntity toEntity(ClassDTO dto); + ClassDTO toDTO(ClassEntity entity); + List toDTOList(List entities); + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void updateFromDto(ClassDTO dto, @MappingTarget ClassEntity entity); + + +} diff --git a/src/main/java/com/classes/repositories/ClassRepository.java b/src/main/java/com/classes/repositories/ClassRepository.java new file mode 100644 index 0000000..6e32432 --- /dev/null +++ b/src/main/java/com/classes/repositories/ClassRepository.java @@ -0,0 +1,15 @@ +package com.classes.repositories; + +import com.classes.entities.ClassEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface ClassRepository extends JpaRepository { + Optional findFirstByTrainerId(UUID trainerId); + Optional findFirstByLocationId(UUID locationId); + +} diff --git a/src/main/java/com/classes/services/ClassService.java b/src/main/java/com/classes/services/ClassService.java new file mode 100644 index 0000000..89cd363 --- /dev/null +++ b/src/main/java/com/classes/services/ClassService.java @@ -0,0 +1,14 @@ +package com.classes.services; + +import com.classes.dtos.ClassDTO; + +import java.util.List; +import java.util.UUID; + +public interface ClassService { + + ClassDTO createClass(ClassDTO dto); + List findAll(); + ClassDTO updateClass(UUID id, ClassDTO dto); + void deleteClass(UUID id); +} diff --git a/src/main/java/com/classes/services/Impl/ClassServiceImpl.java b/src/main/java/com/classes/services/Impl/ClassServiceImpl.java new file mode 100644 index 0000000..621a14e --- /dev/null +++ b/src/main/java/com/classes/services/Impl/ClassServiceImpl.java @@ -0,0 +1,70 @@ +package com.classes.services.Impl; + +import com.classes.dtos.ClassDTO; +import com.classes.entities.ClassEntity; +import com.classes.mappers.ClassMapper; +import com.classes.repositories.ClassRepository; +import com.classes.repositories.LocationRepository; +import com.classes.repositories.TrainerRepository; +import com.classes.services.ClassService; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + + +@Service +@RequiredArgsConstructor +public class ClassServiceImpl implements ClassService { + + private final ClassRepository repository; + private final ClassMapper classMapper; + private final TrainerRepository trainerRepository; + private final LocationRepository locationRepository; + + + @Transactional + @Override + public ClassDTO createClass(ClassDTO dto) { + validateTrainerAndLocation(dto); + ClassEntity entity = classMapper.toEntity(dto); + ClassEntity saved = repository.save(entity); + return classMapper.toDTO(saved); + } + + @Transactional(readOnly = true) + @Override + public List findAll() { + List entities = repository.findAll(); + return classMapper.toDTOList(entities); + } + + @Transactional + @Override + public ClassDTO updateClass(UUID id, ClassDTO dto) { + ClassEntity existing = repository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("La clase con ID " + id + " no existe")); + validateTrainerAndLocation(dto); + classMapper.updateFromDto(dto, existing); + ClassEntity updated = repository.save(existing); + return classMapper.toDTO(updated); + } + + @Transactional + @Override + public void deleteClass(UUID id) { + + } + + private void validateTrainerAndLocation(ClassDTO dto) { + if (!trainerRepository.existsById(dto.getTrainerId())) { + throw new IllegalArgumentException("El trainer con ID " + dto.getTrainerId() + " no existe"); + } + if (!locationRepository.existsById(dto.getLocationId())) { + throw new IllegalArgumentException("La ubicación con ID " + dto.getLocationId() + " no existe"); + } + } +} diff --git a/src/main/java/com/classes/services/Impl/LocationServiceImpl.java b/src/main/java/com/classes/services/Impl/LocationServiceImpl.java index 2a25aff..f7d8bde 100644 --- a/src/main/java/com/classes/services/Impl/LocationServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/LocationServiceImpl.java @@ -3,6 +3,7 @@ import com.classes.dtos.LocationDTO; import com.classes.entities.LocationEntity; import com.classes.mappers.LocationMapper; +import com.classes.repositories.ClassRepository; import com.classes.repositories.LocationRepository; import com.classes.services.LocationService; import lombok.AllArgsConstructor; @@ -23,6 +24,7 @@ public class LocationServiceImpl implements LocationService { private final LocationRepository repository; private final LocationMapper mapper; + private final ClassRepository classRepository; @Override @Transactional @@ -51,9 +53,16 @@ public LocationEntity findById(UUID id) { @Transactional @Override public void delete(UUID id) { + boolean hasClasses = classRepository.findFirstByLocationId(id).isPresent(); + if (hasClasses) { + throw new IllegalArgumentException( + "No se puede eliminar porque hay clases asociadas con este ID" + ); + } repository.deleteById(id); } + @Transactional(readOnly = true) public Page findAll(int page, int size) { Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending()); diff --git a/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java b/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java index a8175ef..fddeff6 100644 --- a/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java @@ -4,11 +4,13 @@ import com.classes.dtos.TrainerDTO; import com.classes.entities.TrainerEntity; import com.classes.mappers.TrainerMapper; +import com.classes.repositories.ClassRepository; import com.classes.repositories.TrainerRepository; import com.classes.services.AzureService; import com.classes.services.CloudinaryService; import com.classes.services.TrainerService; import jakarta.persistence.EntityNotFoundException; +import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -26,7 +28,9 @@ public class TrainerServiceImpl implements TrainerService { private final TrainerMapper trainerMapper; private final CloudinaryService cloudinaryService; private final AzureService azureStorageService; + private final ClassRepository classRepository; + @Transactional @Override public TrainerDTO createTrainer(TrainerDTO trainerDTO, MultipartFile profileImage, @@ -49,6 +53,7 @@ public TrainerDTO createTrainer(TrainerDTO trainerDTO, return trainerMapper.toDTO(savedTrainer); } + @Transactional(readOnly = true) @Override public TrainerDTO getTrainerById(UUID id) { TrainerEntity trainer = trainerRepository.findById(id) @@ -56,11 +61,13 @@ public TrainerDTO getTrainerById(UUID id) { return trainerMapper.toDTO(trainer); } + @Transactional(readOnly = true) @Override public List getAllTrainers() { return trainerMapper.toDTOList(trainerRepository.findAll()); } + @Transactional @Override public TrainerDTO updateTrainer(UUID id, TrainerDTO trainerDTO, @@ -86,10 +93,18 @@ public TrainerDTO updateTrainer(UUID id, TrainerEntity updated = trainerRepository.save(trainer); return trainerMapper.toDTO(updated); } + @Transactional @Override public void deleteTrainer(UUID id) throws IOException { TrainerEntity trainer = trainerRepository.findById(id) .orElseThrow(() -> new EntityNotFoundException("Trainer not found with id: " + id)); + + boolean hasClasses = classRepository.findFirstByTrainerId(id).isPresent(); + if (hasClasses) { + throw new IllegalArgumentException( + "No se puede eliminar el trainer porque tiene clases asignadas" + ); + } if (trainer.getProfileImageUrl() != null) { cloudinaryService.delete(trainer.getProfileImageUrl()); } diff --git a/src/main/java/com/classes/services/TrainerServiceImpl.java b/src/main/java/com/classes/services/TrainerServiceImpl.java deleted file mode 100644 index eb2a4a5..0000000 --- a/src/main/java/com/classes/services/TrainerServiceImpl.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.classes.services; - -public class TrainerServiceImpl { -} From 1db19f146dd026770708a88ab67d1dbe4ae6576d Mon Sep 17 00:00:00 2001 From: Raydberg Gabriel Date: Thu, 2 Oct 2025 16:38:59 -0500 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20agregar=20enumeraciones=20de=20tipo?= =?UTF-8?q?=20de=20contrato,=20disponibilidad=20diaria,=20g=C3=A9nero=20y?= =?UTF-8?q?=20estado=20del=20entrenador?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 - .../com/classes/config/SecurityConfig.java | 3 +- .../classes/controllers/HelloController.java | 9 +++-- .../controllers/TrainerController.java | 18 ++++++++-- .../java/com/classes/dtos/TrainerDTO.java | 8 ++--- .../com/classes/entities/ClassEntity.java | 5 ++- .../com/classes/entities/TrainerEntity.java | 8 ++--- .../{Enums => enums}/ContractType.java | 2 +- .../{Enums => enums}/DayAvailability.java | 2 +- .../com/classes/{Enums => enums}/Gender.java | 2 +- .../{Enums => enums}/TrainerStatus.java | 2 +- .../exceptions/GlobalExceptionController.java | 33 ++++++++++++------- .../services/Impl/AuthServiceImpl.java | 31 +++++++++++++++++ 13 files changed, 93 insertions(+), 31 deletions(-) rename src/main/java/com/classes/{Enums => enums}/ContractType.java (78%) rename src/main/java/com/classes/{Enums => enums}/DayAvailability.java (81%) rename src/main/java/com/classes/{Enums => enums}/Gender.java (78%) rename src/main/java/com/classes/{Enums => enums}/TrainerStatus.java (89%) create mode 100644 src/main/java/com/classes/services/Impl/AuthServiceImpl.java diff --git a/pom.xml b/pom.xml index 3b28882..c669f1c 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,6 @@ - diff --git a/src/main/java/com/classes/config/SecurityConfig.java b/src/main/java/com/classes/config/SecurityConfig.java index 38694cc..26e0134 100644 --- a/src/main/java/com/classes/config/SecurityConfig.java +++ b/src/main/java/com/classes/config/SecurityConfig.java @@ -30,7 +30,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/swagger-ui/**", "/v3/api-docs/**", "/saludo", - "/" + "/", + "/test/**" ).permitAll() .anyRequest().authenticated() ) diff --git a/src/main/java/com/classes/controllers/HelloController.java b/src/main/java/com/classes/controllers/HelloController.java index 05b7594..365769b 100644 --- a/src/main/java/com/classes/controllers/HelloController.java +++ b/src/main/java/com/classes/controllers/HelloController.java @@ -1,12 +1,15 @@ package com.classes.controllers; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/saludo") +@RequestMapping("/test") public class HelloController { - public String saludo() { - return "Microservicio de clases activo"; + @GetMapping("saludo") + public ResponseEntity saludo() { + return ResponseEntity.ok("Microservicio de clases activo"); } } diff --git a/src/main/java/com/classes/controllers/TrainerController.java b/src/main/java/com/classes/controllers/TrainerController.java index 40ed7c7..04dd2da 100644 --- a/src/main/java/com/classes/controllers/TrainerController.java +++ b/src/main/java/com/classes/controllers/TrainerController.java @@ -1,5 +1,7 @@ package com.classes.controllers; +import com.classes.annotations.AdminAccess; +import com.classes.annotations.AdminOrTrainerAccess; import com.classes.dtos.TrainerDTO; import com.classes.services.TrainerService; import com.fasterxml.jackson.databind.ObjectMapper; @@ -10,6 +12,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -26,6 +33,7 @@ public class TrainerController { private final TrainerService trainerService; + @AdminAccess @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity createTrainer( @RequestParam("trainer") String trainerJson, @@ -40,18 +48,21 @@ public ResponseEntity createTrainer( return ResponseEntity.status(HttpStatus.CREATED).body(createdTrainer); } + @PreAuthorize("@authService.canAccessResource(#id, authentication)") @GetMapping("/{id}") public ResponseEntity getTrainerById(@PathVariable UUID id) { TrainerDTO trainer = trainerService.getTrainerById(id); return ResponseEntity.ok(trainer); } + @AdminAccess @GetMapping public ResponseEntity> getAllTrainers() { List trainers = trainerService.getAllTrainers(); return ResponseEntity.ok(trainers); } + @PreAuthorize("@authService.canAccessResource(#ownerId, authentication)") @PutMapping(value = "/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity updateTrainer( @PathVariable UUID id, @@ -67,14 +78,17 @@ public ResponseEntity updateTrainer( return ResponseEntity.ok(updatedTrainer); } + @DeleteMapping("/{id}") public ResponseEntity deleteTrainer(@PathVariable UUID id) { try { trainerService.deleteTrainer(id); return ResponseEntity.noContent().build(); - } catch (EntityNotFoundException e) { + } catch ( + EntityNotFoundException e) { return ResponseEntity.notFound().build(); - } catch (IOException e) { + } catch ( + IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } diff --git a/src/main/java/com/classes/dtos/TrainerDTO.java b/src/main/java/com/classes/dtos/TrainerDTO.java index b27a23c..2a7743e 100644 --- a/src/main/java/com/classes/dtos/TrainerDTO.java +++ b/src/main/java/com/classes/dtos/TrainerDTO.java @@ -1,9 +1,9 @@ package com.classes.dtos; -import com.classes.Enums.ContractType; -import com.classes.Enums.DayAvailability; -import com.classes.Enums.Gender; -import com.classes.Enums.TrainerStatus; +import com.classes.enums.ContractType; +import com.classes.enums.DayAvailability; +import com.classes.enums.Gender; +import com.classes.enums.TrainerStatus; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; diff --git a/src/main/java/com/classes/entities/ClassEntity.java b/src/main/java/com/classes/entities/ClassEntity.java index a52b982..aca092b 100644 --- a/src/main/java/com/classes/entities/ClassEntity.java +++ b/src/main/java/com/classes/entities/ClassEntity.java @@ -21,7 +21,7 @@ public class ClassEntity { @GeneratedValue(strategy = GenerationType.UUID) private UUID id; private String name; - private UUID LocationId; +// private UUID LocationId; private String className; private int duration; private UUID trainerId; @@ -29,6 +29,9 @@ public class ClassEntity { private LocalTime startTime; private boolean active; private String description; + @ManyToOne + @JoinColumn(name = "location_id") + private LocationEntity location; @Embedded private Audit audit; diff --git a/src/main/java/com/classes/entities/TrainerEntity.java b/src/main/java/com/classes/entities/TrainerEntity.java index 61f9400..c3f6222 100644 --- a/src/main/java/com/classes/entities/TrainerEntity.java +++ b/src/main/java/com/classes/entities/TrainerEntity.java @@ -1,9 +1,9 @@ package com.classes.entities; -import com.classes.Enums.ContractType; -import com.classes.Enums.DayAvailability; -import com.classes.Enums.Gender; -import com.classes.Enums.TrainerStatus; +import com.classes.enums.ContractType; +import com.classes.enums.DayAvailability; +import com.classes.enums.Gender; +import com.classes.enums.TrainerStatus; import com.classes.config.Audit; import com.classes.config.AuditListener; import jakarta.persistence.*; diff --git a/src/main/java/com/classes/Enums/ContractType.java b/src/main/java/com/classes/enums/ContractType.java similarity index 78% rename from src/main/java/com/classes/Enums/ContractType.java rename to src/main/java/com/classes/enums/ContractType.java index c5d5b42..1b507b6 100644 --- a/src/main/java/com/classes/Enums/ContractType.java +++ b/src/main/java/com/classes/enums/ContractType.java @@ -1,4 +1,4 @@ -package com.classes.Enums; +package com.classes.enums; public enum ContractType { TIEMPO_COMPLETO, diff --git a/src/main/java/com/classes/Enums/DayAvailability.java b/src/main/java/com/classes/enums/DayAvailability.java similarity index 81% rename from src/main/java/com/classes/Enums/DayAvailability.java rename to src/main/java/com/classes/enums/DayAvailability.java index e394d67..ac2d5df 100644 --- a/src/main/java/com/classes/Enums/DayAvailability.java +++ b/src/main/java/com/classes/enums/DayAvailability.java @@ -1,4 +1,4 @@ -package com.classes.Enums; +package com.classes.enums; public enum DayAvailability { diff --git a/src/main/java/com/classes/Enums/Gender.java b/src/main/java/com/classes/enums/Gender.java similarity index 78% rename from src/main/java/com/classes/Enums/Gender.java rename to src/main/java/com/classes/enums/Gender.java index 543fa99..ba01901 100644 --- a/src/main/java/com/classes/Enums/Gender.java +++ b/src/main/java/com/classes/enums/Gender.java @@ -1,4 +1,4 @@ -package com.classes.Enums; +package com.classes.enums; public enum Gender { MASCULINO, // Masculino diff --git a/src/main/java/com/classes/Enums/TrainerStatus.java b/src/main/java/com/classes/enums/TrainerStatus.java similarity index 89% rename from src/main/java/com/classes/Enums/TrainerStatus.java rename to src/main/java/com/classes/enums/TrainerStatus.java index 68fb885..ff5af03 100644 --- a/src/main/java/com/classes/Enums/TrainerStatus.java +++ b/src/main/java/com/classes/enums/TrainerStatus.java @@ -1,4 +1,4 @@ -package com.classes.Enums; +package com.classes.enums; public enum TrainerStatus { ACTIVO, // Entrenador activo diff --git a/src/main/java/com/classes/exceptions/GlobalExceptionController.java b/src/main/java/com/classes/exceptions/GlobalExceptionController.java index 001c963..889de7f 100644 --- a/src/main/java/com/classes/exceptions/GlobalExceptionController.java +++ b/src/main/java/com/classes/exceptions/GlobalExceptionController.java @@ -1,5 +1,6 @@ package com.classes.exceptions; +import jakarta.persistence.EntityNotFoundException; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -7,6 +8,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; //import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.AccessDeniedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -108,19 +110,28 @@ public ResponseEntity handleNoHandlerFoundException(NoHandlerFoun return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); } -// @ExceptionHandler(AccessDeniedException.class) -// public ResponseEntity handleAccessDeniedException(AccessDeniedException ex) { -// ErrorResponse errorResponse = new ErrorResponse( -// "ACCESS_DENIED", -// "Acceso denegado", -// Collections.singletonList("No tienes los permisos necesarios para realizar esta acción") -// ); -// -// log.warn("Access denied for user"); -// return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse); -// } + @ExceptionHandler(AccessDeniedException.class) + public ResponseEntity handleAccessDeniedException(AccessDeniedException ex) { + ErrorResponse errorResponse = new ErrorResponse( + "ACCESS_DENIED", + "Acceso denegado", + Collections.singletonList("No tienes los permisos necesarios para realizar esta acción") + ); + log.warn("Access denied for user"); + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse); + } + @ExceptionHandler(EntityNotFoundException.class) + public ResponseEntity handleEntityNotFoundException(EntityNotFoundException ex) { + ErrorResponse errorResponse = new ErrorResponse( + "ENTITY_NOT_FOUND", + "No se encontró la entidad solicitada", + List.of(ex.getMessage()) + ); + log.warn("Entity not found: {}", ex.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); + } } \ No newline at end of file diff --git a/src/main/java/com/classes/services/Impl/AuthServiceImpl.java b/src/main/java/com/classes/services/Impl/AuthServiceImpl.java new file mode 100644 index 0000000..ebbecb7 --- /dev/null +++ b/src/main/java/com/classes/services/Impl/AuthServiceImpl.java @@ -0,0 +1,31 @@ +package com.classes.services.Impl; + +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public class AuthServiceImpl { + + public boolean hasRole(Authentication authentication, String role) { + return authentication.getAuthorities().stream() + .anyMatch(a -> a.getAuthority().equals(role)); + } + + public UUID getUserId(Authentication authentication) { + String userIdStr = ((JwtAuthenticationToken) authentication).getToken().getClaim("user_id"); + return UUID.fromString(userIdStr); + } + + // Verifica si el usuario es admin o dueño del recurso + public boolean canAccessResource(UUID ownerId, Authentication authentication) { + return hasRole(authentication, "ROLE_ADMIN") || ownerId.equals(getUserId(authentication)); + } + + // Verifica si el usuario tiene un rol específico o es dueño del recurso + public boolean canAccessWithRoleOrOwner(UUID ownerId, Authentication authentication, String role) { + return hasRole(authentication, role) || ownerId.equals(getUserId(authentication)); + } +} From e0db3e28b41b18802189f351db93d19d7369e2c9 Mon Sep 17 00:00:00 2001 From: RogerCll06 Date: Thu, 9 Oct 2025 12:41:17 -0500 Subject: [PATCH 3/5] END POINT CLASS --- pom.xml | 9 +-- .../classes/controllers/ClassController.java | 19 ++++--- .../controllers/TrainerController.java | 34 ++++++++++- .../ClassRequest.java} | 13 +++-- .../com/classes/dtos/Class/ClassResponse.java | 28 ++++++++++ .../com/classes/entities/ClassEntity.java | 9 ++- .../com/classes/entities/TrainerEntity.java | 5 ++ .../java/com/classes/mappers/ClassMapper.java | 26 +++++---- .../repositories/TrainerRepository.java | 5 ++ .../com/classes/services/ClassService.java | 13 +++-- .../services/Impl/ClassServiceImpl.java | 56 ++++++++++++------- .../services/Impl/TrainerServiceImpl.java | 13 +++++ .../com/classes/services/TrainerService.java | 4 ++ 13 files changed, 173 insertions(+), 61 deletions(-) rename src/main/java/com/classes/dtos/{ClassDTO.java => Class/ClassRequest.java} (73%) create mode 100644 src/main/java/com/classes/dtos/Class/ClassResponse.java diff --git a/pom.xml b/pom.xml index c669f1c..bc1d869 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,8 @@ com.members msvc-members 0.0.1-SNAPSHOT - msvc-members - msvc-members + msvc-classes + msvc-classes @@ -122,11 +122,6 @@ 1.5.5.Final provided - - org.springdoc - springdoc-openapi-starter-webmvc-ui - 2.8.11 - com.cloudinary cloudinary-http44 diff --git a/src/main/java/com/classes/controllers/ClassController.java b/src/main/java/com/classes/controllers/ClassController.java index cbc1051..82061ae 100644 --- a/src/main/java/com/classes/controllers/ClassController.java +++ b/src/main/java/com/classes/controllers/ClassController.java @@ -1,6 +1,7 @@ package com.classes.controllers; -import com.classes.dtos.ClassDTO; +import com.classes.dtos.Class.ClassRequest; +import com.classes.dtos.Class.ClassResponse; import com.classes.services.ClassService; import org.springframework.web.bind.annotation.PostMapping; import lombok.RequiredArgsConstructor; @@ -19,21 +20,23 @@ public class ClassController { private final ClassService classService; + @PostMapping - public ResponseEntity createClass(@RequestBody ClassDTO dto) { - ClassDTO created = classService.createClass(dto); - return new ResponseEntity<>(created, HttpStatus.CREATED); + public ResponseEntity createClass(@RequestBody ClassRequest request) { + ClassResponse created = classService.createClass(request); + return ResponseEntity.status(HttpStatus.CREATED).body(created); } @GetMapping - public ResponseEntity> findAllClasses() { - List list = classService.findAll(); + public ResponseEntity> findAllClasses() { + List list = classService.findAll(); return ResponseEntity.ok(list); } + @PutMapping("/{id}") - public ResponseEntity updateClass(@PathVariable UUID id, @RequestBody ClassDTO dto) { - ClassDTO updated = classService.updateClass(id, dto); + public ResponseEntity updateClass(@PathVariable UUID id, @RequestBody ClassRequest request) { + ClassResponse updated = classService.updateClass(id, request); return ResponseEntity.ok(updated); } diff --git a/src/main/java/com/classes/controllers/TrainerController.java b/src/main/java/com/classes/controllers/TrainerController.java index 04dd2da..20bf4d2 100644 --- a/src/main/java/com/classes/controllers/TrainerController.java +++ b/src/main/java/com/classes/controllers/TrainerController.java @@ -3,20 +3,19 @@ import com.classes.annotations.AdminAccess; import com.classes.annotations.AdminOrTrainerAccess; import com.classes.dtos.TrainerDTO; +import com.classes.services.Impl.AuthServiceImpl; import com.classes.services.TrainerService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -29,9 +28,12 @@ @RestController @RequestMapping("/api/trainers") @RequiredArgsConstructor +@Slf4j public class TrainerController { private final TrainerService trainerService; + private final AuthServiceImpl authService; + @AdminAccess @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @@ -92,5 +94,31 @@ public ResponseEntity deleteTrainer(@PathVariable UUID id) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } + + @GetMapping("/my-trainer") + @AdminOrTrainerAccess + //@PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public ResponseEntity getMyTrainer(Authentication authentication) { + UUID userId = authService.getUserId(authentication); + TrainerDTO trainer = trainerService.getTrainerByUserId(userId); + return trainer != null ? ResponseEntity.ok(trainer) : ResponseEntity.noContent().build(); + } + + @GetMapping("/has-trainer") + //@PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + @AdminOrTrainerAccess + public ResponseEntity hasTrainer(Authentication authentication) { + UUID userId = authService.getUserId(authentication); + return ResponseEntity.ok(trainerService.existsByUserId(userId)); + } + + @GetMapping("/user/{userId}") + //@PreAuthorize("hasRole('ADMIN')") + @AdminAccess + public ResponseEntity getUserTrainer(@PathVariable UUID userId) { + TrainerDTO trainer = trainerService.getTrainerByUserId(userId); + return trainer != null ? ResponseEntity.ok(trainer) : ResponseEntity.noContent().build(); + } } + diff --git a/src/main/java/com/classes/dtos/ClassDTO.java b/src/main/java/com/classes/dtos/Class/ClassRequest.java similarity index 73% rename from src/main/java/com/classes/dtos/ClassDTO.java rename to src/main/java/com/classes/dtos/Class/ClassRequest.java index bfa1099..e972438 100644 --- a/src/main/java/com/classes/dtos/ClassDTO.java +++ b/src/main/java/com/classes/dtos/Class/ClassRequest.java @@ -1,23 +1,26 @@ -package com.classes.dtos; +package com.classes.dtos.Class; import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.time.LocalDate; import java.time.LocalTime; import java.util.UUID; @Data -public class ClassDTO { - private UUID id; +@AllArgsConstructor +@NoArgsConstructor +public class ClassRequest { private String className; private UUID locationId; + private UUID trainerId; @JsonFormat(pattern = "dd-MM-yyyy") private LocalDate classDate; private int duration; - private UUID trainerId; private int maxCapacity; private LocalTime startTime; private boolean active; private String description; -} +} \ No newline at end of file diff --git a/src/main/java/com/classes/dtos/Class/ClassResponse.java b/src/main/java/com/classes/dtos/Class/ClassResponse.java new file mode 100644 index 0000000..7a9ecfc --- /dev/null +++ b/src/main/java/com/classes/dtos/Class/ClassResponse.java @@ -0,0 +1,28 @@ +package com.classes.dtos.Class; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ClassResponse { + private UUID id; + private String className; + private String locationName; + private String trainerName; + @JsonFormat(pattern = "dd-MM-yyyy") + private LocalDate classDate; + private int duration; + private int maxCapacity; + private LocalTime startTime; + private boolean active; + private String description; +} \ No newline at end of file diff --git a/src/main/java/com/classes/entities/ClassEntity.java b/src/main/java/com/classes/entities/ClassEntity.java index aca092b..46d5298 100644 --- a/src/main/java/com/classes/entities/ClassEntity.java +++ b/src/main/java/com/classes/entities/ClassEntity.java @@ -21,18 +21,23 @@ public class ClassEntity { @GeneratedValue(strategy = GenerationType.UUID) private UUID id; private String name; -// private UUID LocationId; + private String className; private int duration; - private UUID trainerId; + private int maxCapacity; private LocalTime startTime; private boolean active; private String description; + @ManyToOne @JoinColumn(name = "location_id") private LocationEntity location; + @ManyToOne + @JoinColumn(name = "trainer_id") + private TrainerEntity trainer; + @Embedded private Audit audit; } diff --git a/src/main/java/com/classes/entities/TrainerEntity.java b/src/main/java/com/classes/entities/TrainerEntity.java index c3f6222..eae3af6 100644 --- a/src/main/java/com/classes/entities/TrainerEntity.java +++ b/src/main/java/com/classes/entities/TrainerEntity.java @@ -65,6 +65,11 @@ public class TrainerEntity { private BigDecimal salaryPerClass; private String bankInfo; private String notes; + + @Column(name = "user_id") + private UUID userId; + + @Embedded private Audit audit; diff --git a/src/main/java/com/classes/mappers/ClassMapper.java b/src/main/java/com/classes/mappers/ClassMapper.java index a477bd5..49bcdf0 100644 --- a/src/main/java/com/classes/mappers/ClassMapper.java +++ b/src/main/java/com/classes/mappers/ClassMapper.java @@ -1,24 +1,26 @@ package com.classes.mappers; import com.classes.config.MapStructConfig; -import com.classes.dtos.ClassDTO; -import com.classes.dtos.LocationDTO; +import com.classes.dtos.Class.ClassRequest; + +import com.classes.dtos.Class.ClassResponse; import com.classes.entities.ClassEntity; -import com.classes.entities.LocationEntity; -import org.mapstruct.BeanMapping; -import org.mapstruct.Mapper; -import org.mapstruct.MappingTarget; -import org.mapstruct.NullValuePropertyMappingStrategy; + +import org.mapstruct.*; import java.util.List; @Mapper(config = MapStructConfig.class) public interface ClassMapper { - ClassEntity toEntity(ClassDTO dto); - ClassDTO toDTO(ClassEntity entity); - List toDTOList(List entities); - @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) - void updateFromDto(ClassDTO dto, @MappingTarget ClassEntity entity); + // Crear entidad desde el request + ClassEntity toEntity(ClassRequest request); + ClassResponse toResponse(ClassEntity entity); + // Lista de respuestas + List toResponseList(List entities); + + // Actualizar entidad desde el request (solo los campos no nulos) + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void updateFromRequest(ClassRequest request, @MappingTarget ClassEntity entity); } diff --git a/src/main/java/com/classes/repositories/TrainerRepository.java b/src/main/java/com/classes/repositories/TrainerRepository.java index 5a0cdc2..7dc1e03 100644 --- a/src/main/java/com/classes/repositories/TrainerRepository.java +++ b/src/main/java/com/classes/repositories/TrainerRepository.java @@ -4,9 +4,14 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.Optional; import java.util.UUID; @Repository public interface TrainerRepository extends JpaRepository { + Optional findByUserId(UUID userId); + + // Verificar si existe trainer por userId + boolean existsByUserId(UUID userId); } diff --git a/src/main/java/com/classes/services/ClassService.java b/src/main/java/com/classes/services/ClassService.java index 89cd363..4d6f370 100644 --- a/src/main/java/com/classes/services/ClassService.java +++ b/src/main/java/com/classes/services/ClassService.java @@ -1,14 +1,19 @@ package com.classes.services; -import com.classes.dtos.ClassDTO; +import com.classes.dtos.Class.ClassRequest; +import com.classes.dtos.Class.ClassResponse; import java.util.List; import java.util.UUID; public interface ClassService { - ClassDTO createClass(ClassDTO dto); - List findAll(); - ClassDTO updateClass(UUID id, ClassDTO dto); + + ClassResponse createClass(ClassRequest request); + + List findAll(); + + ClassResponse updateClass(UUID id, ClassRequest request); + void deleteClass(UUID id); } diff --git a/src/main/java/com/classes/services/Impl/ClassServiceImpl.java b/src/main/java/com/classes/services/Impl/ClassServiceImpl.java index 621a14e..966aa27 100644 --- a/src/main/java/com/classes/services/Impl/ClassServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/ClassServiceImpl.java @@ -1,13 +1,15 @@ package com.classes.services.Impl; -import com.classes.dtos.ClassDTO; +import com.classes.dtos.Class.ClassRequest; +import com.classes.dtos.Class.ClassResponse; import com.classes.entities.ClassEntity; +import com.classes.entities.LocationEntity; +import com.classes.entities.TrainerEntity; import com.classes.mappers.ClassMapper; import com.classes.repositories.ClassRepository; import com.classes.repositories.LocationRepository; import com.classes.repositories.TrainerRepository; import com.classes.services.ClassService; -import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -28,43 +30,57 @@ public class ClassServiceImpl implements ClassService { @Transactional @Override - public ClassDTO createClass(ClassDTO dto) { - validateTrainerAndLocation(dto); - ClassEntity entity = classMapper.toEntity(dto); + public ClassResponse createClass(ClassRequest request) { + validateTrainerAndLocation(request); + ClassEntity entity = classMapper.toEntity(request); + TrainerEntity trainer = trainerRepository.findById(request.getTrainerId()) + .orElseThrow(() -> new IllegalArgumentException("El trainer con ID " + request.getTrainerId() + " no existe")); + LocationEntity location = locationRepository.findById(request.getLocationId()) + .orElseThrow(() -> new IllegalArgumentException("La ubicación con ID " + request.getLocationId() + " no existe")); + entity.setTrainer(trainer); + entity.setLocation(location); ClassEntity saved = repository.save(entity); - return classMapper.toDTO(saved); + return classMapper.toResponse(saved); } - @Transactional(readOnly = true) + @Transactional @Override - public List findAll() { - List entities = repository.findAll(); - return classMapper.toDTOList(entities); + public List findAll() { + return classMapper.toResponseList(repository.findAll()); } @Transactional @Override - public ClassDTO updateClass(UUID id, ClassDTO dto) { + public ClassResponse updateClass(UUID id, ClassRequest request) { ClassEntity existing = repository.findById(id) .orElseThrow(() -> new IllegalArgumentException("La clase con ID " + id + " no existe")); - validateTrainerAndLocation(dto); - classMapper.updateFromDto(dto, existing); + validateTrainerAndLocation(request); + classMapper.updateFromRequest(request, existing); + TrainerEntity trainer = trainerRepository.findById(request.getTrainerId()) + .orElseThrow(() -> new IllegalArgumentException("El trainer con ID " + request.getTrainerId() + " no existe")); + LocationEntity location = locationRepository.findById(request.getLocationId()) + .orElseThrow(() -> new IllegalArgumentException("La ubicación con ID " + request.getLocationId() + " no existe")); + existing.setTrainer(trainer); + existing.setLocation(location); ClassEntity updated = repository.save(existing); - return classMapper.toDTO(updated); + return classMapper.toResponse(updated); } @Transactional @Override public void deleteClass(UUID id) { - + if (!repository.existsById(id)) { + throw new IllegalArgumentException("La clase con ID " + id + " no existe"); + } + repository.deleteById(id); } - private void validateTrainerAndLocation(ClassDTO dto) { - if (!trainerRepository.existsById(dto.getTrainerId())) { - throw new IllegalArgumentException("El trainer con ID " + dto.getTrainerId() + " no existe"); + private void validateTrainerAndLocation(ClassRequest request) { + if (!trainerRepository.existsById(request.getTrainerId())) { + throw new IllegalArgumentException("El trainer con ID " + request.getTrainerId() + " no existe"); } - if (!locationRepository.existsById(dto.getLocationId())) { - throw new IllegalArgumentException("La ubicación con ID " + dto.getLocationId() + " no existe"); + if (!locationRepository.existsById(request.getLocationId())) { + throw new IllegalArgumentException("La ubicación con ID " + request.getLocationId() + " no existe"); } } } diff --git a/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java b/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java index fddeff6..fed1ef7 100644 --- a/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java @@ -116,4 +116,17 @@ public void deleteTrainer(UUID id) throws IOException { } trainerRepository.delete(trainer); } + + @Override + public TrainerDTO getTrainerByUserId(UUID userId) { + return trainerRepository.findByUserId(userId) + .map(trainerMapper::toDTO) + .orElse(null); // null si no tiene trainer + } + + @Override + public boolean existsByUserId(UUID userId) { + return trainerRepository.existsByUserId(userId); + } + } \ No newline at end of file diff --git a/src/main/java/com/classes/services/TrainerService.java b/src/main/java/com/classes/services/TrainerService.java index 1b93963..8ac38cb 100644 --- a/src/main/java/com/classes/services/TrainerService.java +++ b/src/main/java/com/classes/services/TrainerService.java @@ -22,5 +22,9 @@ TrainerDTO updateTrainer(UUID id, List certifications) throws IOException; void deleteTrainer(UUID id) throws IOException; + + TrainerDTO getTrainerByUserId(UUID userId); + + boolean existsByUserId(UUID userId); } From 5b58c8f588f6a117c4e9c4c592bf74bfcceaaafa Mon Sep 17 00:00:00 2001 From: RogerCll06 Date: Sat, 11 Oct 2025 01:11:11 -0500 Subject: [PATCH 4/5] finalize endpoints --- .../classes/controllers/ClassController.java | 5 ++ .../controllers/LocationController.java | 40 ++++++++---- .../controllers/TrainerController.java | 48 ++++---------- .../LocationRequest.java} | 5 +- .../dtos/Location/LocationResponse.java | 14 +++++ .../dtos/{ => Trainer}/FileResponseDTO.java | 2 +- .../dtos/{ => Trainer}/TrainerDTO.java | 2 +- .../com/classes/entities/TrainerEntity.java | 4 -- .../java/com/classes/mappers/ClassMapper.java | 7 +++ .../com/classes/mappers/LocationMapper.java | 12 ++-- .../com/classes/mappers/TrainerMapper.java | 2 +- .../repositories/LocationRepository.java | 16 ++++- .../repositories/TrainerRepository.java | 5 +- .../services/AuthorizationService.java | 15 +++++ .../com/classes/services/AzureService.java | 2 +- .../classes/services/CloudinaryService.java | 2 +- ...mpl.java => AuthorizationServiceImpl.java} | 10 ++- .../services/Impl/AzureServiceImpl.java | 2 +- .../services/Impl/ClassServiceImpl.java | 22 ++++--- .../services/Impl/CloudinaryServiceImpl.java | 2 +- .../services/Impl/LocationServiceImpl.java | 63 +++++++++++-------- .../services/Impl/TrainerServiceImpl.java | 17 +---- .../com/classes/services/LocationService.java | 20 +++--- .../com/classes/services/TrainerService.java | 6 +- src/main/resources/bootstrap.yml | 5 +- 25 files changed, 194 insertions(+), 134 deletions(-) rename src/main/java/com/classes/dtos/{LocationDTO.java => Location/LocationRequest.java} (67%) create mode 100644 src/main/java/com/classes/dtos/Location/LocationResponse.java rename src/main/java/com/classes/dtos/{ => Trainer}/FileResponseDTO.java (90%) rename src/main/java/com/classes/dtos/{ => Trainer}/TrainerDTO.java (96%) create mode 100644 src/main/java/com/classes/services/AuthorizationService.java rename src/main/java/com/classes/services/Impl/{AuthServiceImpl.java => AuthorizationServiceImpl.java} (84%) diff --git a/src/main/java/com/classes/controllers/ClassController.java b/src/main/java/com/classes/controllers/ClassController.java index 82061ae..c7188d6 100644 --- a/src/main/java/com/classes/controllers/ClassController.java +++ b/src/main/java/com/classes/controllers/ClassController.java @@ -3,6 +3,7 @@ import com.classes.dtos.Class.ClassRequest; import com.classes.dtos.Class.ClassResponse; import com.classes.services.ClassService; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PostMapping; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -22,12 +23,14 @@ public class ClassController { @PostMapping + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id, authentication)") public ResponseEntity createClass(@RequestBody ClassRequest request) { ClassResponse created = classService.createClass(request); return ResponseEntity.status(HttpStatus.CREATED).body(created); } @GetMapping + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id, authentication)") public ResponseEntity> findAllClasses() { List list = classService.findAll(); return ResponseEntity.ok(list); @@ -35,12 +38,14 @@ public ResponseEntity> findAllClasses() { @PutMapping("/{id}") + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id, authentication)") public ResponseEntity updateClass(@PathVariable UUID id, @RequestBody ClassRequest request) { ClassResponse updated = classService.updateClass(id, request); return ResponseEntity.ok(updated); } @DeleteMapping("/{id}") + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id, authentication)") public ResponseEntity deleteClass(@PathVariable UUID id) { classService.deleteClass(id); return ResponseEntity.ok("Clase eliminada correctamente"); diff --git a/src/main/java/com/classes/controllers/LocationController.java b/src/main/java/com/classes/controllers/LocationController.java index 7d4a738..8efd485 100644 --- a/src/main/java/com/classes/controllers/LocationController.java +++ b/src/main/java/com/classes/controllers/LocationController.java @@ -1,16 +1,16 @@ package com.classes.controllers; import com.classes.annotations.AdminOrTrainerAccess; -import com.classes.dtos.LocationDTO; -import com.classes.entities.LocationEntity; +import com.classes.dtos.Location.LocationRequest; +import com.classes.dtos.Location.LocationResponse; import com.classes.services.LocationService; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import java.util.List; import java.util.UUID; @RestController @@ -21,29 +21,45 @@ public class LocationController { private final LocationService locationService; + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @PostMapping - public ResponseEntity create(@RequestBody LocationDTO dto) { - LocationDTO created = locationService.create(dto); + public ResponseEntity create(@RequestBody LocationRequest request) { + LocationResponse created = locationService.create(request); return new ResponseEntity<>(created, HttpStatus.CREATED); } + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @GetMapping - public ResponseEntity> findAll( + public ResponseEntity> findAll( @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "10") int size + @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String search, + @RequestParam(required = false) Boolean active ) { - Page locations = locationService.findAll(page, size); + Page locations = locationService.findAll(page, size, search, active); return ResponseEntity.ok(locations); } - + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @GetMapping("/{id}") - public ResponseEntity findById(@PathVariable UUID id) { - return ResponseEntity.ok(locationService.findById(id)); + public ResponseEntity findById(@PathVariable UUID id) { + LocationResponse location = locationService.findById(id); + return ResponseEntity.ok(location); + } + + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") + @PutMapping("/{id}") + public ResponseEntity update( + @PathVariable UUID id, + @RequestBody LocationRequest request + ) { + LocationResponse updated = locationService.update(id, request); + return ResponseEntity.ok(updated); } + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @DeleteMapping("/{id}") public ResponseEntity delete(@PathVariable UUID id) { - locationService.delete(id); + locationService.delete(id); // Validación interna return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/com/classes/controllers/TrainerController.java b/src/main/java/com/classes/controllers/TrainerController.java index 20bf4d2..07db9e2 100644 --- a/src/main/java/com/classes/controllers/TrainerController.java +++ b/src/main/java/com/classes/controllers/TrainerController.java @@ -1,9 +1,7 @@ package com.classes.controllers; -import com.classes.annotations.AdminAccess; -import com.classes.annotations.AdminOrTrainerAccess; -import com.classes.dtos.TrainerDTO; -import com.classes.services.Impl.AuthServiceImpl; +import com.classes.dtos.Trainer.TrainerDTO; +import com.classes.services.AuthorizationService; import com.classes.services.TrainerService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -32,12 +30,13 @@ public class TrainerController { private final TrainerService trainerService; - private final AuthServiceImpl authService; + private final AuthorizationService authService; - - @AdminAccess +/*probado*/ + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity createTrainer( + Authentication authentication, @RequestParam("trainer") String trainerJson, @RequestParam(value = "profileImage", required = false) MultipartFile profileImage, @RequestParam(value = "certifications", required = false) List certifications @@ -49,22 +48,23 @@ public ResponseEntity createTrainer( TrainerDTO createdTrainer = trainerService.createTrainer(trainerDTO, profileImage, certifications); return ResponseEntity.status(HttpStatus.CREATED).body(createdTrainer); } - - @PreAuthorize("@authService.canAccessResource(#id, authentication)") + /*probado*/ + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @GetMapping("/{id}") public ResponseEntity getTrainerById(@PathVariable UUID id) { TrainerDTO trainer = trainerService.getTrainerById(id); return ResponseEntity.ok(trainer); } - @AdminAccess + //probado + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @GetMapping public ResponseEntity> getAllTrainers() { List trainers = trainerService.getAllTrainers(); return ResponseEntity.ok(trainers); } - @PreAuthorize("@authService.canAccessResource(#ownerId, authentication)") + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id, authentication)") @PutMapping(value = "/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity updateTrainer( @PathVariable UUID id, @@ -81,6 +81,7 @@ public ResponseEntity updateTrainer( } + @PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)") @DeleteMapping("/{id}") public ResponseEntity deleteTrainer(@PathVariable UUID id) { try { @@ -94,31 +95,6 @@ public ResponseEntity deleteTrainer(@PathVariable UUID id) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } - - @GetMapping("/my-trainer") - @AdminOrTrainerAccess - //@PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - public ResponseEntity getMyTrainer(Authentication authentication) { - UUID userId = authService.getUserId(authentication); - TrainerDTO trainer = trainerService.getTrainerByUserId(userId); - return trainer != null ? ResponseEntity.ok(trainer) : ResponseEntity.noContent().build(); - } - - @GetMapping("/has-trainer") - //@PreAuthorize("hasRole('USER') or hasRole('ADMIN')") - @AdminOrTrainerAccess - public ResponseEntity hasTrainer(Authentication authentication) { - UUID userId = authService.getUserId(authentication); - return ResponseEntity.ok(trainerService.existsByUserId(userId)); - } - - @GetMapping("/user/{userId}") - //@PreAuthorize("hasRole('ADMIN')") - @AdminAccess - public ResponseEntity getUserTrainer(@PathVariable UUID userId) { - TrainerDTO trainer = trainerService.getTrainerByUserId(userId); - return trainer != null ? ResponseEntity.ok(trainer) : ResponseEntity.noContent().build(); - } } diff --git a/src/main/java/com/classes/dtos/LocationDTO.java b/src/main/java/com/classes/dtos/Location/LocationRequest.java similarity index 67% rename from src/main/java/com/classes/dtos/LocationDTO.java rename to src/main/java/com/classes/dtos/Location/LocationRequest.java index f3a1d08..f065e52 100644 --- a/src/main/java/com/classes/dtos/LocationDTO.java +++ b/src/main/java/com/classes/dtos/Location/LocationRequest.java @@ -1,10 +1,9 @@ -package com.classes.dtos; - +package com.classes.dtos.Location; import lombok.Data; @Data -public class LocationDTO { +public class LocationRequest { private String name; private String description; private int ability; diff --git a/src/main/java/com/classes/dtos/Location/LocationResponse.java b/src/main/java/com/classes/dtos/Location/LocationResponse.java new file mode 100644 index 0000000..6ad0dba --- /dev/null +++ b/src/main/java/com/classes/dtos/Location/LocationResponse.java @@ -0,0 +1,14 @@ +package com.classes.dtos.Location; + +import lombok.Data; + +import java.util.UUID; + +@Data +public class LocationResponse { + private UUID id; // si tienes un ID generado + private String name; + private String description; + private int ability; + private boolean active; +} \ No newline at end of file diff --git a/src/main/java/com/classes/dtos/FileResponseDTO.java b/src/main/java/com/classes/dtos/Trainer/FileResponseDTO.java similarity index 90% rename from src/main/java/com/classes/dtos/FileResponseDTO.java rename to src/main/java/com/classes/dtos/Trainer/FileResponseDTO.java index c4c2e4b..ed64c33 100644 --- a/src/main/java/com/classes/dtos/FileResponseDTO.java +++ b/src/main/java/com/classes/dtos/Trainer/FileResponseDTO.java @@ -1,4 +1,4 @@ -package com.classes.dtos; +package com.classes.dtos.Trainer; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/src/main/java/com/classes/dtos/TrainerDTO.java b/src/main/java/com/classes/dtos/Trainer/TrainerDTO.java similarity index 96% rename from src/main/java/com/classes/dtos/TrainerDTO.java rename to src/main/java/com/classes/dtos/Trainer/TrainerDTO.java index 2a7743e..ec47f27 100644 --- a/src/main/java/com/classes/dtos/TrainerDTO.java +++ b/src/main/java/com/classes/dtos/Trainer/TrainerDTO.java @@ -1,4 +1,4 @@ -package com.classes.dtos; +package com.classes.dtos.Trainer; import com.classes.enums.ContractType; import com.classes.enums.DayAvailability; diff --git a/src/main/java/com/classes/entities/TrainerEntity.java b/src/main/java/com/classes/entities/TrainerEntity.java index eae3af6..acbdb9b 100644 --- a/src/main/java/com/classes/entities/TrainerEntity.java +++ b/src/main/java/com/classes/entities/TrainerEntity.java @@ -66,10 +66,6 @@ public class TrainerEntity { private String bankInfo; private String notes; - @Column(name = "user_id") - private UUID userId; - - @Embedded private Audit audit; diff --git a/src/main/java/com/classes/mappers/ClassMapper.java b/src/main/java/com/classes/mappers/ClassMapper.java index 49bcdf0..3507ebf 100644 --- a/src/main/java/com/classes/mappers/ClassMapper.java +++ b/src/main/java/com/classes/mappers/ClassMapper.java @@ -14,7 +14,14 @@ public interface ClassMapper { // Crear entidad desde el request + + @Mapping(target = "id", ignore = true) + @Mapping(target = "location", ignore = true) // se setean en el servicio + @Mapping(target = "trainer", ignore = true) ClassEntity toEntity(ClassRequest request); + + @Mapping(target = "locationName", source = "location.name") + @Mapping(target = "trainerName", source = "trainer.firstName") ClassResponse toResponse(ClassEntity entity); // Lista de respuestas diff --git a/src/main/java/com/classes/mappers/LocationMapper.java b/src/main/java/com/classes/mappers/LocationMapper.java index 4a6da5c..37cf259 100644 --- a/src/main/java/com/classes/mappers/LocationMapper.java +++ b/src/main/java/com/classes/mappers/LocationMapper.java @@ -1,7 +1,8 @@ package com.classes.mappers; -import com.classes.dtos.LocationDTO; +import com.classes.dtos.Location.LocationRequest; +import com.classes.dtos.Location.LocationResponse; import com.classes.config.MapStructConfig; import com.classes.entities.LocationEntity; import org.mapstruct.BeanMapping; @@ -12,11 +13,14 @@ @Mapper(config = MapStructConfig.class) public interface LocationMapper { - LocationDTO toDto(LocationEntity locationEntity); - LocationEntity toEntity(LocationDTO locationDTO); + LocationResponse toResponse(LocationEntity locationEntity); + // Request → Entity + LocationEntity toEntity(LocationRequest locationRequest); + + // Actualización parcial: Request → Entity @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) - void updateFromDto(LocationDTO dto, @MappingTarget LocationEntity entity); + void updateFromRequest(LocationRequest request, @MappingTarget LocationEntity entity); } diff --git a/src/main/java/com/classes/mappers/TrainerMapper.java b/src/main/java/com/classes/mappers/TrainerMapper.java index 1bf7d53..666f32f 100644 --- a/src/main/java/com/classes/mappers/TrainerMapper.java +++ b/src/main/java/com/classes/mappers/TrainerMapper.java @@ -1,7 +1,7 @@ package com.classes.mappers; import com.classes.config.MapStructConfig; -import com.classes.dtos.TrainerDTO; +import com.classes.dtos.Trainer.TrainerDTO; import com.classes.entities.TrainerEntity; import org.mapstruct.BeanMapping; import org.mapstruct.Mapper; diff --git a/src/main/java/com/classes/repositories/LocationRepository.java b/src/main/java/com/classes/repositories/LocationRepository.java index aa10536..7bb3b09 100644 --- a/src/main/java/com/classes/repositories/LocationRepository.java +++ b/src/main/java/com/classes/repositories/LocationRepository.java @@ -3,11 +3,23 @@ import com.classes.entities.LocationEntity; -import java.util.UUID; - +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.UUID; + + @Repository public interface LocationRepository extends JpaRepository { + Page findByNameContainingIgnoreCase(String name, Pageable pageable); + + // 2️⃣ Solo activos + Page findByActiveTrue(Pageable pageable); + + // 3️⃣ Solo inactivos + Page findByActiveFalse(Pageable pageable); + Page findByNameContainingIgnoreCaseAndActive(String name, boolean active, Pageable pageable); + } diff --git a/src/main/java/com/classes/repositories/TrainerRepository.java b/src/main/java/com/classes/repositories/TrainerRepository.java index 7dc1e03..9af8498 100644 --- a/src/main/java/com/classes/repositories/TrainerRepository.java +++ b/src/main/java/com/classes/repositories/TrainerRepository.java @@ -10,8 +10,7 @@ @Repository public interface TrainerRepository extends JpaRepository { - Optional findByUserId(UUID userId); - // Verificar si existe trainer por userId - boolean existsByUserId(UUID userId); + + } diff --git a/src/main/java/com/classes/services/AuthorizationService.java b/src/main/java/com/classes/services/AuthorizationService.java new file mode 100644 index 0000000..d62f206 --- /dev/null +++ b/src/main/java/com/classes/services/AuthorizationService.java @@ -0,0 +1,15 @@ +package com.classes.services; + +import org.springframework.security.core.Authentication; + +import java.util.UUID; + +public interface AuthorizationService { + boolean hasRole(Authentication authentication, String role); + + UUID getUserId(Authentication authentication); + + boolean canAccessResource(UUID ownerId, Authentication authentication); + + boolean canAccessWithRoleOrOwner(UUID ownerId, Authentication authentication, String role); +} diff --git a/src/main/java/com/classes/services/AzureService.java b/src/main/java/com/classes/services/AzureService.java index 3a3c6f3..0362893 100644 --- a/src/main/java/com/classes/services/AzureService.java +++ b/src/main/java/com/classes/services/AzureService.java @@ -1,6 +1,6 @@ package com.classes.services; -import com.classes.dtos.FileResponseDTO; +import com.classes.dtos.Trainer.FileResponseDTO; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; diff --git a/src/main/java/com/classes/services/CloudinaryService.java b/src/main/java/com/classes/services/CloudinaryService.java index 8f12c0e..5bc238a 100644 --- a/src/main/java/com/classes/services/CloudinaryService.java +++ b/src/main/java/com/classes/services/CloudinaryService.java @@ -1,6 +1,6 @@ package com.classes.services; -import com.classes.dtos.FileResponseDTO; +import com.classes.dtos.Trainer.FileResponseDTO; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; diff --git a/src/main/java/com/classes/services/Impl/AuthServiceImpl.java b/src/main/java/com/classes/services/Impl/AuthorizationServiceImpl.java similarity index 84% rename from src/main/java/com/classes/services/Impl/AuthServiceImpl.java rename to src/main/java/com/classes/services/Impl/AuthorizationServiceImpl.java index ebbecb7..00356fb 100644 --- a/src/main/java/com/classes/services/Impl/AuthServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/AuthorizationServiceImpl.java @@ -1,5 +1,7 @@ package com.classes.services.Impl; +import com.classes.services.AuthorizationService; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.stereotype.Service; @@ -7,23 +9,27 @@ import java.util.UUID; @Service -public class AuthServiceImpl { - +@Slf4j +public class AuthorizationServiceImpl implements AuthorizationService { + @Override public boolean hasRole(Authentication authentication, String role) { return authentication.getAuthorities().stream() .anyMatch(a -> a.getAuthority().equals(role)); } + @Override public UUID getUserId(Authentication authentication) { String userIdStr = ((JwtAuthenticationToken) authentication).getToken().getClaim("user_id"); return UUID.fromString(userIdStr); } + @Override // Verifica si el usuario es admin o dueño del recurso public boolean canAccessResource(UUID ownerId, Authentication authentication) { return hasRole(authentication, "ROLE_ADMIN") || ownerId.equals(getUserId(authentication)); } + @Override // Verifica si el usuario tiene un rol específico o es dueño del recurso public boolean canAccessWithRoleOrOwner(UUID ownerId, Authentication authentication, String role) { return hasRole(authentication, role) || ownerId.equals(getUserId(authentication)); diff --git a/src/main/java/com/classes/services/Impl/AzureServiceImpl.java b/src/main/java/com/classes/services/Impl/AzureServiceImpl.java index 9e3a8d6..7cf4514 100644 --- a/src/main/java/com/classes/services/Impl/AzureServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/AzureServiceImpl.java @@ -5,7 +5,7 @@ import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.blob.BlobServiceClientBuilder; import com.azure.storage.common.StorageSharedKeyCredential; -import com.classes.dtos.FileResponseDTO; +import com.classes.dtos.Trainer.FileResponseDTO; import com.classes.services.AzureService; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/classes/services/Impl/ClassServiceImpl.java b/src/main/java/com/classes/services/Impl/ClassServiceImpl.java index 966aa27..5274d7a 100644 --- a/src/main/java/com/classes/services/Impl/ClassServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/ClassServiceImpl.java @@ -52,16 +52,24 @@ public List findAll() { @Transactional @Override public ClassResponse updateClass(UUID id, ClassRequest request) { + ClassEntity existing = repository.findById(id) .orElseThrow(() -> new IllegalArgumentException("La clase con ID " + id + " no existe")); - validateTrainerAndLocation(request); + + + if (request.getTrainerId() != null) { + TrainerEntity trainer = trainerRepository.findById(request.getTrainerId()) + .orElseThrow(() -> new IllegalArgumentException("El trainer con ID " + request.getTrainerId() + " no existe")); + existing.setTrainer(trainer); + } + + + if (request.getLocationId() != null) { + LocationEntity location = locationRepository.findById(request.getLocationId()) + .orElseThrow(() -> new IllegalArgumentException("La ubicación con ID " + request.getLocationId() + " no existe")); + existing.setLocation(location); + } classMapper.updateFromRequest(request, existing); - TrainerEntity trainer = trainerRepository.findById(request.getTrainerId()) - .orElseThrow(() -> new IllegalArgumentException("El trainer con ID " + request.getTrainerId() + " no existe")); - LocationEntity location = locationRepository.findById(request.getLocationId()) - .orElseThrow(() -> new IllegalArgumentException("La ubicación con ID " + request.getLocationId() + " no existe")); - existing.setTrainer(trainer); - existing.setLocation(location); ClassEntity updated = repository.save(existing); return classMapper.toResponse(updated); } diff --git a/src/main/java/com/classes/services/Impl/CloudinaryServiceImpl.java b/src/main/java/com/classes/services/Impl/CloudinaryServiceImpl.java index 550857c..c1f2c05 100644 --- a/src/main/java/com/classes/services/Impl/CloudinaryServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/CloudinaryServiceImpl.java @@ -1,6 +1,6 @@ package com.classes.services.Impl; -import com.classes.dtos.FileResponseDTO; +import com.classes.dtos.Trainer.FileResponseDTO; import com.classes.services.CloudinaryService; import com.cloudinary.Cloudinary; import com.cloudinary.utils.ObjectUtils; diff --git a/src/main/java/com/classes/services/Impl/LocationServiceImpl.java b/src/main/java/com/classes/services/Impl/LocationServiceImpl.java index f7d8bde..d7f8034 100644 --- a/src/main/java/com/classes/services/Impl/LocationServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/LocationServiceImpl.java @@ -1,21 +1,18 @@ package com.classes.services.Impl; -import com.classes.dtos.LocationDTO; +import com.classes.dtos.Location.LocationRequest; +import com.classes.dtos.Location.LocationResponse; import com.classes.entities.LocationEntity; import com.classes.mappers.LocationMapper; import com.classes.repositories.ClassRepository; import com.classes.repositories.LocationRepository; import com.classes.services.LocationService; -import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; import java.util.UUID; @Service @@ -28,30 +25,24 @@ public class LocationServiceImpl implements LocationService { @Override @Transactional - public LocationDTO create(LocationDTO dto) { - LocationEntity locationEntity = mapper.toEntity(dto); - return mapper.toDto(repository.save(locationEntity)); + public LocationResponse create(LocationRequest request) { + LocationEntity entity = mapper.toEntity(request); + LocationEntity saved = repository.save(entity); + return mapper.toResponse(saved); } @Override @Transactional - public LocationDTO update(UUID id, LocationDTO dto) { - LocationEntity locationEntity = findById(id); - mapper.updateFromDto(dto, locationEntity); - LocationEntity updated = repository.save(locationEntity); - return mapper.toDto(updated); - } - - - @Override - @Transactional(readOnly = true) - public LocationEntity findById(UUID id) { - return repository.findById(id) + public LocationResponse update(UUID id, LocationRequest request) { + LocationEntity entity = repository.findById(id) .orElseThrow(() -> new RuntimeException("Location not found")); + mapper.updateFromRequest(request, entity); + LocationEntity updated = repository.save(entity); + return mapper.toResponse(updated); } - @Transactional @Override + @Transactional public void delete(UUID id) { boolean hasClasses = classRepository.findFirstByLocationId(id).isPresent(); if (hasClasses) { @@ -62,12 +53,30 @@ public void delete(UUID id) { repository.deleteById(id); } - - @Transactional(readOnly = true) - public Page findAll(int page, int size) { - Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending()); - Page pageEntity = repository.findAll(pageable); - return pageEntity.map(mapper::toDto); + @Override + public LocationResponse findById(UUID id) { + LocationEntity entity = repository.findById(id) + .orElseThrow(() -> new RuntimeException("Location not found")); + return mapper.toResponse(entity); } + @Override + public Page findAll(int page, int size, String search, Boolean active) { + PageRequest pageable = PageRequest.of(page, size); + Page pageResult; + + if (search != null && !search.isEmpty() && active != null) { + pageResult = repository.findByNameContainingIgnoreCaseAndActive(search, active, pageable); + } else if (search != null && !search.isEmpty()) { + pageResult = repository.findByNameContainingIgnoreCase(search, pageable); + } else if (active != null) { + pageResult = active ? repository.findByActiveTrue(pageable) + : repository.findByActiveFalse(pageable); + } else { + pageResult = repository.findAll(pageable); + } + + return pageResult.map(mapper::toResponse); + } } + diff --git a/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java b/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java index fed1ef7..973edba 100644 --- a/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/TrainerServiceImpl.java @@ -1,7 +1,7 @@ package com.classes.services.Impl; -import com.classes.dtos.FileResponseDTO; -import com.classes.dtos.TrainerDTO; +import com.classes.dtos.Trainer.FileResponseDTO; +import com.classes.dtos.Trainer.TrainerDTO; import com.classes.entities.TrainerEntity; import com.classes.mappers.TrainerMapper; import com.classes.repositories.ClassRepository; @@ -116,17 +116,4 @@ public void deleteTrainer(UUID id) throws IOException { } trainerRepository.delete(trainer); } - - @Override - public TrainerDTO getTrainerByUserId(UUID userId) { - return trainerRepository.findByUserId(userId) - .map(trainerMapper::toDTO) - .orElse(null); // null si no tiene trainer - } - - @Override - public boolean existsByUserId(UUID userId) { - return trainerRepository.existsByUserId(userId); - } - } \ No newline at end of file diff --git a/src/main/java/com/classes/services/LocationService.java b/src/main/java/com/classes/services/LocationService.java index 09921b9..5dad83b 100644 --- a/src/main/java/com/classes/services/LocationService.java +++ b/src/main/java/com/classes/services/LocationService.java @@ -1,17 +1,21 @@ package com.classes.services; -import com.classes.dtos.LocationDTO; -import com.classes.entities.LocationEntity; +import com.classes.dtos.Location.LocationRequest; +import com.classes.dtos.Location.LocationResponse; import org.springframework.data.domain.Page; -import java.util.List; import java.util.UUID; public interface LocationService { - LocationDTO create(LocationDTO dto); - LocationDTO update(UUID id, LocationDTO dto); + LocationResponse create(LocationRequest request); + + LocationResponse update(UUID id, LocationRequest request); + void delete(UUID id); - LocationEntity findById(UUID id); - Page findAll(int page, int size); -} + + LocationResponse findById(UUID id); + + // Paginación + filtros + Page findAll(int page, int size, String search, Boolean active); +} \ No newline at end of file diff --git a/src/main/java/com/classes/services/TrainerService.java b/src/main/java/com/classes/services/TrainerService.java index 8ac38cb..f2b6a44 100644 --- a/src/main/java/com/classes/services/TrainerService.java +++ b/src/main/java/com/classes/services/TrainerService.java @@ -1,6 +1,6 @@ package com.classes.services; -import com.classes.dtos.TrainerDTO; +import com.classes.dtos.Trainer.TrainerDTO; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; @@ -23,8 +23,8 @@ TrainerDTO updateTrainer(UUID id, void deleteTrainer(UUID id) throws IOException; - TrainerDTO getTrainerByUserId(UUID userId); - boolean existsByUserId(UUID userId); + + } diff --git a/src/main/resources/bootstrap.yml b/src/main/resources/bootstrap.yml index 26449e8..3381ebb 100644 --- a/src/main/resources/bootstrap.yml +++ b/src/main/resources/bootstrap.yml @@ -6,4 +6,7 @@ spring: cloud: config: uri: ${SPRING_CLOUD_CONFIG_URI:http://config-server:7777} - \ No newline at end of file + servlet: + multipart: + max-file-size: 10MB + max-request-size: 20MB \ No newline at end of file From 5d7f1150ea7df99b478de46b2c49fd2f1fac794c Mon Sep 17 00:00:00 2001 From: RogerCll06 Date: Tue, 14 Oct 2025 11:22:12 -0500 Subject: [PATCH 5/5] changes in entity class --- src/main/java/com/classes/dtos/Class/ClassRequest.java | 1 + src/main/java/com/classes/dtos/Class/ClassResponse.java | 2 +- src/main/java/com/classes/entities/ClassEntity.java | 1 + src/main/java/com/classes/mappers/ClassMapper.java | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/classes/dtos/Class/ClassRequest.java b/src/main/java/com/classes/dtos/Class/ClassRequest.java index e972438..b7653c6 100644 --- a/src/main/java/com/classes/dtos/Class/ClassRequest.java +++ b/src/main/java/com/classes/dtos/Class/ClassRequest.java @@ -21,6 +21,7 @@ public class ClassRequest { private int duration; private int maxCapacity; private LocalTime startTime; + private LocalTime endTime; private boolean active; private String description; } \ No newline at end of file diff --git a/src/main/java/com/classes/dtos/Class/ClassResponse.java b/src/main/java/com/classes/dtos/Class/ClassResponse.java index 7a9ecfc..baf76a4 100644 --- a/src/main/java/com/classes/dtos/Class/ClassResponse.java +++ b/src/main/java/com/classes/dtos/Class/ClassResponse.java @@ -22,7 +22,7 @@ public class ClassResponse { private LocalDate classDate; private int duration; private int maxCapacity; - private LocalTime startTime; + private String schedule; private boolean active; private String description; } \ No newline at end of file diff --git a/src/main/java/com/classes/entities/ClassEntity.java b/src/main/java/com/classes/entities/ClassEntity.java index 46d5298..962b09e 100644 --- a/src/main/java/com/classes/entities/ClassEntity.java +++ b/src/main/java/com/classes/entities/ClassEntity.java @@ -27,6 +27,7 @@ public class ClassEntity { private int maxCapacity; private LocalTime startTime; + private LocalTime endTime; private boolean active; private String description; diff --git a/src/main/java/com/classes/mappers/ClassMapper.java b/src/main/java/com/classes/mappers/ClassMapper.java index 3507ebf..46985e2 100644 --- a/src/main/java/com/classes/mappers/ClassMapper.java +++ b/src/main/java/com/classes/mappers/ClassMapper.java @@ -22,6 +22,7 @@ public interface ClassMapper { @Mapping(target = "locationName", source = "location.name") @Mapping(target = "trainerName", source = "trainer.firstName") + @Mapping(target = "schedule", expression = "java(entity.getStartTime() + \" - \" + entity.getEndTime())") ClassResponse toResponse(ClassEntity entity); // Lista de respuestas