wiki:Advanced Application Development

Version 1 (modified by 231035, 7 days ago) ( diff )

--

Advanced Application Development

Transactional

Adding a listing as a favorite

@Transactional
    public void addFavorite(Long userId, Long listingId) {
        Client client = clientRepository.findByUserId(userId)
            .orElseThrow(() -> new RuntimeException("Client not found"));

        Listing listing = listingRepository.findById(listingId)
            .orElseThrow(() -> new RuntimeException("Listing not found"));

        FavoriteListing favorite = new FavoriteListing(client, listing);
        favoriteRepository.save(favorite);
        logger.info("Added favorite - User: {}, Listing: {}", userId, listingId);
    }

Removing a listing from favorites

@Transactional
    public void removeFavorite(Long userId, Long listingId) {
        Client client = clientRepository.findByUserId(userId)
            .orElseThrow(() -> new RuntimeException("Client not found"));

        Listing listing = listingRepository.findById(listingId)
            .orElseThrow(() -> new RuntimeException("Listing not found"));

        FavoriteListing favorite = favoriteRepository.findByClientAndListing(client, listing)
            .orElseThrow(() -> new RuntimeException("Favorite not found"));

        favoriteRepository.delete(favorite);
        logger.info("Removed favorite - User: {}, Listing: {}", userId, listingId);
    }

Getting information from the favorite_listings table

    @Transactional(readOnly = true)
    public List<ListingDTO> getFavoritedListings(Long userId) {
        return favoriteRepository.findFavoritedListingDTOs(userId);
    }

    @Transactional(readOnly = true)
    public boolean isFavorited(Long userId, Long listingId) {
        Client client = clientRepository.findByUserId(userId)
            .orElse(null);

        if (client == null) return false;

        Listing listing = listingRepository.findById(listingId)
            .orElse(null);

        if (listing == null) return false;

        return favoriteRepository.findByClientAndListing(client, listing).isPresent();
    }

Creating a review

@Transactional
    public ReviewDTO createReview(Long reviewerId, Long targetUserId, CreateReviewRequest request) {
        logger.info("==== START createReview ====");
        // Validate rating
        if (request.getRating() == null || request.getRating() < 1 || request.getRating() > 5) {
            logger.error(" VALIDATION FAILED: Invalid rating: {}", request.getRating());
            throw new RuntimeException("Rating must be between 1 and 5");
        }
     

        // Check if reviewer exists
        User reviewer = userRepository.findById(reviewerId)
                .orElseThrow(() -> {
                    logger.error(" Reviewer not found with ID: {}", reviewerId);
                    return new RuntimeException("Reviewer not found");
                });

        // Check if target user exists
        User targetUser = userRepository.findById(targetUserId)
                .orElseThrow(() -> {
                    logger.error(" Target user not found with ID: {}", targetUserId);
                    return new RuntimeException("Target user not found");
                });

        // Check if review already exists and is not deleted
        logger.info("Checking if reviewer {} has already reviewed user {}", reviewerId, targetUserId);
        var existingReview = userReviewRepository.findTopByReviewReviewerUserIdAndTargetUserIdAndReviewIsDeletedFalseOrderByReviewCreatedAtDesc(reviewerId, targetUserId);

        if (existingReview.isPresent()) {
            Review existingReviewEntity = existingReview.get().getReview();
            logger.info("Found existing review with ID: {}", existingReviewEntity.getReviewId());
            logger.info("Existing review isDeleted status: {}", existingReviewEntity.getIsDeleted());

            if (!existingReviewEntity.getIsDeleted()) {
                logger.error(" User {} has already reviewed user {} and review is NOT deleted", reviewerId, targetUserId);
                throw new RuntimeException("You have already reviewed this user");
            } else {
                logger.info(" User {} has a deleted review for user {} - can create a new one", reviewerId, targetUserId);
            }
        } else {
            logger.info(" No existing review found - safe to create new review");
        }

        // Create Review entity
        Review review = new Review(reviewer, request.getRating(), request.getComment());


        // Save Review to database with flush
        review = reviewRepository.saveAndFlush(review);
        logger.info("Review ID after save: {}", review.getReviewId());

        if (review.getReviewId() == null) {
            logger.error(" CRITICAL: Review ID is NULL after save!");
            throw new RuntimeException("Failed to save review - ID is null");
        }

        // Create UserReview entry
        UserReview userReview = new UserReview();
        logger.info("Setting Review on UserReview (will copy ID via @MapsId)...");
        userReview.setReview(review);
        logger.info("UserReview reviewId after setReview: {}", userReview.getReviewId());

        userReview.setTargetUserId(targetUserId);

        // Save UserReview to database with flush
        userReview = userReviewRepository.saveAndFlush(userReview);
        logger.info(" UserReview saved successfully");

        // Create and return DTO
        ReviewDTO reviewDTO = new ReviewDTO(review);
        logger.info(" ReviewDTO created successfully");

        return reviewDTO;
    }

