unify error handling
This commit is contained in:
@@ -13,6 +13,9 @@ import com.petshop.backend.dto.auth.ResetPasswordResponse;
|
||||
import com.petshop.backend.dto.auth.UserInfoResponse;
|
||||
import com.petshop.backend.entity.StoreLocation;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.BusinessException;
|
||||
import com.petshop.backend.exception.ConflictException;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import com.petshop.backend.security.JwtUtil;
|
||||
import com.petshop.backend.security.UserAuthCacheService;
|
||||
@@ -74,7 +77,7 @@ public class AuthController {
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {
|
||||
public ResponseEntity<RegisterResponse> register(@Valid @RequestBody RegisterRequest request) {
|
||||
String username = trimToNull(request.getUsername());
|
||||
String email = trimToNull(request.getEmail());
|
||||
String firstName = trimToNull(request.getFirstName());
|
||||
@@ -83,23 +86,17 @@ public class AuthController {
|
||||
|
||||
if (userRepository.findByUsername(username).isPresent()) {
|
||||
log.warn("Registration rejected: username already exists ({})", username);
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Username already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Username already exists");
|
||||
}
|
||||
|
||||
if (userRepository.findByEmail(email).isPresent()) {
|
||||
log.warn("Registration rejected: email already exists");
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Email already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Email already exists");
|
||||
}
|
||||
|
||||
if (phone != null && userRepository.findByPhone(phone).isPresent()) {
|
||||
log.warn("Registration rejected: phone already exists");
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Phone already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Phone already exists");
|
||||
}
|
||||
|
||||
User user = new User();
|
||||
@@ -117,9 +114,7 @@ public class AuthController {
|
||||
try {
|
||||
savedUser = userRepository.save(user);
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Username, email, or phone already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Username, email, or phone already exists");
|
||||
}
|
||||
|
||||
emailService.sendWelcome(savedUser);
|
||||
@@ -137,7 +132,7 @@ public class AuthController {
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {
|
||||
public ResponseEntity<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||
try {
|
||||
authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
|
||||
@@ -159,21 +154,15 @@ public class AuthController {
|
||||
|
||||
} catch (BadCredentialsException e) {
|
||||
log.warn("Login failed for username {}", request.getUsername());
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Invalid username or password");
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
|
||||
throw e;
|
||||
} catch (InternalAuthenticationServiceException e) {
|
||||
if (e.getCause() instanceof DisabledException disabledException) {
|
||||
log.warn("Login denied for disabled user {}", request.getUsername());
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", disabledException.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
|
||||
throw disabledException;
|
||||
}
|
||||
throw e;
|
||||
} catch (DisabledException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +185,7 @@ public class AuthController {
|
||||
|
||||
@Transactional
|
||||
@PutMapping("/me")
|
||||
public ResponseEntity<?> updateProfile(@Valid @RequestBody ProfileUpdateRequest request) {
|
||||
public ResponseEntity<UserInfoResponse> updateProfile(@Valid @RequestBody ProfileUpdateRequest request) {
|
||||
Long userId = AuthenticationHelper.getAuthenticatedUserId();
|
||||
User user = userRepository.findByIdForUpdate(userId)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
@@ -205,9 +194,7 @@ public class AuthController {
|
||||
String username = trimToNull(request.getUsername());
|
||||
if (username != null && !username.equals(user.getUsername())) {
|
||||
if (userRepository.findByUsername(username).isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Username already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Username already exists");
|
||||
}
|
||||
user.setUsername(username);
|
||||
invalidateToken = true;
|
||||
@@ -216,9 +203,7 @@ public class AuthController {
|
||||
String email = trimToNull(request.getEmail());
|
||||
if (email != null && !email.equals(user.getEmail())) {
|
||||
if (userRepository.findByEmail(email).isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Email already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Email already exists");
|
||||
}
|
||||
user.setEmail(email);
|
||||
}
|
||||
@@ -241,9 +226,7 @@ public class AuthController {
|
||||
if (phone != null && userRepository.findByPhone(phone)
|
||||
.filter(existing -> !existing.getId().equals(user.getId()))
|
||||
.isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Phone already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Phone already exists");
|
||||
}
|
||||
user.setPhone(phone);
|
||||
}
|
||||
@@ -262,9 +245,7 @@ public class AuthController {
|
||||
try {
|
||||
updatedUser = userRepository.save(user);
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Username, email, or phone already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
throw new ConflictException("Username, email, or phone already exists");
|
||||
}
|
||||
userAuthCacheService.evict(updatedUser.getId());
|
||||
return ResponseEntity.ok(toUserInfoResponse(updatedUser));
|
||||
@@ -306,17 +287,6 @@ public class AuthController {
|
||||
return trimToNull(PhoneUtils.normalize(trimToNull(value)));
|
||||
}
|
||||
|
||||
private NameParts splitFullName(String value) {
|
||||
String normalized = trimToNull(value);
|
||||
if (normalized == null) {
|
||||
throw new IllegalArgumentException("Full name is required");
|
||||
}
|
||||
String[] parts = normalized.split("\\s+", 2);
|
||||
String firstName = parts[0];
|
||||
String lastName = parts.length > 1 ? parts[1] : "";
|
||||
return new NameParts(firstName, lastName, joinFullName(firstName, lastName));
|
||||
}
|
||||
|
||||
private String joinFullName(String firstName, String lastName) {
|
||||
String first = trimToNull(firstName);
|
||||
String last = trimToNull(lastName);
|
||||
@@ -329,30 +299,21 @@ public class AuthController {
|
||||
return first + " " + last;
|
||||
}
|
||||
|
||||
private record NameParts(String firstName, String lastName, String fullName) {
|
||||
}
|
||||
|
||||
@PostMapping("/me/avatar")
|
||||
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
||||
public ResponseEntity<AvatarUploadResponse> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
||||
User user = getAuthenticatedUser();
|
||||
|
||||
if (file.isEmpty()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Please select a file to upload");
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
throw new BusinessException("Please select a file to upload");
|
||||
}
|
||||
|
||||
if (file.getSize() > 5 * 1024 * 1024) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "File size must not exceed 5MB");
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
throw new BusinessException("File size must not exceed 5MB");
|
||||
}
|
||||
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || (!contentType.equals("image/jpeg") && !contentType.equals("image/png") && !contentType.equals("image/gif"))) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Only JPG, PNG, and GIF images are allowed");
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
throw new BusinessException("Only JPG, PNG, and GIF images are allowed");
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -364,9 +325,7 @@ public class AuthController {
|
||||
return ResponseEntity.ok(new AvatarUploadResponse(avatarStorageService.toOwnerAvatarUrl(user), "Avatar uploaded successfully"));
|
||||
|
||||
} catch (IOException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Failed to upload avatar: " + e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
|
||||
throw new BusinessException("Failed to upload avatar: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,9 +334,7 @@ public class AuthController {
|
||||
User user = getAuthenticatedUser();
|
||||
|
||||
if (!avatarStorageService.hasAvatar(user)) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "No avatar uploaded");
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
|
||||
throw new ResourceNotFoundException("No avatar uploaded");
|
||||
}
|
||||
|
||||
Map<String, String> response = new HashMap<>();
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.petshop.backend.exception;
|
||||
|
||||
public class ConflictException extends RuntimeException {
|
||||
public ConflictException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.data.core.PropertyReferenceException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.FieldError;
|
||||
@@ -33,6 +35,11 @@ public class GlobalExceptionHandler {
|
||||
return buildErrorResponse(HttpStatus.BAD_REQUEST, ex.getMessage(), ex, request);
|
||||
}
|
||||
|
||||
@ExceptionHandler(ConflictException.class)
|
||||
public ResponseEntity<ApiErrorResponse> handleConflictException(ConflictException ex, HttpServletRequest request) {
|
||||
return buildErrorResponse(HttpStatus.CONFLICT, ex.getMessage(), ex, request);
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleValidationExceptions(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
Map<String, String> errors = new HashMap<>();
|
||||
@@ -54,6 +61,16 @@ public class GlobalExceptionHandler {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BadCredentialsException.class)
|
||||
public ResponseEntity<ApiErrorResponse> handleBadCredentials(BadCredentialsException ex, HttpServletRequest request) {
|
||||
return buildErrorResponse(HttpStatus.UNAUTHORIZED, "Invalid username or password", ex, request);
|
||||
}
|
||||
|
||||
@ExceptionHandler(DisabledException.class)
|
||||
public ResponseEntity<ApiErrorResponse> handleDisabledException(DisabledException ex, HttpServletRequest request) {
|
||||
return buildErrorResponse(HttpStatus.FORBIDDEN, ex.getMessage(), ex, request);
|
||||
}
|
||||
|
||||
@ExceptionHandler(org.springframework.security.access.AccessDeniedException.class)
|
||||
public ResponseEntity<ApiErrorResponse> handleAccessDeniedException(org.springframework.security.access.AccessDeniedException ex, HttpServletRequest request) {
|
||||
return buildErrorResponse(HttpStatus.FORBIDDEN, ex.getMessage(), ex, request);
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.petshop.backend.entity.Pet;
|
||||
import com.petshop.backend.entity.Sale;
|
||||
import com.petshop.backend.entity.StoreLocation;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.BusinessException;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.event.AdoptionConfirmedEvent;
|
||||
import com.petshop.backend.event.SaleReceiptEvent;
|
||||
@@ -96,7 +97,7 @@ public class AdoptionService {
|
||||
String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
|
||||
validatePetAvailability(pet, null, null);
|
||||
if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus) && request.getAdoptionDate() != null && request.getAdoptionDate().isAfter(LocalDate.now())) {
|
||||
throw new IllegalArgumentException("Cannot mark a future-dated adoption as Completed");
|
||||
throw new BusinessException("Cannot mark a future-dated adoption as Completed");
|
||||
}
|
||||
|
||||
Adoption adoption = new Adoption();
|
||||
@@ -139,7 +140,7 @@ public class AdoptionService {
|
||||
Long currentPetId = adoption.getPet() != null ? adoption.getPet().getPetId() : null;
|
||||
validatePetAvailability(pet, adoption.getAdoptionId(), currentPetId);
|
||||
if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus) && request.getAdoptionDate() != null && request.getAdoptionDate().isAfter(LocalDate.now())) {
|
||||
throw new IllegalArgumentException("Cannot mark a future-dated adoption as Completed");
|
||||
throw new BusinessException("Cannot mark a future-dated adoption as Completed");
|
||||
}
|
||||
|
||||
adoption.setPet(pet);
|
||||
@@ -168,7 +169,7 @@ public class AdoptionService {
|
||||
|
||||
// Verify the pet is actually located at the claimed store
|
||||
if (pet.getStore() == null || !pet.getStore().getStoreId().equals(sourceStoreId)) {
|
||||
throw new IllegalArgumentException("The specified pet is not located at the selected store.");
|
||||
throw new BusinessException("The specified pet is not located at the selected store.");
|
||||
}
|
||||
|
||||
// Verify the pet is available for adoption
|
||||
@@ -203,7 +204,7 @@ public class AdoptionService {
|
||||
}
|
||||
|
||||
if (!ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoption.getAdoptionStatus())) {
|
||||
throw new IllegalArgumentException("Only pending adoptions can be cancelled");
|
||||
throw new BusinessException("Only pending adoptions can be cancelled");
|
||||
}
|
||||
|
||||
adoption.setAdoptionStatus(ADOPTION_STATUS_CANCELLED);
|
||||
@@ -280,7 +281,7 @@ public class AdoptionService {
|
||||
User employee = userRepository.findById(requestedEmployeeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
|
||||
if (!isAssignableUser(employee)) {
|
||||
throw new IllegalArgumentException("Selected employee is not assignable for adoption work");
|
||||
throw new BusinessException("Selected employee is not assignable for adoption work");
|
||||
}
|
||||
return employee;
|
||||
}
|
||||
@@ -295,7 +296,7 @@ public class AdoptionService {
|
||||
|
||||
private String normalizeAdoptionStatus(String adoptionStatus) {
|
||||
if (adoptionStatus == null) {
|
||||
throw new IllegalArgumentException("Adoption status is required");
|
||||
throw new BusinessException("Adoption status is required");
|
||||
}
|
||||
String trimmedStatus = adoptionStatus.trim();
|
||||
if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(trimmedStatus)) {
|
||||
@@ -310,7 +311,7 @@ public class AdoptionService {
|
||||
if (ADOPTION_STATUS_MISSED.equalsIgnoreCase(trimmedStatus)) {
|
||||
return ADOPTION_STATUS_MISSED;
|
||||
}
|
||||
throw new IllegalArgumentException("Adoption status must be Pending, Completed, Cancelled, or Missed");
|
||||
throw new BusinessException("Adoption status must be Pending, Completed, Cancelled, or Missed");
|
||||
}
|
||||
|
||||
private void validatePetAvailability(Pet pet, Long adoptionId, Long currentPetId) {
|
||||
@@ -319,11 +320,11 @@ public class AdoptionService {
|
||||
? adoptionRepository.existsByPet_IdAndAdoptionStatusIgnoreCase(pet.getPetId(), ADOPTION_STATUS_COMPLETED)
|
||||
: adoptionRepository.existsByPet_IdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(pet.getPetId(), ADOPTION_STATUS_COMPLETED, adoptionId);
|
||||
if (adoptedElsewhere) {
|
||||
throw new IllegalArgumentException("Selected pet has already been adopted");
|
||||
throw new BusinessException("Selected pet has already been adopted");
|
||||
}
|
||||
|
||||
if (!samePetAsCurrentAdoption && !PET_STATUS_AVAILABLE.equalsIgnoreCase(pet.getPetStatus())) {
|
||||
throw new IllegalArgumentException("Selected pet is not available for adoption");
|
||||
throw new BusinessException("Selected pet is not available for adoption");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,14 +116,14 @@ public class AppointmentService {
|
||||
// Customers must supply a pet that is Adopted and owned by them
|
||||
if (User.Role.CUSTOMER.equals(authenticatedUser.getRole())) {
|
||||
if (pet == null) {
|
||||
throw new IllegalArgumentException("A pet must be selected for your appointment");
|
||||
throw new BusinessException("A pet must be selected for your appointment");
|
||||
}
|
||||
if (pet.getOwner() == null || !pet.getOwner().getId().equals(authenticatedUser.getId())) {
|
||||
throw new IllegalArgumentException("The selected pet does not belong to your account");
|
||||
throw new BusinessException("The selected pet does not belong to your account");
|
||||
}
|
||||
String petStatus = pet.getPetStatus();
|
||||
if (!"Owned".equalsIgnoreCase(petStatus) && !"Adopted".equalsIgnoreCase(petStatus)) {
|
||||
throw new IllegalArgumentException("Only your own pets can be booked for appointments");
|
||||
throw new BusinessException("Only your own pets can be booked for appointments");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public class AppointmentService {
|
||||
|
||||
String status = appointment.getAppointmentStatus();
|
||||
if (!"Booked".equalsIgnoreCase(status) && !"Scheduled".equalsIgnoreCase(status)) {
|
||||
throw new IllegalArgumentException("Only booked or scheduled appointments can be cancelled");
|
||||
throw new BusinessException("Only booked or scheduled appointments can be cancelled");
|
||||
}
|
||||
|
||||
appointment.setAppointmentStatus("Cancelled");
|
||||
@@ -311,7 +311,7 @@ public class AppointmentService {
|
||||
if ("Booked".equalsIgnoreCase(request.getAppointmentStatus())) {
|
||||
LocalDateTime appointmentDateTime = LocalDateTime.of(request.getAppointmentDate(), request.getAppointmentTime());
|
||||
if (appointmentDateTime.isBefore(LocalDateTime.now())) {
|
||||
throw new IllegalArgumentException("Booked appointments must be scheduled in the future");
|
||||
throw new BusinessException("Booked appointments must be scheduled in the future");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,7 +363,7 @@ public class AppointmentService {
|
||||
boolean assignedToStore = assignableUsers.stream()
|
||||
.anyMatch(u -> u.getId().equals(requestedEmployeeId));
|
||||
if (!assignedToStore) {
|
||||
throw new IllegalArgumentException("Selected employee is not assignable for the selected store");
|
||||
throw new BusinessException("Selected employee is not assignable for the selected store");
|
||||
}
|
||||
return employee;
|
||||
}
|
||||
@@ -377,7 +377,7 @@ public class AppointmentService {
|
||||
List<Appointment> existingAppointments = appointmentRepository
|
||||
.findByEmployeeIdAndAppointmentDate(employee.getId(), date);
|
||||
if (!isSlotAvailable(existingAppointments, service, time, appointmentIdToIgnore)) {
|
||||
throw new IllegalArgumentException("The selected employee is already booked for this time slot");
|
||||
throw new BusinessException("The selected employee is already booked for this time slot");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ public class AppointmentService {
|
||||
List<Appointment> existingAppointments = appointmentRepository
|
||||
.findByPetIdAndAppointmentDate(pet.getPetId(), date);
|
||||
if (!isSlotAvailable(existingAppointments, service, time, appointmentIdToIgnore)) {
|
||||
throw new IllegalArgumentException("This pet already has an appointment during this time slot");
|
||||
throw new BusinessException("This pet already has an appointment during this time slot");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.petshop.backend.dto.common.BulkDeleteRequest;
|
||||
import com.petshop.backend.dto.common.CouponRequest;
|
||||
import com.petshop.backend.dto.common.CouponResponse;
|
||||
import com.petshop.backend.entity.Coupon;
|
||||
import com.petshop.backend.exception.BusinessException;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.repository.CouponRepository;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -39,7 +40,7 @@ public class CouponService {
|
||||
@Transactional
|
||||
public CouponResponse createCoupon(CouponRequest request) {
|
||||
if (couponRepository.findByCouponCode(request.getCouponCode()).isPresent()) {
|
||||
throw new IllegalArgumentException("Coupon code already exists: " + request.getCouponCode());
|
||||
throw new BusinessException("Coupon code already exists: " + request.getCouponCode());
|
||||
}
|
||||
|
||||
Coupon coupon = new Coupon();
|
||||
@@ -55,7 +56,7 @@ public class CouponService {
|
||||
|
||||
couponRepository.findByCouponCode(request.getCouponCode()).ifPresent(existing -> {
|
||||
if (!existing.getCouponId().equals(id)) {
|
||||
throw new IllegalArgumentException("Coupon code already exists: " + request.getCouponCode());
|
||||
throw new BusinessException("Coupon code already exists: " + request.getCouponCode());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.petshop.backend.entity.Adoption;
|
||||
import com.petshop.backend.entity.Pet;
|
||||
import com.petshop.backend.entity.StoreLocation;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.BusinessException;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.security.AppPrincipal;
|
||||
import com.petshop.backend.repository.AdoptionRepository;
|
||||
@@ -125,7 +126,7 @@ public class PetService {
|
||||
boolean hasBooked = linkedAppointments.stream()
|
||||
.anyMatch(a -> "Booked".equalsIgnoreCase(a.getAppointmentStatus()));
|
||||
if (hasBooked) {
|
||||
throw new IllegalArgumentException(
|
||||
throw new BusinessException(
|
||||
"Your pet has a booked appointment. Please cancel the appointment before removing your pet from our database.");
|
||||
}
|
||||
// Nullify the pet reference on non-booked appointments to avoid FK constraint violations
|
||||
@@ -280,18 +281,18 @@ public class PetService {
|
||||
|
||||
private void validateImageFile(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("Please select an image to upload");
|
||||
throw new BusinessException("Please select an image to upload");
|
||||
}
|
||||
if (file.getSize() > 5 * 1024 * 1024) {
|
||||
throw new IllegalArgumentException("Image file size must be less than 5MB");
|
||||
throw new BusinessException("Image file size must be less than 5MB");
|
||||
}
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null) {
|
||||
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
|
||||
throw new BusinessException("Only JPG, PNG, and GIF images are allowed");
|
||||
}
|
||||
String normalized = contentType.toLowerCase(Locale.ROOT);
|
||||
if (!normalized.equals("image/jpeg") && !normalized.equals("image/png") && !normalized.equals("image/gif")) {
|
||||
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
|
||||
throw new BusinessException("Only JPG, PNG, and GIF images are allowed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,18 +135,18 @@ public class ProductService {
|
||||
|
||||
private void validateImageFile(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("Please select an image to upload");
|
||||
throw new BusinessException("Please select an image to upload");
|
||||
}
|
||||
if (file.getSize() > 5 * 1024 * 1024) {
|
||||
throw new IllegalArgumentException("Image file size must be less than 5MB");
|
||||
throw new BusinessException("Image file size must be less than 5MB");
|
||||
}
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null) {
|
||||
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
|
||||
throw new BusinessException("Only JPG, PNG, and GIF images are allowed");
|
||||
}
|
||||
String normalized = contentType.toLowerCase(Locale.ROOT);
|
||||
if (!normalized.equals("image/jpeg") && !normalized.equals("image/png") && !normalized.equals("image/gif")) {
|
||||
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
|
||||
throw new BusinessException("Only JPG, PNG, and GIF images are allowed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ package com.petshop.backend.util;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import com.petshop.backend.security.AppPrincipal;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@@ -13,7 +16,7 @@ public class AuthenticationHelper {
|
||||
public static Authentication getAuthentication() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
throw new RuntimeException("No authenticated user found");
|
||||
throw new AuthenticationCredentialsNotFoundException("No authenticated user found");
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
@@ -23,7 +26,7 @@ public class AuthenticationHelper {
|
||||
if (principal instanceof AppPrincipal appPrincipal) {
|
||||
return appPrincipal;
|
||||
}
|
||||
throw new RuntimeException("Authenticated principal is not supported");
|
||||
throw new AuthenticationServiceException("Authenticated principal is not supported");
|
||||
}
|
||||
|
||||
public static Long getAuthenticatedUserId() {
|
||||
@@ -36,11 +39,11 @@ public class AuthenticationHelper {
|
||||
|
||||
if (principal instanceof AppPrincipal appPrincipal) {
|
||||
return userRepository.findById(appPrincipal.getUserId())
|
||||
.orElseThrow(() -> new RuntimeException("User not found: " + appPrincipal.getUserId()));
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + appPrincipal.getUserId()));
|
||||
}
|
||||
|
||||
String username = authentication.getName();
|
||||
return userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new RuntimeException("User not found: " + username));
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user