From 9efacfe677d60c4aff4e3724ecc582f9d310b52b Mon Sep 17 00:00:00 2001 From: berkai Date: Tue, 23 Jun 2026 22:26:16 +0300 Subject: [PATCH 1/2] event join enhanced enhanced --- .../EventAlreadyEndedException.java | 7 +++ .../exceptions/GlobalExceptionHandler.java | 17 ++++- .../configs/QuartzConfig.java | 39 ++++++++++-- .../repository/EventRepository.java | 9 +++ .../scheduler/EventStatusUpdateJob.java | 63 +++++++++++++++++++ .../serviceImpl/ParticipationServiceImpl.java | 8 +++ 6 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 ems-common/src/main/java/com/example/ems_common/exceptions/EventAlreadyEndedException.java create mode 100644 event-service/event-service-app/src/main/java/com/example/event_service_app/scheduler/EventStatusUpdateJob.java diff --git a/ems-common/src/main/java/com/example/ems_common/exceptions/EventAlreadyEndedException.java b/ems-common/src/main/java/com/example/ems_common/exceptions/EventAlreadyEndedException.java new file mode 100644 index 0000000..e213221 --- /dev/null +++ b/ems-common/src/main/java/com/example/ems_common/exceptions/EventAlreadyEndedException.java @@ -0,0 +1,7 @@ +package com.example.ems_common.exceptions; + +public class EventAlreadyEndedException extends RuntimeException { + public EventAlreadyEndedException(String message) { + super(message); + } +} diff --git a/ems-common/src/main/java/com/example/ems_common/exceptions/GlobalExceptionHandler.java b/ems-common/src/main/java/com/example/ems_common/exceptions/GlobalExceptionHandler.java index 388c4bc..c78f452 100644 --- a/ems-common/src/main/java/com/example/ems_common/exceptions/GlobalExceptionHandler.java +++ b/ems-common/src/main/java/com/example/ems_common/exceptions/GlobalExceptionHandler.java @@ -89,7 +89,7 @@ public ResponseEntity handleForbiddenException(RuntimeExceptio @ExceptionHandler({CannotJoinOwnEventException.class}) public ResponseEntity handleCannotJoinOwnEventException(CannotJoinOwnEventException ex, - HttpServletRequest request) { + HttpServletRequest request) { ErrorResponseDto error = new ErrorResponseDto( "CANNOT_JOIN_OWN_EVENT", ex.getMessage(), @@ -102,6 +102,21 @@ public ResponseEntity handleCannotJoinOwnEventException(Cannot .body(error); } + @ExceptionHandler({EventAlreadyEndedException.class}) + public ResponseEntity handleEventAlreadyEndedException(EventAlreadyEndedException ex, + HttpServletRequest request) { + ErrorResponseDto error = new ErrorResponseDto( + "EVENT_ALREADY_ENDED", + ex.getMessage(), + HttpStatus.BAD_REQUEST.value(), + LocalDateTime.now(), + request.getRequestURI() + ); + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(error); + } + @ExceptionHandler({TooManyRequestsException.class}) public ResponseEntity handleTooManyRequestsException(TooManyRequestsException ex, HttpServletRequest request) { diff --git a/event-service/event-service-app/src/main/java/com/example/event_service_app/configs/QuartzConfig.java b/event-service/event-service-app/src/main/java/com/example/event_service_app/configs/QuartzConfig.java index 5b95270..7847426 100644 --- a/event-service/event-service-app/src/main/java/com/example/event_service_app/configs/QuartzConfig.java +++ b/event-service/event-service-app/src/main/java/com/example/event_service_app/configs/QuartzConfig.java @@ -1,6 +1,7 @@ package com.example.event_service_app.configs; import com.example.event_service_app.scheduler.EventReminderJob; +import com.example.event_service_app.scheduler.EventStatusUpdateJob; import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -8,14 +9,18 @@ @Configuration public class QuartzConfig { - private static final String JOB_NAME = "eventReminderJob"; - private static final String JOB_GROUP = "reminderGroup"; - private static final String TRIGGER_NAME = "eventReminderTrigger"; + private static final String REMINDER_JOB_NAME = "eventReminderJob"; + private static final String REMINDER_JOB_GROUP = "reminderGroup"; + private static final String REMINDER_TRIGGER_NAME = "eventReminderTrigger"; + + private static final String STATUS_JOB_NAME = "eventStatusUpdateJob"; + private static final String STATUS_JOB_GROUP = "statusGroup"; + private static final String STATUS_TRIGGER_NAME = "eventStatusUpdateTrigger"; @Bean public JobDetail eventReminderJobDetail() { return JobBuilder.newJob(EventReminderJob.class) - .withIdentity(JOB_NAME, JOB_GROUP) + .withIdentity(REMINDER_JOB_NAME, REMINDER_JOB_GROUP) .withDescription("24 saat içinde başlayacak etkinlikler için Kafka'ya hatırlatıcı event gönderir") .storeDurably() // trigger olmasa da silinmesin .requestRecovery() // node crash sonrası yeniden çalıştır @@ -27,7 +32,7 @@ public Trigger eventReminderTrigger(JobDetail eventReminderJobDetail) { // Her saat başı çalışır return TriggerBuilder.newTrigger() .forJob(eventReminderJobDetail) - .withIdentity(TRIGGER_NAME, JOB_GROUP) + .withIdentity(REMINDER_TRIGGER_NAME, REMINDER_JOB_GROUP) .withDescription("Saatlik reminder trigger") .withSchedule( CronScheduleBuilder.cronSchedule("0 0 * * * ?") // her saat başı @@ -35,5 +40,29 @@ public Trigger eventReminderTrigger(JobDetail eventReminderJobDetail) { ) .build(); } + + @Bean + public JobDetail eventStatusUpdateJobDetail() { + return JobBuilder.newJob(EventStatusUpdateJob.class) + .withIdentity(STATUS_JOB_NAME, STATUS_JOB_GROUP) + .withDescription("Event status'lerini otomatik olarak günceller (UPCOMING -> ONGOING -> COMPLETED)") + .storeDurably() + .requestRecovery() + .build(); + } + + @Bean + public Trigger eventStatusUpdateTrigger(JobDetail eventStatusUpdateJobDetail) { + // Her 5 dakikada bir çalışır + return TriggerBuilder.newTrigger() + .forJob(eventStatusUpdateJobDetail) + .withIdentity(STATUS_TRIGGER_NAME, STATUS_JOB_GROUP) + .withDescription("5 dakikalık event status update trigger") + .withSchedule( + CronScheduleBuilder.cronSchedule("0 0/5 * * * ?") // her 5 dakika + .withMisfireHandlingInstructionFireAndProceed() + ) + .build(); + } } diff --git a/event-service/event-service-app/src/main/java/com/example/event_service_app/repository/EventRepository.java b/event-service/event-service-app/src/main/java/com/example/event_service_app/repository/EventRepository.java index e12ad2c..6c1f855 100644 --- a/event-service/event-service-app/src/main/java/com/example/event_service_app/repository/EventRepository.java +++ b/event-service/event-service-app/src/main/java/com/example/event_service_app/repository/EventRepository.java @@ -5,8 +5,11 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.time.LocalDateTime; +import java.util.List; public interface EventRepository extends JpaRepository { Page findByCategoryId(Long categoryId, Pageable pageable); @@ -14,5 +17,11 @@ public interface EventRepository extends JpaRepository { Page findByStartDateBetween(LocalDateTime start, LocalDateTime end, Pageable pageable); Page findByStatus(EventStatus status, Pageable pageable); Page findByOwnerId(Long ownerId, Pageable pageable); + + @Query("SELECT e FROM Event e WHERE e.status = :status AND e.endDate < :now") + List findByStatusAndEndDateBefore(@Param("status") EventStatus status, @Param("now") LocalDateTime now); + + @Query("SELECT e FROM Event e WHERE e.status = :status AND e.startDate <= :now AND e.endDate >= :now") + List findByStatusAndStartDateBeforeAndEndDateAfter(@Param("status") EventStatus status, @Param("now") LocalDateTime now); } diff --git a/event-service/event-service-app/src/main/java/com/example/event_service_app/scheduler/EventStatusUpdateJob.java b/event-service/event-service-app/src/main/java/com/example/event_service_app/scheduler/EventStatusUpdateJob.java new file mode 100644 index 0000000..144d658 --- /dev/null +++ b/event-service/event-service-app/src/main/java/com/example/event_service_app/scheduler/EventStatusUpdateJob.java @@ -0,0 +1,63 @@ +package com.example.event_service_app.scheduler; + +import com.example.event_service_app.entity.Event; +import com.example.event_service_app.repository.EventRepository; +import com.example.event_service_client.enums.EventStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import org.springframework.scheduling.quartz.QuartzJobBean; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +@DisallowConcurrentExecution +public class EventStatusUpdateJob extends QuartzJobBean { + + private final EventRepository eventRepository; + + @Override + @Transactional + protected void executeInternal(JobExecutionContext context) { + LocalDateTime now = LocalDateTime.now(); + + // UPCOMING -> ONGOING (startDate <= now < endDate) + List upcomingToOngoing = eventRepository.findByStatusAndStartDateBeforeAndEndDateAfter( + EventStatus.UPCOMING, now); + + for (Event event : upcomingToOngoing) { + event.setStatus(EventStatus.ONGOING); + eventRepository.save(event); + log.info("[EventStatusUpdateJob] Event {} status updated from UPCOMING to ONGOING", event.getId()); + } + + // ONGOING -> COMPLETED (endDate < now) + List ongoingToCompleted = eventRepository.findByStatusAndEndDateBefore( + EventStatus.ONGOING, now); + + for (Event event : ongoingToCompleted) { + event.setStatus(EventStatus.COMPLETED); + eventRepository.save(event); + log.info("[EventStatusUpdateJob] Event {} status updated from ONGOING to COMPLETED", event.getId()); + } + + // UPCOMING -> COMPLETED (endDate < now, missed ONGOING window) + List upcomingToCompleted = eventRepository.findByStatusAndEndDateBefore( + EventStatus.UPCOMING, now); + + for (Event event : upcomingToCompleted) { + event.setStatus(EventStatus.COMPLETED); + eventRepository.save(event); + log.info("[EventStatusUpdateJob] Event {} status updated from UPCOMING to COMPLETED", event.getId()); + } + + int total = upcomingToOngoing.size() + ongoingToCompleted.size() + upcomingToCompleted.size(); + log.info("[EventStatusUpdateJob] Total {} events updated", total); + } +} diff --git a/event-service/event-service-app/src/main/java/com/example/event_service_app/serviceImpl/ParticipationServiceImpl.java b/event-service/event-service-app/src/main/java/com/example/event_service_app/serviceImpl/ParticipationServiceImpl.java index 12d1b83..b566e39 100644 --- a/event-service/event-service-app/src/main/java/com/example/event_service_app/serviceImpl/ParticipationServiceImpl.java +++ b/event-service/event-service-app/src/main/java/com/example/event_service_app/serviceImpl/ParticipationServiceImpl.java @@ -4,7 +4,9 @@ import com.example.ems_common.dto.NotificationEventType; import com.example.ems_common.exceptions.AlreadyExistsException; import com.example.ems_common.exceptions.CannotJoinOwnEventException; +import com.example.ems_common.exceptions.EventAlreadyEndedException; import com.example.ems_common.exceptions.NotFoundException; +import com.example.event_service_client.enums.EventStatus; import com.example.ems_common.security.SecurityUtils; import com.example.event_service_app.entity.Event; import com.example.event_service_app.entity.OutboxEvent; @@ -48,6 +50,12 @@ public ParticipationResponseDto register(ParticipationCreateDto dto) { throw new CannotJoinOwnEventException("You cannot join an event that you have created"); } + if (event.getEndDate().isBefore(LocalDateTime.now()) || + event.getStatus() == EventStatus.COMPLETED || + event.getStatus() == EventStatus.CANCELLED) { + throw new EventAlreadyEndedException("Event has already ended or been cancelled"); + } + if (participationRepository.existsByEventIdAndParticipantId(dto.getEventId(), currentUserId)) { throw new AlreadyExistsException("Already registered for this event"); } From e5750800ea57c7b3b9d55e0291c7506e1d0c11de Mon Sep 17 00:00:00 2001 From: berkai Date: Tue, 23 Jun 2026 22:35:28 +0300 Subject: [PATCH 2/2] bump version to 1.1.8 in pom.xml --- ems-common/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ems-common/pom.xml b/ems-common/pom.xml index ba9e536..3afe918 100644 --- a/ems-common/pom.xml +++ b/ems-common/pom.xml @@ -10,7 +10,7 @@ com.example ems-common - 1.1.7 + 1.1.8 ems-common Common module for Event Management System