LoanService.java
package com.student_loan.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import com.student_loan.model.Item;
import com.student_loan.model.Loan;
import com.student_loan.model.Loan.Status;
import com.student_loan.model.User;
import com.student_loan.model.Item.ItemStatus;
import com.student_loan.model.Item;
import com.student_loan.repository.ItemRepository;
import com.student_loan.repository.LoanRepository;
import com.student_loan.repository.UserRepository;
import java.util.Date;
import java.util.ArrayList;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
@Service
public class LoanService {
@Autowired
private LoanRepository loanRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private ItemRepository itemRepository;
@Autowired
private NotificationService notificationService;
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(LoanService.class);
public List<Loan> getAllLoans() {
return loanRepository.findAll();
}
public Optional<Loan> getLoanById(Long id) {
return loanRepository.findById(id);
}
public List<Loan> getLoansByLender(Long userId) {
return loanRepository.findByLender(userId);
}
public List<Loan> getLoansByBorrower(Long userId) {
return loanRepository.findByBorrower(userId);
}
public List<Long> getLentItemsIdByUser(Long userId) {
List<Loan> loans = loanRepository.findByLenderAndLoanStatus(userId, Status.IN_USE);
List<Long> lentItems = new ArrayList<>();
for (Loan loan : loans) {
lentItems.add(loan.getItem());
}
return lentItems;
}
public List<Long> getBorrowedItemsIdByUser(Long userId) {
List<Loan> loans = loanRepository.findByBorrowerAndLoanStatus(userId, Status.IN_USE);
List<Long> borrowedItems = new ArrayList<>();
for (Loan loan : loans) {
borrowedItems.add(loan.getItem());
}
return borrowedItems;
}
public Loan saveLoan(Loan loan) {
// Validate lender first to satisfy tests
Optional<User> lenderOpt = userRepository.findById(loan.getLender());
if (lenderOpt == null || lenderOpt.isEmpty()) {
throw new RuntimeException("Failed to save loan with id " + loan.getId() + ": Lender not found with id: " + loan.getLender());
}
// Validate borrower
Optional<User> borrowerOpt = userRepository.findById(loan.getBorrower());
if (borrowerOpt == null || borrowerOpt.isEmpty()) {
throw new RuntimeException("Failed to save loan with id " + loan.getId() + ": Borrower not found with id: " + loan.getBorrower());
}
User borrower = borrowerOpt.get();
if (borrower.hasPenalty()) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
"Cannot borrow items while under penalty."
);
}
// Validate item
Optional<Item> itemOpt = itemRepository.findById(loan.getItem());
if (itemOpt == null || itemOpt.isEmpty()) {
throw new RuntimeException("Failed to save loan with id " + loan.getId() + ": Item not found with id: " + loan.getItem());
}
// Check active loans limit on new loans
if (loan.getId() == null && loan.getLoanStatus() == Loan.Status.IN_USE) {
int activos = loanRepository.countByBorrowerAndLoanStatus(
loan.getBorrower(), Loan.Status.IN_USE);
if (activos >= 3) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
"Failed to save loan with id " + loan.getId() + ": You already have 3 items reserved. Return an item before booking another."
);
}
}
// Change item's status in DB
Item itemToModify = itemOpt.get();
itemToModify.setStatus(ItemStatus.BORROWED);
itemRepository.save(itemToModify);
// Save loan
return loanRepository.save(loan);
}
public boolean returnLoan(Long itemId, Long borrowerId) {
Optional<Loan> optionalLoan = loanRepository.findByBorrowerAndItemAndLoanStatus(
borrowerId, itemId, Loan.Status.IN_USE);
if (optionalLoan != null && optionalLoan.isPresent()) {
// Update item status
Optional<Item> optionalItem = itemRepository.findById(itemId);
if (optionalItem.isPresent()) {
Item item = optionalItem.get();
item.setStatus(ItemStatus.AVAILABLE);
itemRepository.save(item);
}
// Update loan status
Loan loan = optionalLoan.get();
loan.setLoanStatus(Loan.Status.RETURNED);
loan.setRealReturnDate(new Date());
Item item = itemRepository.findById(loan.getItem()).get();
item.setStatus(Item.ItemStatus.AVAILABLE);
itemRepository.save(item);
loanRepository.save(loan);
//Update lender ranking
Optional<User> lenderOpt = userRepository.findById(loan.getLender());
if (lenderOpt.isPresent()) {
User lender= lenderOpt.get();
double currentRating = 0;
if(lender!=null && lender.getAverageRating() != null) {
currentRating = lender.getAverageRating()+0.1;
if (currentRating > 5) {
currentRating = 5;
}
}
lender.setAverageRating(currentRating);
userRepository.save(lender);
}
// Mail to lender
if(userRepository.findById(loan.getLender()).isPresent() && userRepository.findById(loan.getBorrower()).isPresent()) {
notificationService.enviarCorreo(userRepository.findById(loan.getLender()).get().getEmail(), "Item returned",
"Your item has been returned!\nItem: " + itemRepository.findById(loan.getItem()).get().getName()
+ "\nBorrower: " + userRepository.findById(loan.getBorrower()).get().getName()
+ "\nReturn date: " + loan.getRealReturnDate().toString()
+ "\n\nThank you for using our service!");
// Mail to borrower
notificationService.enviarCorreo(userRepository.findById(loan.getBorrower()).get().getEmail(), "Item returned",
"You have returned the item!\nItem: " + itemRepository.findById(loan.getItem()).get().getName()
+ "\nLender: " + userRepository.findById(loan.getLender()).get().getName() + "\nReturn date: "
+ loan.getRealReturnDate().toString() + "\n\nThank you for using our service!");
}
return true;
} else {
return false;
}
}
public Loan createLoan(Loan loan) {
if (loan.getId() != null) {
if (loanRepository.existsById(loan.getId())) {
throw new RuntimeException("Loan already exists with id: " + loan.getId());
}
}
User lender = userRepository.findById(loan.getLender())
.orElseThrow(() -> new RuntimeException(
"Failed to save loan: Lender not found with id: " + loan.getLender()));
User borrower = userRepository.findById(loan.getBorrower())
.orElseThrow(() -> new RuntimeException(
"Failed to save loan: Borrower not found with id: " + loan.getBorrower()));
if (borrower.hasPenalty()) {
throw new RuntimeException("Cannot borrow items while under penalty.");
}
Item item = itemRepository.findById(loan.getItem())
.orElseThrow(() -> new RuntimeException(
"Failed to save loan: Item not found with id: " + loan.getItem()));
if (loan.getId() == null && loan.getLoanStatus() == Loan.Status.IN_USE) {
int activos = loanRepository.countByBorrowerAndLoanStatus(loan.getBorrower(), Loan.Status.IN_USE);
if (activos >= 3) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"You already have 3 items reserved. Return an item before booking another.");
}
}
loan.setLoanStatus(Loan.Status.IN_USE);
item.setStatus(Item.ItemStatus.BORROWED);
notificationService.enviarCorreo(
borrower.getEmail(),
"Loan Created",
"You have successfully made a loan!\nItem: " + item.getName() +
"\nLender: " + lender.getName() +
"\nReturn date: " + loan.getEstimatedReturnDate().toString() +
"\n\nThank you for using our service!"
);
notificationService.enviarCorreo(
lender.getEmail(),
"Item lended",
"Your item has successfully been lended!\nItem: " + item.getName() +
"\nBorrower: " + borrower.getName() +
"\nReturn date: " + loan.getEstimatedReturnDate().toString() +
"\n\nThank you for using our service!"
);
return loanRepository.save(loan);
}
public void deleteLoan(Long id) {
loanRepository.deleteById(id);
}
}