Deleting a review

@Transactional
    public void deleteReview(Long reviewId, Long userId) {
        logger.info("=== START deleteReview (SOFT DELETE) ===");
        

        // Fetch review
        logger.info("Fetching review with ID: {}", reviewId);
        Review review = reviewRepository.findById(reviewId)
                .orElseThrow(() -> {
                    logger.error(" Review not found with ID: {}", reviewId);
                    return new RuntimeException("Review not found");
                });
        logger.info(" Review found: reviewer={}, rating={}", review.getReviewer().getUsername(), review.getRating());

        // Check authorization
        if (!review.getReviewer().getUserId().equals(userId)) {
            logger.error(" User {} is not authorized to delete review {}. Reviewer is {}", userId, reviewId, review.getReviewer().getUserId());
            throw new RuntimeException("You can only delete your own reviews");
        }

        // Soft delete: mark as deleted instead of physically removing
        review.setIsDeleted(true);
        review.setUpdatedAt(LocalDateTime.now());
        reviewRepository.save(review);

        logger.info("=== END deleteReview - SUCCESS ===");
    }

Creating a new pet

@Transactional
    public AnimalResponseDTO addPet(Long userId,@RequestBody CreatePetRequest request) {

        // Validate required fields
        if (request.getName() == null || request.getName().isBlank()) {
            throw new RuntimeException("Pet name is required");
        }

        if (request.getSex() == null || request.getSex().isBlank()) {
            throw new RuntimeException("Pet sex is required");
        }

        if (request.getType() == null || request.getType().isBlank()) {
            throw new RuntimeException("Pet type is required");
        }


        if (request.getSpecies() == null || request.getSpecies().isBlank()) {
            throw new RuntimeException("Pet species is required");
        }

        // Get user
        User user = userRepository.findById(userId)
                .orElseThrow(() -> {
                    logger.error(" User not found with ID: {}", userId);
                    return new RuntimeException("User not found");
                });

        logger.info("Adding pet for user ID: {}", userId);

        // Check if user is already an owner, if not, promote them
        Owner owner = ownerRepository.findByUserId(userId)
                .orElseGet(() -> {
                    logger.info("⚠User {} is a CLIENT, promoting to OWNER", userId);
                    Owner newOwner = new Owner(user);
                    Owner savedOwner = ownerRepository.save(newOwner);
                    logger.info(" User promoted to OWNER with ID: {}", savedOwner.getUserId());
                    return savedOwner;
                });

        // Create new pet with all schema fields

        Pet pet = new Pet(
                request.getName(),
                request.getSex(),
                request.getDateOfBirth(),
                request.getPhotoUrl(),
                request.getType(),
                request.getSpecies(),
                request.getBreed(),
                request.getLocatedName(),
                owner
        );
        
        Pet savedPet = petRepository.save(pet);
        logger.info("Pet ID: {}, Owner ID: {}, Name: {}",
                savedPet.getAnimalId(), userId, savedPet.getName());

        AnimalResponseDTO result = new AnimalResponseDTO(savedPet);


        return result;
    }

Creating a new listing

   @Transactional
    public ListingDTO createListing(Long userId, CreateListingRequest request) {
        // Check if user is an owner
        Owner owner = ownerRepository.findByUserId(userId)
            .orElseThrow(() -> new RuntimeException("User is not an owner. Only owners can create listings."));

        logger.info("Creating listing for owner ID: {}", userId);

        // Create new listing with Owner object
        Listing listing = new Listing(
            owner,
            request.getAnimalId(),
            request.getPrice(),
            request.getDescription()
        );

        Listing savedListing = listingRepository.save(listing);

        logger.info("Listing created successfully - ID: {}, Owner ID: {}, Animal ID: {}",
            savedListing.getListingId(), userId, request.getAnimalId());

        return mapToDTO(savedListing);
    }
Note: See TracWiki for help on using the wiki.