decouple emails from transactions
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
package com.petshop.backend.event;
|
||||||
|
|
||||||
|
public record AdoptionConfirmedEvent(Long adoptionId) {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.petshop.backend.event;
|
||||||
|
|
||||||
|
public record AdoptionReminderEvent(Long adoptionId) {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.petshop.backend.event;
|
||||||
|
|
||||||
|
public record AppointmentConfirmedEvent(Long appointmentId) {}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.petshop.backend.event;
|
||||||
|
|
||||||
|
public record AppointmentReminderEvent(Long appointmentId) {}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.petshop.backend.event;
|
||||||
|
|
||||||
|
import com.petshop.backend.repository.AdoptionRepository;
|
||||||
|
import com.petshop.backend.repository.AppointmentRepository;
|
||||||
|
import com.petshop.backend.repository.SaleRepository;
|
||||||
|
import com.petshop.backend.service.EmailService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.event.TransactionPhase;
|
||||||
|
import org.springframework.transaction.event.TransactionalEventListener;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class EmailEventListener {
|
||||||
|
|
||||||
|
private final AdoptionRepository adoptionRepository;
|
||||||
|
private final AppointmentRepository appointmentRepository;
|
||||||
|
private final SaleRepository saleRepository;
|
||||||
|
private final EmailService emailService;
|
||||||
|
|
||||||
|
public EmailEventListener(AdoptionRepository adoptionRepository, AppointmentRepository appointmentRepository, SaleRepository saleRepository, EmailService emailService) {
|
||||||
|
this.adoptionRepository = adoptionRepository;
|
||||||
|
this.appointmentRepository = appointmentRepository;
|
||||||
|
this.saleRepository = saleRepository;
|
||||||
|
this.emailService = emailService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
|
public void onAdoptionConfirmed(AdoptionConfirmedEvent event) {
|
||||||
|
adoptionRepository.findById(event.adoptionId())
|
||||||
|
.ifPresent(emailService::sendAdoptionConfirmation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
|
public void onAdoptionReminder(AdoptionReminderEvent event) {
|
||||||
|
adoptionRepository.findById(event.adoptionId())
|
||||||
|
.ifPresent(emailService::sendAdoptionReminder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
|
public void onAppointmentConfirmed(AppointmentConfirmedEvent event) {
|
||||||
|
appointmentRepository.findById(event.appointmentId())
|
||||||
|
.ifPresent(emailService::sendAppointmentConfirmation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
|
public void onAppointmentReminder(AppointmentReminderEvent event) {
|
||||||
|
appointmentRepository.findById(event.appointmentId())
|
||||||
|
.ifPresent(emailService::sendAppointmentReminder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
|
public void onSaleReceipt(SaleReceiptEvent event) {
|
||||||
|
saleRepository.findById(event.saleId())
|
||||||
|
.ifPresent(emailService::sendPurchaseReceipt);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.petshop.backend.event;
|
||||||
|
|
||||||
|
public record SaleReceiptEvent(Long saleId) {}
|
||||||
@@ -9,11 +9,14 @@ import com.petshop.backend.entity.Sale;
|
|||||||
import com.petshop.backend.entity.StoreLocation;
|
import com.petshop.backend.entity.StoreLocation;
|
||||||
import com.petshop.backend.entity.User;
|
import com.petshop.backend.entity.User;
|
||||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||||
|
import com.petshop.backend.event.AdoptionConfirmedEvent;
|
||||||
|
import com.petshop.backend.event.SaleReceiptEvent;
|
||||||
import com.petshop.backend.repository.AdoptionRepository;
|
import com.petshop.backend.repository.AdoptionRepository;
|
||||||
import com.petshop.backend.repository.PetRepository;
|
import com.petshop.backend.repository.PetRepository;
|
||||||
import com.petshop.backend.repository.SaleRepository;
|
import com.petshop.backend.repository.SaleRepository;
|
||||||
import com.petshop.backend.repository.StoreRepository;
|
import com.petshop.backend.repository.StoreRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -40,15 +43,15 @@ public class AdoptionService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final StoreRepository storeRepository;
|
private final StoreRepository storeRepository;
|
||||||
private final SaleRepository saleRepository;
|
private final SaleRepository saleRepository;
|
||||||
private final EmailService emailService;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, UserRepository userRepository, StoreRepository storeRepository, SaleRepository saleRepository, EmailService emailService) {
|
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, UserRepository userRepository, StoreRepository storeRepository, SaleRepository saleRepository, ApplicationEventPublisher eventPublisher) {
|
||||||
this.adoptionRepository = adoptionRepository;
|
this.adoptionRepository = adoptionRepository;
|
||||||
this.petRepository = petRepository;
|
this.petRepository = petRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.storeRepository = storeRepository;
|
this.storeRepository = storeRepository;
|
||||||
this.saleRepository = saleRepository;
|
this.saleRepository = saleRepository;
|
||||||
this.emailService = emailService;
|
this.eventPublisher = eventPublisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<AdoptionResponse> getAllAdoptions(String query, Long customerId, String status, Long storeId, LocalDate date, Pageable pageable) {
|
public Page<AdoptionResponse> getAllAdoptions(String query, Long customerId, String status, Long storeId, LocalDate date, Pageable pageable) {
|
||||||
@@ -110,7 +113,7 @@ public class AdoptionService {
|
|||||||
createSaleForAdoption(adoption, request.getPaymentMethod());
|
createSaleForAdoption(adoption, request.getPaymentMethod());
|
||||||
}
|
}
|
||||||
if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus)) {
|
if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus)) {
|
||||||
emailService.sendAdoptionConfirmation(adoption);
|
eventPublisher.publishEvent(new AdoptionConfirmedEvent(adoption.getAdoptionId()));
|
||||||
}
|
}
|
||||||
return mapToResponse(adoption);
|
return mapToResponse(adoption);
|
||||||
}
|
}
|
||||||
@@ -153,7 +156,7 @@ public class AdoptionService {
|
|||||||
}
|
}
|
||||||
boolean statusChanged = !adoptionStatus.equalsIgnoreCase(previousStatus);
|
boolean statusChanged = !adoptionStatus.equalsIgnoreCase(previousStatus);
|
||||||
if (statusChanged && (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus))) {
|
if (statusChanged && (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus))) {
|
||||||
emailService.sendAdoptionConfirmation(adoption);
|
eventPublisher.publishEvent(new AdoptionConfirmedEvent(adoption.getAdoptionId()));
|
||||||
}
|
}
|
||||||
return mapToResponse(adoption);
|
return mapToResponse(adoption);
|
||||||
}
|
}
|
||||||
@@ -343,7 +346,7 @@ public class AdoptionService {
|
|||||||
sale.setIsRefund(false);
|
sale.setIsRefund(false);
|
||||||
sale.setChannel("ADOPTION");
|
sale.setChannel("ADOPTION");
|
||||||
Sale savedSale = saleRepository.save(sale);
|
Sale savedSale = saleRepository.save(sale);
|
||||||
emailService.sendPurchaseReceipt(savedSale);
|
eventPublisher.publishEvent(new SaleReceiptEvent(savedSale.getSaleId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId, User customer) {
|
private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId, User customer) {
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import com.petshop.backend.entity.StoreLocation;
|
|||||||
import com.petshop.backend.entity.User;
|
import com.petshop.backend.entity.User;
|
||||||
import com.petshop.backend.exception.BusinessException;
|
import com.petshop.backend.exception.BusinessException;
|
||||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||||
|
import com.petshop.backend.event.AdoptionReminderEvent;
|
||||||
|
import com.petshop.backend.event.AppointmentConfirmedEvent;
|
||||||
|
import com.petshop.backend.event.AppointmentReminderEvent;
|
||||||
import com.petshop.backend.repository.AdoptionRepository;
|
import com.petshop.backend.repository.AdoptionRepository;
|
||||||
import com.petshop.backend.repository.AppointmentRepository;
|
import com.petshop.backend.repository.AppointmentRepository;
|
||||||
import com.petshop.backend.repository.PetRepository;
|
import com.petshop.backend.repository.PetRepository;
|
||||||
@@ -17,6 +20,7 @@ import com.petshop.backend.repository.ServiceRepository;
|
|||||||
import com.petshop.backend.repository.StoreRepository;
|
import com.petshop.backend.repository.StoreRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
import com.petshop.backend.util.AuthenticationHelper;
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
@@ -41,16 +45,16 @@ public class AppointmentService {
|
|||||||
private final StoreRepository storeRepository;
|
private final StoreRepository storeRepository;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final AdoptionRepository adoptionRepository;
|
private final AdoptionRepository adoptionRepository;
|
||||||
private final EmailService emailService;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, AdoptionRepository adoptionRepository, EmailService emailService) {
|
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, AdoptionRepository adoptionRepository, ApplicationEventPublisher eventPublisher) {
|
||||||
this.appointmentRepository = appointmentRepository;
|
this.appointmentRepository = appointmentRepository;
|
||||||
this.serviceRepository = serviceRepository;
|
this.serviceRepository = serviceRepository;
|
||||||
this.petRepository = petRepository;
|
this.petRepository = petRepository;
|
||||||
this.storeRepository = storeRepository;
|
this.storeRepository = storeRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.adoptionRepository = adoptionRepository;
|
this.adoptionRepository = adoptionRepository;
|
||||||
this.emailService = emailService;
|
this.eventPublisher = eventPublisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@@ -140,7 +144,7 @@ public class AppointmentService {
|
|||||||
appointment.setPet(pet);
|
appointment.setPet(pet);
|
||||||
|
|
||||||
appointment = appointmentRepository.save(appointment);
|
appointment = appointmentRepository.save(appointment);
|
||||||
emailService.sendAppointmentConfirmation(appointment);
|
eventPublisher.publishEvent(new AppointmentConfirmedEvent(appointment.getAppointmentId()));
|
||||||
return mapToResponse(appointment);
|
return mapToResponse(appointment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +183,7 @@ public class AppointmentService {
|
|||||||
appointment.setEmployee(employee);
|
appointment.setEmployee(employee);
|
||||||
|
|
||||||
appointment = appointmentRepository.save(appointment);
|
appointment = appointmentRepository.save(appointment);
|
||||||
emailService.sendAppointmentConfirmation(appointment);
|
eventPublisher.publishEvent(new AppointmentConfirmedEvent(appointment.getAppointmentId()));
|
||||||
return mapToResponse(appointment);
|
return mapToResponse(appointment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,13 +277,13 @@ public class AppointmentService {
|
|||||||
List<Appointment> tomorrowAppointments = appointmentRepository
|
List<Appointment> tomorrowAppointments = appointmentRepository
|
||||||
.findByAppointmentDateAndAppointmentStatusIgnoreCase(tomorrow, "Booked");
|
.findByAppointmentDateAndAppointmentStatusIgnoreCase(tomorrow, "Booked");
|
||||||
for (Appointment appointment : tomorrowAppointments) {
|
for (Appointment appointment : tomorrowAppointments) {
|
||||||
emailService.sendAppointmentReminder(appointment);
|
eventPublisher.publishEvent(new AppointmentReminderEvent(appointment.getAppointmentId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Adoption> tomorrowAdoptions = adoptionRepository
|
List<Adoption> tomorrowAdoptions = adoptionRepository
|
||||||
.findByAdoptionDateAndAdoptionStatusIgnoreCase(tomorrow, "Pending");
|
.findByAdoptionDateAndAdoptionStatusIgnoreCase(tomorrow, "Pending");
|
||||||
for (Adoption adoption : tomorrowAdoptions) {
|
for (Adoption adoption : tomorrowAdoptions) {
|
||||||
emailService.sendAdoptionReminder(adoption);
|
eventPublisher.publishEvent(new AdoptionReminderEvent(adoption.getAdoptionId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import com.petshop.backend.dto.sale.SaleResponse;
|
|||||||
import com.petshop.backend.entity.*;
|
import com.petshop.backend.entity.*;
|
||||||
import com.petshop.backend.exception.BusinessException;
|
import com.petshop.backend.exception.BusinessException;
|
||||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||||
|
import com.petshop.backend.event.SaleReceiptEvent;
|
||||||
import com.petshop.backend.repository.*;
|
import com.petshop.backend.repository.*;
|
||||||
import com.petshop.backend.util.AuthenticationHelper;
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -31,9 +33,9 @@ public class SaleService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final CouponRepository couponRepository;
|
private final CouponRepository couponRepository;
|
||||||
private final CartRepository cartRepository;
|
private final CartRepository cartRepository;
|
||||||
private final EmailService emailService;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository, CouponRepository couponRepository, CartRepository cartRepository, EmailService emailService) {
|
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository, CouponRepository couponRepository, CartRepository cartRepository, ApplicationEventPublisher eventPublisher) {
|
||||||
this.saleRepository = saleRepository;
|
this.saleRepository = saleRepository;
|
||||||
this.productRepository = productRepository;
|
this.productRepository = productRepository;
|
||||||
this.storeRepository = storeRepository;
|
this.storeRepository = storeRepository;
|
||||||
@@ -41,7 +43,7 @@ public class SaleService {
|
|||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.couponRepository = couponRepository;
|
this.couponRepository = couponRepository;
|
||||||
this.cartRepository = cartRepository;
|
this.cartRepository = cartRepository;
|
||||||
this.emailService = emailService;
|
this.eventPublisher = eventPublisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@@ -272,7 +274,7 @@ public class SaleService {
|
|||||||
Sale savedSale = saleRepository.save(sale);
|
Sale savedSale = saleRepository.save(sale);
|
||||||
|
|
||||||
if (!Boolean.TRUE.equals(savedSale.getIsRefund()) && savedSale.getCustomer() != null) {
|
if (!Boolean.TRUE.equals(savedSale.getIsRefund()) && savedSale.getCustomer() != null) {
|
||||||
emailService.sendPurchaseReceipt(savedSale);
|
eventPublisher.publishEvent(new SaleReceiptEvent(savedSale.getSaleId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapToResponse(savedSale);
|
return mapToResponse(savedSale);
|
||||||
|
|||||||
Reference in New Issue
Block a user