From e0432d884a9748992ad46f14ee216a5218092630 Mon Sep 17 00:00:00 2001 From: RogerCll06 Date: Wed, 22 Oct 2025 23:31:17 -0500 Subject: [PATCH] implement detail class --- .../classes/clients/MemberFeignClient.java | 32 ++++++++++++++++ .../config/FeignClientInterceptor.java | 37 +++++++++++++++++++ .../java/com/classes/config/FeignConfig.java | 36 ++++++++++++++++++ .../classes/dtos/external/MemberInfoDTO.java | 3 +- .../classes/dtos/external/MembershipDTO.java | 29 +++++++++++++++ .../services/Impl/ClassStatsServiceImpl.java | 4 +- 6 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/classes/clients/MemberFeignClient.java create mode 100644 src/main/java/com/classes/config/FeignClientInterceptor.java create mode 100644 src/main/java/com/classes/config/FeignConfig.java create mode 100644 src/main/java/com/classes/dtos/external/MembershipDTO.java diff --git a/src/main/java/com/classes/clients/MemberFeignClient.java b/src/main/java/com/classes/clients/MemberFeignClient.java new file mode 100644 index 0000000..1a29ce8 --- /dev/null +++ b/src/main/java/com/classes/clients/MemberFeignClient.java @@ -0,0 +1,32 @@ +package com.classes.clients; + +import com.classes.config.FeignConfig; +import com.classes.dtos.external.MemberInfoDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.UUID; + +/** + * Cliente Feign para comunicarse con msvc-members + * + * NOTA: Si msvc-members no está corriendo, este cliente fallará con "Connection refused" + * Asegúrate de que msvc-members esté registrado en Eureka o corriendo en el puerto especificado + */ +@FeignClient( + name = "msvc-members", // Usa Eureka para descubrir el servicio + // url = "http://localhost:9098", // Comentado: usa Eureka en lugar de URL fija + configuration = FeignConfig.class +) +public interface MemberFeignClient { + + /** + * Obtiene información completa de un miembro por su ID + * Incluye email desde msvc-security y membership activa + * @param memberId ID del miembro + * @return Información del miembro con email y membership + */ + @GetMapping("/member/{memberId}/info") + MemberInfoDTO getMemberInfo(@PathVariable("memberId") UUID memberId); +} diff --git a/src/main/java/com/classes/config/FeignClientInterceptor.java b/src/main/java/com/classes/config/FeignClientInterceptor.java new file mode 100644 index 0000000..635581a --- /dev/null +++ b/src/main/java/com/classes/config/FeignClientInterceptor.java @@ -0,0 +1,37 @@ +package com.classes.config; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.stereotype.Component; + +/** + * Interceptor para agregar el token JWT a las peticiones de Feign + * Propaga el token de autenticación del usuario actual a los microservicios + */ +@Component +@Slf4j +public class FeignClientInterceptor implements RequestInterceptor { + + @Override + public void apply(RequestTemplate template) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication instanceof JwtAuthenticationToken jwtAuth) { + Jwt jwt = jwtAuth.getToken(); + String token = jwt.getTokenValue(); + + log.debug("🔐 Agregando token JWT a la petición Feign: {}", + template.method() + " " + template.url()); + + template.header("Authorization", "Bearer " + token); + } else { + log.warn("⚠️ No hay token JWT disponible para la petición Feign: {}", + template.method() + " " + template.url()); + } + } +} diff --git a/src/main/java/com/classes/config/FeignConfig.java b/src/main/java/com/classes/config/FeignConfig.java new file mode 100644 index 0000000..84b5997 --- /dev/null +++ b/src/main/java/com/classes/config/FeignConfig.java @@ -0,0 +1,36 @@ +package com.classes.config; + +import feign.Logger; +import feign.RequestInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configuración para OpenFeign + * Incluye logging y propagación de tokens JWT + */ +@Configuration +@RequiredArgsConstructor +public class FeignConfig { + + private final FeignClientInterceptor feignClientInterceptor; + + /** + * Configura el nivel de logging de Feign + * BASIC: Log solo método, URL, código de respuesta y tiempo de ejecución + * FULL: Log completo de requests y responses + */ + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } + + /** + * Registra el interceptor para propagar tokens JWT + */ + @Bean + public RequestInterceptor requestInterceptor() { + return feignClientInterceptor; + } +} diff --git a/src/main/java/com/classes/dtos/external/MemberInfoDTO.java b/src/main/java/com/classes/dtos/external/MemberInfoDTO.java index 80eec96..8ac4585 100644 --- a/src/main/java/com/classes/dtos/external/MemberInfoDTO.java +++ b/src/main/java/com/classes/dtos/external/MemberInfoDTO.java @@ -18,8 +18,7 @@ @NoArgsConstructor @Builder public class MemberInfoDTO { - @JsonProperty("userId") // msvc-members usa "userId" - private UUID id; + private UUID userId; private String firstName; private String lastName; diff --git a/src/main/java/com/classes/dtos/external/MembershipDTO.java b/src/main/java/com/classes/dtos/external/MembershipDTO.java new file mode 100644 index 0000000..cd64814 --- /dev/null +++ b/src/main/java/com/classes/dtos/external/MembershipDTO.java @@ -0,0 +1,29 @@ +package com.classes.dtos.external; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * DTO para recibir información de la membresía desde msvc-members + * Mapea exactamente los campos que devuelve MembershipDto de msvc-members + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MembershipDTO { + @JsonProperty("planName") + private String type; // Mapea planName a type para mantener compatibilidad + + private String status; // "ACTIVE", "EXPIRED", "CANCELLED", "SUSPENDED" + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer daysRemaining; + private boolean isActive; + private boolean isExpired; +} diff --git a/src/main/java/com/classes/services/Impl/ClassStatsServiceImpl.java b/src/main/java/com/classes/services/Impl/ClassStatsServiceImpl.java index 7aa2ab2..d0f2601 100644 --- a/src/main/java/com/classes/services/Impl/ClassStatsServiceImpl.java +++ b/src/main/java/com/classes/services/Impl/ClassStatsServiceImpl.java @@ -105,7 +105,7 @@ public List getUpcomingClasses() { private StudentInClassDTO buildStudentDTO(ClassReservation reservation, List membersInfo) { MemberInfoDTO memberInfo = membersInfo.stream() - .filter(m -> m.getId().equals(reservation.getMemberId())) + .filter(m -> m.getUserId().equals(reservation.getMemberId())) .findFirst() .orElse(createDefaultMemberInfo(reservation.getMemberId())); @@ -181,7 +181,7 @@ private String determineAction(ClassEntity classEntity, int currentStudents) { private MemberInfoDTO createDefaultMemberInfo(UUID memberId) { return MemberInfoDTO.builder() - .id(memberId) + .userId(memberId) .firstName("Usuario") .lastName("Desconocido") .email("no-disponible@email.com")