diff --git a/src/main/java/com/libraryman_api/controller/BookController.java b/src/main/java/com/libraryman_api/controller/BookController.java index 92601fe..7dea8dd 100644 --- a/src/main/java/com/libraryman_api/controller/BookController.java +++ b/src/main/java/com/libraryman_api/controller/BookController.java @@ -9,17 +9,36 @@ import java.util.List; +/** + * REST controller for managing books in the LibraryMan application. + * This controller provides endpoints for performing CRUD operations on books, + * including retrieving all books, getting a book by its ID, adding a new book, + * updating an existing book, and deleting a book. + */ @RestController @RequestMapping("/api/books") public class BookController { + @Autowired private BookService bookService; + /** + * Retrieves a list of all books in the library. + * + * @return a list of {@link Books} objects representing all the books in the library. + */ @GetMapping public List getAllBooks() { return bookService.getAllBooks(); } + /** + * Retrieves a book by its ID. + * + * @param id the ID of the book to retrieve. + * @return a {@link ResponseEntity} containing the {@link Books} object, if found. + * @throws ResourceNotFoundException if the book with the specified ID is not found. + */ @GetMapping("/{id}") public ResponseEntity getBookById(@PathVariable int id) { return bookService.getBookById(id) @@ -27,19 +46,36 @@ public ResponseEntity getBookById(@PathVariable int id) { .orElseThrow(() -> new ResourceNotFoundException("Book not found")); } + /** + * Adds a new book to the library. + * + * @param book the {@link Books} object representing the new book to add. + * @return the added {@link Books} object. + */ @PostMapping public Books addBook(@RequestBody Books book) { return bookService.addBook(book); } + /** + * Updates an existing book in the library. + * + * @param id the ID of the book to update. + * @param bookDetails the {@link Books} object containing the updated book details. + * @return the updated {@link Books} object. + */ @PutMapping("/{id}") public Books updateBook(@PathVariable int id, @RequestBody Books bookDetails) { return bookService.updateBook(id, bookDetails); } + /** + * Deletes a book from the library by its ID. + * + * @param id the ID of the book to delete. + */ @DeleteMapping("/{id}") public void deleteBook(@PathVariable int id) { bookService.deleteBook(id); } } - diff --git a/src/main/java/com/libraryman_api/controller/BorrowingController.java b/src/main/java/com/libraryman_api/controller/BorrowingController.java index 50fdc2b..a0597d9 100644 --- a/src/main/java/com/libraryman_api/controller/BorrowingController.java +++ b/src/main/java/com/libraryman_api/controller/BorrowingController.java @@ -4,46 +4,93 @@ import com.libraryman_api.exception.ResourceNotFoundException; import com.libraryman_api.service.BorrowingService; import org.springframework.web.bind.annotation.*; + import java.util.List; +/** + * REST controller for managing borrowings in the LibraryMan application. + * This controller provides endpoints for performing operations related to borrowing and returning books, + * paying fines, and retrieving borrowing records. + */ @RestController @RequestMapping("/api/borrowings") public class BorrowingController { private final BorrowingService borrowingService; + + /** + * Constructs a new {@code BorrowingController} with the specified {@link BorrowingService}. + * + * @param borrowingService the service used to handle borrowing-related operations. + */ public BorrowingController(BorrowingService borrowingService) { this.borrowingService = borrowingService; } + /** + * Retrieves a list of all borrowing records in the library. + * + * @return a list of {@link Borrowings} objects representing all borrowings. + */ @GetMapping public List getAllBorrowings() { return borrowingService.getAllBorrowings(); } + /** + * Records a new book borrowing. + * + * @param borrowing the {@link Borrowings} object containing borrowing details. + * @return the saved {@link Borrowings} object representing the borrowing record. + */ @PostMapping public Borrowings borrowBook(@RequestBody Borrowings borrowing) { return borrowingService.borrowBook(borrowing); } + /** + * Marks a borrowed book as returned. + * + * @param id the ID of the borrowing record to update. + */ @PutMapping("/{id}/return") public void returnBook(@PathVariable int id) { borrowingService.returnBook(id); } + /** + * Pays the fine for an overdue book. + * + * @param id the ID of the borrowing record for which the fine is being paid. + * @return a message indicating the payment status. + */ @PutMapping("/{id}/pay") public String payFine(@PathVariable int id) { System.out.println("Pay Fine Id: " + id); return borrowingService.payFine(id); } + /** + * Retrieves all borrowing records for a specific member. + * + * @param memberId the ID of the member whose borrowing records are to be retrieved. + * @return a list of {@link Borrowings} objects representing the member's borrowings. + */ @GetMapping("member/{memberId}") public List getAllBorrowingsOfAMember(@PathVariable int memberId) { return borrowingService.getAllBorrowingsOfMember(memberId); } - + /** + * Retrieves a borrowing record by its ID. + * + * @param borrowingId the ID of the borrowing record to retrieve. + * @return the {@link Borrowings} object representing the borrowing record. + * @throws ResourceNotFoundException if the borrowing record with the specified ID is not found. + */ @GetMapping("{borrowingId}") public Borrowings getBorrowingById(@PathVariable int borrowingId) { - return borrowingService.getBorrowingById(borrowingId).orElseThrow(() -> new ResourceNotFoundException("Borrowing not found")); + return borrowingService.getBorrowingById(borrowingId) + .orElseThrow(() -> new ResourceNotFoundException("Borrowing not found")); } } diff --git a/src/main/java/com/libraryman_api/controller/GlobalExceptionHandler.java b/src/main/java/com/libraryman_api/controller/GlobalExceptionHandler.java index 6581e1a..ece2876 100644 --- a/src/main/java/com/libraryman_api/controller/GlobalExceptionHandler.java +++ b/src/main/java/com/libraryman_api/controller/GlobalExceptionHandler.java @@ -10,8 +10,24 @@ import java.util.Date; +/** + * Global exception handler for the LibraryMan API. + * This class provides centralized exception handling across all controllers in the application. + * It handles specific exceptions and returns appropriate HTTP responses. + */ @ControllerAdvice public class GlobalExceptionHandler { + + /** + * Handles {@link ResourceNotFoundException} exceptions. + * This method is triggered when a {@code ResourceNotFoundException} is thrown in the application. + * It constructs an {@link ErrorDetails} object containing the exception details and returns + * a {@link ResponseEntity} with an HTTP status of {@code 404 Not Found}. + * + * @param ex the exception that was thrown. + * @param request the current web request in which the exception was thrown. + * @return a {@link ResponseEntity} containing the {@link ErrorDetails} and an HTTP status of {@code 404 Not Found}. + */ @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); diff --git a/src/main/java/com/libraryman_api/controller/MemberController.java b/src/main/java/com/libraryman_api/controller/MemberController.java index 85afc93..36c7218 100644 --- a/src/main/java/com/libraryman_api/controller/MemberController.java +++ b/src/main/java/com/libraryman_api/controller/MemberController.java @@ -1,33 +1,49 @@ package com.libraryman_api.controller; -import com.libraryman_api.entity.Borrowings; import com.libraryman_api.entity.Members; import com.libraryman_api.exception.ResourceNotFoundException; -import com.libraryman_api.service.BorrowingService; import com.libraryman_api.service.MemberService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; - +/** + * REST controller for managing library members. + * This controller provides endpoints for performing CRUD operations on members. + */ @RestController @RequestMapping("/api/members") public class MemberController { private final MemberService memberService; + + /** + * Constructs a new {@code MemberController} with the specified {@link MemberService}. + * + * @param memberService the service to handle member-related operations + */ public MemberController(MemberService memberService) { this.memberService = memberService; } - - + /** + * Retrieves a list of all library members. + * + * @return a list of {@link Members} representing all members in the library + */ @GetMapping public List getAllMembers() { return memberService.getAllMembers(); } + /** + * Retrieves a library member by their ID. + * If the member is not found, a {@link ResourceNotFoundException} is thrown. + * + * @param id the ID of the member to retrieve + * @return a {@link ResponseEntity} containing the found {@link Members} object + */ @GetMapping("/{id}") public ResponseEntity getMemberById(@PathVariable int id) { return memberService.getMemberById(id) @@ -35,19 +51,38 @@ public ResponseEntity getMemberById(@PathVariable int id) { .orElseThrow(() -> new ResourceNotFoundException("Member not found")); } + /** + * Adds a new library member. + * + * @param member the {@link Members} object representing the new member + * @return the added {@link Members} object + */ @PostMapping public Members addMember(@RequestBody Members member) { return memberService.addMember(member); } + /** + * Updates an existing library member. + * If the member is not found, a {@link ResourceNotFoundException} is thrown. + * + * @param id the ID of the member to update + * @param memberDetails the {@link Members} object containing the updated details + * @return the updated {@link Members} object + */ @PutMapping("/{id}") public Members updateMember(@PathVariable int id, @RequestBody Members memberDetails) { return memberService.updateMember(id, memberDetails); } + /** + * Deletes a library member by their ID. + * If the member is not found, a {@link ResourceNotFoundException} is thrown. + * + * @param id the ID of the member to delete + */ @DeleteMapping("/{id}") public void deleteMember(@PathVariable int id) { memberService.deleteMember(id); } } - diff --git a/src/main/java/com/libraryman_api/email/EmailSender.java b/src/main/java/com/libraryman_api/email/EmailSender.java index 65e9a11..3689596 100644 --- a/src/main/java/com/libraryman_api/email/EmailSender.java +++ b/src/main/java/com/libraryman_api/email/EmailSender.java @@ -1,6 +1,22 @@ package com.libraryman_api.email; + import com.libraryman_api.entity.Notifications; +/** + * Interface representing an email sending service for the Library Management System. + * Classes implementing this interface are responsible for sending emails + * and updating the status of notifications in the system. + */ public interface EmailSender { - void send (String to, String body, String subject, Notifications notification); + + /** + * Sends an email to the specified recipient with the given body, subject, and notification details. + * + * @param to the email address of the recipient + * @param body the body of the email, typically in HTML or plain text format + * @param subject the subject of the email + * @param notification the notification entity associated with the email being sent, + * used to track the status of the notification + */ + void send(String to, String body, String subject, Notifications notification); } diff --git a/src/main/java/com/libraryman_api/email/EmailService.java b/src/main/java/com/libraryman_api/email/EmailService.java index 72a1c2a..2e2954e 100644 --- a/src/main/java/com/libraryman_api/email/EmailService.java +++ b/src/main/java/com/libraryman_api/email/EmailService.java @@ -13,43 +13,65 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; - +/** + * Service class for sending emails asynchronously. + * This class handles the construction and sending of MIME email messages. + */ @Service -public class EmailService implements EmailSender{ +public class EmailService implements EmailSender { private final NotificationRepository notificationRepository; private final JavaMailSender mailSender; - public EmailService(NotificationRepository notificationRepository, JavaMailSender mailSender) { - this.notificationRepository = notificationRepository; - this.mailSender = mailSender; - } - private final static Logger LOGGER = LoggerFactory - .getLogger(EmailService.class); + + private static final Logger LOGGER = LoggerFactory.getLogger(EmailService.class); @Value("${spring.mail.properties.domain_name}") private String domainName; + /** + * Constructs a new {@code EmailService} with the specified {@link NotificationRepository} and {@link JavaMailSender}. + * + * @param notificationRepository the repository for managing notification entities + * @param mailSender the mail sender for sending email messages + */ + public EmailService(NotificationRepository notificationRepository, JavaMailSender mailSender) { + this.notificationRepository = notificationRepository; + this.mailSender = mailSender; + } + /** + * Sends an email asynchronously to the specified recipient. + * If the email is successfully sent, the notification status is updated to SENT. + * If the email fails to send, the notification status is updated to FAILED and an exception is thrown. + * + * @param to the recipient's email address + * @param email the content of the email to send + * @param subject the subject of the email + * @param notification the {@link Notifications} object representing the email notification + */ @Override @Async - public void send(String to, String email, String subject, Notifications notification) { + public void send(String to, String email, String subject, Notifications notification) { try { MimeMessage mimeMessage = mailSender.createMimeMessage(); - MimeMessageHelper helper = - new MimeMessageHelper(mimeMessage, "utf-8"); + MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, "utf-8"); helper.setText(email, true); helper.setTo(to); helper.setSubject(subject); helper.setFrom(domainName); mailSender.send(mimeMessage); + // Update notification status to SENT notification.setNotificationStatus(NotificationStatus.SENT); notificationRepository.save(notification); } catch (MessagingException e) { - LOGGER.error("failed to send email", e); + LOGGER.error("Failed to send email", e); + + // Update notification status to FAILED notification.setNotificationStatus(NotificationStatus.FAILED); notificationRepository.save(notification); - throw new IllegalStateException("failed to send email"); + + throw new IllegalStateException("Failed to send email", e); } } } diff --git a/src/main/java/com/libraryman_api/entity/Notifications.java b/src/main/java/com/libraryman_api/entity/Notifications.java index 42c7d7b..a52ae9d 100644 --- a/src/main/java/com/libraryman_api/entity/Notifications.java +++ b/src/main/java/com/libraryman_api/entity/Notifications.java @@ -1,19 +1,14 @@ package com.libraryman_api.entity; import jakarta.persistence.*; - - import java.sql.Timestamp; - @Entity public class Notifications { + @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE, - generator = "notification_id_generator") - @SequenceGenerator(name = "notification_id_generator", - sequenceName = "notification_id_sequence", - allocationSize = 1) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "notification_id_generator") + @SequenceGenerator(name = "notification_id_generator", sequenceName = "notification_id_sequence", allocationSize = 1) @Column(name = "notification_id") private int notificationId; diff --git a/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java b/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java index 2c8c26b..24281e0 100644 --- a/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java +++ b/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java @@ -2,14 +2,32 @@ import java.io.Serial; +/** + * Custom exception class to handle scenarios where a requested resource + * is not found in the Library Management System. + * This exception is thrown when an operation fails to locate a resource such as + * a book, member, or borrowing record. + */ public class ResourceNotFoundException extends RuntimeException { + @Serial private static final long serialVersionUID = 1L; + /** + * Constructs a new {@code ResourceNotFoundException} with the specified detail message. + * + * @param message the detail message explaining the reason for the exception + */ public ResourceNotFoundException(String message) { super(message); } + /** + * Constructs a new {@code ResourceNotFoundException} with the specified detail message and cause. + * + * @param message the detail message explaining the reason for the exception + * @param cause the cause of the exception (which is saved for later retrieval by the {@link #getCause()} method) + */ public ResourceNotFoundException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/com/libraryman_api/service/BookService.java b/src/main/java/com/libraryman_api/service/BookService.java index 3693ca5..1b497e2 100644 --- a/src/main/java/com/libraryman_api/service/BookService.java +++ b/src/main/java/com/libraryman_api/service/BookService.java @@ -8,27 +8,72 @@ import java.util.List; import java.util.Optional; +/** + * Service class for managing books in the LibraryMan system. + * + *

This service provides methods to perform CRUD operations on books, including + * retrieving all books, retrieving a book by its ID, adding a new book, updating + * an existing book, and deleting a book by its ID.

+ * + *

Each method in this service interacts with the {@link BookRepository} to + * perform database operations.

+ * + *

In the case of an invalid book ID being provided, the service throws a + * {@link ResourceNotFoundException}.

+ * + * @author Ajay Negi + */ @Service public class BookService { private final BookRepository bookRepository; + /** + * Constructs a new {@code BookService} with the specified {@code BookRepository}. + * + * @param bookRepository the repository to be used by this service to interact with the database + */ public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } + /** + * Retrieves all books from the database. + * + * @return a list of all books + */ public List getAllBooks() { return bookRepository.findAll(); } + /** + * Retrieves a book by its ID. + * + * @param bookId the ID of the book to retrieve + * @return an {@code Optional} containing the found book, or {@code Optional.empty()} if no book was found + */ public Optional getBookById(int bookId) { return bookRepository.findById(bookId); } + /** + * Adds a new book to the database. + * + * @param book the book to be added + * @return the saved book + */ public Books addBook(Books book) { return bookRepository.save(book); } + /** + * Updates an existing book with the given details. + * + * @param bookId the ID of the book to update + * @param bookDetails the new details for the book + * @return the updated book + * @throws ResourceNotFoundException if the book with the specified ID is not found + */ public Books updateBook(int bookId, Books bookDetails) { Books book = bookRepository.findById(bookId) .orElseThrow(() -> new ResourceNotFoundException("Book not found")); @@ -42,16 +87,16 @@ public Books updateBook(int bookId, Books bookDetails) { return bookRepository.save(book); } + /** + * Deletes a book by its ID. + * + * @param bookId the ID of the book to delete + * @throws ResourceNotFoundException if the book with the specified ID is not found + */ public void deleteBook(int bookId) { Books book = bookRepository.findById(bookId) .orElseThrow(() -> new ResourceNotFoundException("Book not found")); - /**/ bookRepository.delete(book); } - - - - } - diff --git a/src/main/java/com/libraryman_api/service/BorrowingService.java b/src/main/java/com/libraryman_api/service/BorrowingService.java index b3e448c..1a1ce6e 100644 --- a/src/main/java/com/libraryman_api/service/BorrowingService.java +++ b/src/main/java/com/libraryman_api/service/BorrowingService.java @@ -15,6 +15,23 @@ import java.util.List; import java.util.Optional; +/** + * Service class for managing the borrowing and returning of books in the LibraryMan system. + * + *

This service handles the borrowing and returning processes, including calculating + * due dates, imposing fines for overdue returns, and managing the availability of books. + * It also integrates with the {@link NotificationService} to send notifications + * related to borrowing, returning, and fines.

+ * + *

Each method interacts with the {@link BorrowingRepository} and {@link FineRepository} + * to perform database operations, ensuring consistency and proper transactional behavior.

+ * + *

In cases where a book or borrowing record is not found, or if an operation cannot be completed + * (e.g., returning a book with an outstanding fine), the service throws a + * {@link ResourceNotFoundException}.

+ * + * @author Ajay Negi + */ @Service public class BorrowingService { @@ -22,6 +39,15 @@ public class BorrowingService { private final FineRepository fineRepository; private final NotificationService notificationService; private final BookService bookService; + + /** + * Constructs a new {@code BorrowingService} with the specified repositories and services. + * + * @param borrowingRepository the repository for managing borrowing records + * @param fineRepository the repository for managing fine records + * @param notificationService the service for sending notifications + * @param bookService the service for managing book records + */ public BorrowingService(BorrowingRepository borrowingRepository, FineRepository fineRepository, NotificationService notificationService, BookService bookService) { this.borrowingRepository = borrowingRepository; this.fineRepository = fineRepository; @@ -29,14 +55,37 @@ public BorrowingService(BorrowingRepository borrowingRepository, FineRepository this.bookService = bookService; } + /** + * Retrieves all borrowings from the database. + * + * @return a list of all borrowings + */ public List getAllBorrowings() { return borrowingRepository.findAll(); } + /** + * Retrieves a borrowing record by its ID. + * + * @param borrowingId the ID of the borrowing to retrieve + * @return an {@code Optional} containing the found borrowing, or {@code Optional.empty()} if no borrowing was found + */ public Optional getBorrowingById(int borrowingId) { return borrowingRepository.findById(borrowingId); } + /** + * Manages the borrowing process for a book. + * + *

This method is synchronized to ensure thread safety and is marked as + * {@link Transactional} to ensure that all database operations complete successfully + * or roll back in case of any errors. It updates the book's availability, sets the + * borrowing and due dates, and sends notifications related to the borrowing.

+ * + * @param borrowing the borrowing details provided by the user + * @return the saved borrowing record + * @throws ResourceNotFoundException if the book is not found or if there are not enough copies available + */ @Transactional public synchronized Borrowings borrowBook(Borrowings borrowing) { Optional book = bookService.getBookById(borrowing.getBook().getBookId()); @@ -62,7 +111,17 @@ public synchronized Borrowings borrowBook(Borrowings borrowing) { } } -// @Transactional + /** + * Manages the return process for a borrowed book. + * + *

This method is synchronized to ensure thread safety. It checks for overdue returns, + * imposes fines if necessary, and updates the book's availability. Notifications are sent + * for fines and successful returns. If a fine is imposed, the book cannot be returned until + * the fine is paid.

+ * + * @param borrowingId the ID of the borrowing record + * @throws ResourceNotFoundException if the borrowing record is not found, if the book has already been returned, or if there are outstanding fines + */ public synchronized void returnBook(int borrowingId) { Borrowings borrowing = getBorrowingById(borrowingId) .orElseThrow(() -> new ResourceNotFoundException("Borrowing not found")); @@ -88,15 +147,28 @@ public synchronized void returnBook(int borrowingId) { borrowingRepository.save(borrowing); } + /** + * Imposes a fine on a borrowing for overdue returns. + * + * @param borrowing the borrowing record with an overdue return + * @return the saved fine record + */ private Fines imposeFine(Borrowings borrowing) { Fines fine = new Fines(); -// fine.setAmount(BigDecimal.valueOf(10)); // Hard Coding fine for testing purpose fine.setAmount(calculateFineAmount(borrowing)); return fineRepository.save(fine); } - - + /** + * Processes the payment of a fine associated with a borrowing. + * + *

If the fine is successfully paid, a notification is sent. The borrowing and fine + * records are updated in the database.

+ * + * @param borrowingId the ID of the borrowing record with the fine + * @return a confirmation string indicating the fine has been paid + * @throws ResourceNotFoundException if the borrowing record is not found or if there is no outstanding fine + */ public String payFine(int borrowingId) { Borrowings borrowing = getBorrowingById(borrowingId) .orElseThrow(() -> new ResourceNotFoundException("Borrowing not found")); @@ -113,7 +185,18 @@ public String payFine(int borrowingId) { return "PAID"; } - + /** + * Updates the number of available copies of a book in the library. + * + *

This method increases or decreases the number of copies based on the specified operation. + * If there are not enough copies available for a removal operation, or if the book is not found, + * a {@link ResourceNotFoundException} is thrown.

+ * + * @param bookId the ID of the book to update + * @param operation the operation to perform ("ADD" to increase, "REMOVE" to decrease) + * @param numberOfCopies the number of copies to add or remove + * @throws ResourceNotFoundException if the book is not found or if there are not enough copies to remove + */ public void updateBookCopies(int bookId, String operation, int numberOfCopies) { Optional book = bookService.getBookById(bookId); @@ -133,22 +216,42 @@ public void updateBookCopies(int bookId, String operation, int numberOfCopies) { } } + /** + * Calculates the due date for a borrowing. + * + * @return the calculated due date, which is 15 days from the current date + */ private Date calculateDueDate() { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_YEAR, 15); return calendar.getTime(); } + /** + * Calculates the fine amount for an overdue borrowing. + * + *

The fine is calculated based on the number of overdue days multiplied by a fixed + * rate of 10 rupees per day.

+ * + * @param borrowing the borrowing record for which the fine is being calculated + * @return the calculated fine amount + */ private BigDecimal calculateFineAmount(Borrowings borrowing) { long overdueDays = ChronoUnit.DAYS.between( borrowing.getDueDate().toInstant(), new Date().toInstant()); System.out.println("Over Due Days" + overdueDays); - System.out.println("Fine Amount" + (overdueDays*10)); + System.out.println("Fine Amount" + (overdueDays * 10)); return BigDecimal.valueOf(overdueDays * 10); // 10 rupees per day fine } - + /** + * Retrieves all borrowings associated with a specific member. + * + * @param memberId the ID of the member whose borrowings are to be retrieved + * @return a list of borrowings associated with the specified member + * @throws ResourceNotFoundException if the member has not borrowed any books + */ public List getAllBorrowingsOfMember(int memberId) { return borrowingRepository.findByMember_memberId(memberId).orElseThrow(() -> new ResourceNotFoundException("Member didn't borrow any book")); } diff --git a/src/main/java/com/libraryman_api/service/MemberService.java b/src/main/java/com/libraryman_api/service/MemberService.java index 77b8caf..af60bad 100644 --- a/src/main/java/com/libraryman_api/service/MemberService.java +++ b/src/main/java/com/libraryman_api/service/MemberService.java @@ -1,40 +1,91 @@ package com.libraryman_api.service; import com.libraryman_api.entity.Members; -import com.libraryman_api.entity.Notifications; import com.libraryman_api.exception.ResourceNotFoundException; import com.libraryman_api.repository.MemberRepository; -import com.libraryman_api.repository.NotificationRepository; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; +/** + * Service class responsible for managing member-related operations in the LibraryMan system. + * + *

This service provides methods for retrieving, adding, updating, and deleting member records. + * It integrates with the {@link NotificationService} to send notifications related to member + * activities such as account creation, updates, and deletions.

+ * + *

Each method interacts with the {@link MemberRepository} to perform database operations, ensuring + * proper transactional behavior and consistency.

+ * + *

In cases where a member record is not found, the service throws a + * {@link ResourceNotFoundException} to indicate the error.

+ * + * @author Ajay Negi + */ @Service public class MemberService { private final MemberRepository memberRepository; private final NotificationService notificationService; + + /** + * Constructs a new {@code MemberService} with the specified repositories and services. + * + * @param memberRepository the repository for managing member records + * @param notificationService the service for sending notifications related to member activities + */ public MemberService(MemberRepository memberRepository, NotificationService notificationService) { this.memberRepository = memberRepository; this.notificationService = notificationService; } + /** + * Retrieves all members from the database. + * + * @return a list of all members + */ public List getAllMembers() { return memberRepository.findAll(); } + /** + * Retrieves a member record by its ID. + * + * @param memberId the ID of the member to retrieve + * @return an {@code Optional} containing the found member, or {@code Optional.empty()} if no member was found + */ public Optional getMemberById(int memberId) { return memberRepository.findById(memberId); } + /** + * Adds a new member to the library system. + * + *

This method saves the new member record in the database and sends a notification + * about the account creation.

+ * + * @param member the member details to be added + * @return the saved member record + */ public Members addMember(Members member) { - Members CurrentMember = memberRepository.save(member); - notificationService.accountCreatedNotification(CurrentMember); + Members currentMember = memberRepository.save(member); + notificationService.accountCreatedNotification(currentMember); - return CurrentMember; + return currentMember; } + /** + * Updates an existing member's details. + * + *

This method updates the member's details in the database. It throws a + * {@link ResourceNotFoundException} if the member is not found. After updating, + * a notification about the account details update is sent.

+ * + * @param memberId the ID of the member to update + * @param memberDetails the updated member details + * @return the updated member record + * @throws ResourceNotFoundException if the member is not found + */ public Members updateMember(int memberId, Members memberDetails) { Members member = memberRepository.findById(memberId) .orElseThrow(() -> new ResourceNotFoundException("Member not found")); @@ -43,18 +94,29 @@ public Members updateMember(int memberId, Members memberDetails) { member.setPassword(memberDetails.getPassword()); member.setRole(memberDetails.getRole()); member.setMembershipDate(memberDetails.getMembershipDate()); - member = memberRepository.save(member); + member = memberRepository.save(member); notificationService.accountDetailsUpdateNotification(member); return member; } + /** + * Deletes a member from the library system. + * + *

This method deletes the member record from the database after ensuring that + * the member has no outstanding fines or borrowed books. Before deletion, it + * sends a notification about the account deletion.

+ * + * @param memberId the ID of the member to delete + * @throws ResourceNotFoundException if the member is not found + */ public void deleteMember(int memberId) { Members member = memberRepository.findById(memberId) .orElseThrow(() -> new ResourceNotFoundException("Member not found")); - // write logic to check if there is any fine or any book to return. If there is nothing, then delete all notification, borrow, fine + // TODO: Implement logic to check if the member has any outstanding fines or borrowed books. + // If there are no pending obligations, delete all related notifications, borrowings, and fines. + notificationService.accountDeletionNotification(member); memberRepository.delete(member); } } - diff --git a/src/main/java/com/libraryman_api/service/NotificationService.java b/src/main/java/com/libraryman_api/service/NotificationService.java index 1bb0ada..7436b36 100644 --- a/src/main/java/com/libraryman_api/service/NotificationService.java +++ b/src/main/java/com/libraryman_api/service/NotificationService.java @@ -8,24 +8,42 @@ import org.springframework.stereotype.Service; import java.sql.Timestamp; - +/** + * Service class responsible for managing notifications within the LibraryMan application. + * This service handles various notification-related tasks, such as sending email notifications + * for account creation, deletion, borrowing books, returning books, fines, and account updates. + * It interacts with the {@link NotificationRepository}, {@link MemberRepository}, and {@link EmailSender} + * to perform these tasks. + */ @Service public class NotificationService { private final EmailSender emailSender; private final NotificationRepository notificationRepository; private final MemberRepository memberRepository; + + /** + * Constructs a new {@code NotificationService} with the specified {@link EmailSender}, + * {@link NotificationRepository}, and {@link MemberRepository}. + * + * @param emailSender the service responsible for sending emails. + * @param notificationRepository the repository to manage notifications in the database. + * @param memberRepository the repository to manage members in the database. + */ public NotificationService(EmailSender emailSender, NotificationRepository notificationRepository, MemberRepository memberRepository) { this.emailSender = emailSender; this.notificationRepository = notificationRepository; this.memberRepository = memberRepository; } - - + /** + * Sends a notification to a member when their account is created. + * + * @param members the member whose account has been created. + */ public void accountCreatedNotification(Members members) { Notifications notification = new Notifications(); notification.setMember(members); - notification.setMessage("We’re excited to welcome you to LibraryMan! Your account has been successfully created, and you’re now part of our community of book lovers. \uD83D\uDCDA

Feel free to explore our vast collection of books and other resources. If you have any questions or need assistance, our team is here to help.

Happy reading! \uD83D\uDCD6"); + notification.setMessage("We’re excited to welcome you to LibraryMan! Your account has been successfully created, and you’re now part of our community of book lovers. 📚

Feel free to explore our vast collection of books and other resources. If you have any questions or need assistance, our team is here to help.

Happy reading! 📖"); notification.setNotificationType(NotificationType.ACCOUNT_CREATED); notification.setSentDate(new Timestamp(System.currentTimeMillis())); @@ -33,6 +51,11 @@ public void accountCreatedNotification(Members members) { notificationRepository.save(notification); } + /** + * Sends a notification to a member when their account is deleted. + * + * @param members the member whose account has been deleted. + */ public void accountDeletionNotification(Members members) { Notifications notification = new Notifications(); notification.setMember(members); @@ -42,16 +65,20 @@ public void accountDeletionNotification(Members members) { sendNotification(notification); } - + /** + * Sends a notification to a member when they borrow a book. + * + * @param borrowing the borrowing instance containing information about the borrowed book. + */ public void borrowBookNotification(Borrowings borrowing) { Notifications notification = new Notifications(); notification.setMember(borrowing.getMember()); - notification.setMessage("Congratulations! \uD83C\uDF89 You have successfully borrowed '" + - borrowing.getBook().getTitle() + " book on " + // This is passing null value. correct it + notification.setMessage("Congratulations! 🎉 You have successfully borrowed '" + + borrowing.getBook().getTitle() + "' on " + borrowing.getBorrowDate() + ".

You now have 15 days to enjoy reading it. We kindly request that you return it to us on or before " + borrowing.getDueDate() + - " to avoid any late fees \uD83D\uDCC6, which are ₹10 per day for late returns.

If you need to renew the book or have any questions, please don't hesitate to reach out to us.

Thank you for choosing our library!"); + " to avoid any late fees 📆, which are ₹10 per day for late returns.

If you need to renew the book or have any questions, please don't hesitate to reach out to us.

Thank you for choosing our library!"); notification.setNotificationType(NotificationType.BORROW); notification.setSentDate(new Timestamp(System.currentTimeMillis())); @@ -59,17 +86,20 @@ public void borrowBookNotification(Borrowings borrowing) { notificationRepository.save(notification); } - + /** + * Sends a reminder notification to a member when the due date for returning a borrowed book is approaching. + * + * @param borrowing the borrowing instance containing information about the borrowed book. + */ public void reminderNotification(Borrowings borrowing) { Notifications notification = new Notifications(); notification.setMember(borrowing.getMember()); notification.setMessage("This is a friendly reminder that the due date to return '" + - borrowing.getBook().getTitle() + // This is passing null value. correct it - "' book is approaching. Please ensure that you return the book by " + + borrowing.getBook().getTitle() + "' is approaching. Please ensure that you return the book by " + borrowing.getBorrowDate() + - " to avoid any late fees. \uD83D\uDCC5" + + " to avoid any late fees. 📅" + "

If you need more time, consider renewing your book through our online portal or by contacting us." + - "

Thank you, and happy reading! \uD83D\uDE0A"); + "

Thank you, and happy reading! 😊"); notification.setNotificationType(NotificationType.REMINDER); notification.setSentDate(new Timestamp(System.currentTimeMillis())); @@ -77,13 +107,17 @@ public void reminderNotification(Borrowings borrowing) { notificationRepository.save(notification); } + /** + * Sends a notification to a member when they pay a fine for an overdue book. + * + * @param borrowing the borrowing instance containing information about the overdue book and the fine paid. + */ public void finePaidNotification(Borrowings borrowing) { Notifications notification = new Notifications(); notification.setMember(borrowing.getMember()); notification.setMessage("Thank you for your payment. We’ve received your payment of ₹" + - borrowing.getFine().getAmount() + " towards the fine for the overdue return of '" + borrowing.getBook().getTitle() + "' book. ✅" + - "

Your account has been updated accordingly. If you have any questions or need further assistance, please feel free to reach out.\n" + - "

Thank you for your prompt payment."); + borrowing.getFine().getAmount() + " towards the fine for the overdue return of '" + borrowing.getBook().getTitle() + "'. ✅" + + "

Your account has been updated accordingly. If you have any questions or need further assistance, please feel free to reach out.

Thank you for your prompt payment."); notification.setNotificationType(NotificationType.PAID); notification.setSentDate(new Timestamp(System.currentTimeMillis())); @@ -91,15 +125,19 @@ public void finePaidNotification(Borrowings borrowing) { notificationRepository.save(notification); } - + /** + * Sends a notification to a member when a fine is imposed for the late return of a borrowed book. + * + * @param borrowing the borrowing instance containing information about the overdue book and the fine imposed. + */ public void fineImposedNotification(Borrowings borrowing) { Notifications notification = new Notifications(); notification.setMember(borrowing.getMember()); notification.setMessage("We hope you enjoyed reading '" + borrowing.getBook().getTitle() + - "' book. Unfortunately, our records show that the book was returned after the due date of " + + "'. Unfortunately, our records show that the book was returned after the due date of " + borrowing.getDueDate() + - " . As a result, a fine of ₹10 per day has been imposed for the late return.

The total fine amount for this overdue return is ₹" + + ". As a result, a fine of ₹10 per day has been imposed for the late return.

The total fine amount for this overdue return is ₹" + borrowing.getFine().getAmount() + ".

If you have any questions or would like to discuss this matter further, please don't hesitate to contact us.

Thank you for your understanding and for being a valued member of our library."); notification.setNotificationType(NotificationType.FINE); @@ -109,7 +147,11 @@ public void fineImposedNotification(Borrowings borrowing) { notificationRepository.save(notification); } - + /** + * Sends a notification to a member when their account details are updated. + * + * @param members the member whose account details have been updated. + */ public void accountDetailsUpdateNotification(Members members) { Notifications notification = new Notifications(); notification.setMember(members); @@ -122,10 +164,15 @@ public void accountDetailsUpdateNotification(Members members) { notificationRepository.save(notification); } + /** + * Sends a notification to a member when they return a borrowed book. + * + * @param borrowing the borrowing instance containing information about the returned book. + */ public void bookReturnedNotification(Borrowings borrowing) { Notifications notification = new Notifications(); notification.setMember(borrowing.getMember()); - notification.setMessage("Thank you for returning '" + borrowing.getBook().getTitle() + "' book on " + borrowing.getReturnDate() + ". We hope you enjoyed the book!" + + notification.setMessage("Thank you for returning '" + borrowing.getBook().getTitle() + "' on " + borrowing.getReturnDate() + ". We hope you enjoyed the book!" + "

Feel free to explore our collection for your next read. If you have any questions or need assistance, we’re here to help." + "

Thank you for choosing LibraryMan!"); notification.setNotificationType(NotificationType.RETURNED); @@ -135,7 +182,11 @@ public void bookReturnedNotification(Borrowings borrowing) { notificationRepository.save(notification); } - + /** + * Sends the notification email to the member. + * + * @param notification the notification instance containing information about the notification. + */ private void sendNotification(Notifications notification) { Members member = memberRepository.findByMemberId(notification.getMember().getMemberId()) .orElseThrow(() -> new ResourceNotFoundException("Member not found")); @@ -152,20 +203,46 @@ private void sendNotification(Notifications notification) { ); } - private String subject(NotificationType notificationType) { - return switch (notificationType) { - case ACCOUNT_CREATED -> "Welcome to LibraryMan! \uD83C\uDF89"; - case ACCOUNT_DELETED -> "Account Deletion Confirmation 🗑️"; - case BORROW -> "Book Borrowed Successfully \uD83C\uDF89"; - case REMINDER -> "Reminder: Due date approaching ⏰"; - case PAID -> "Payment Received for Fine \uD83D\uDCB8"; - case FINE -> "Overdue Fine Imposed ‼️"; - case UPDATE -> "Account Details Updated \uD83D\uDD04"; - case RETURNED -> "Book Returned Successfully \uD83D\uDCDA"; - }; + /** + * Builds the email content based on the notification type, member name, and notification message. + * + * @param notificationType the type of notification. + * @param memberName the name of the member. + * @param notificationMessage the notification message to include in the email. + * @return the built email content. + */ + private String buildEmail(NotificationType notificationType, String memberName, String notificationMessage) { + return "Hello " + memberName + ",

" + notificationMessage + "

" + "Best regards,
The LibraryMan Team"; } - + /** + * Determines the subject line for the email based on the notification type. + * + * @param notificationType the type of notification. + * @return the subject line for the email. + */ + private String subject(NotificationType notificationType) { + switch (notificationType) { + case ACCOUNT_CREATED: + return "Welcome to LibraryMan!"; + case ACCOUNT_DELETED: + return "Your LibraryMan Account has been Deleted"; + case BORROW: + return "Book Borrowed Successfully"; + case REMINDER: + return "Reminder: Book Due Date Approaching"; + case PAID: + return "Payment Received"; + case FINE: + return "Fine Imposed for Late Return"; + case RETURNED: + return "Thank You for Returning Your Book"; + case UPDATE: + return "Your Account Details Have Been Updated"; + default: + return "LibraryMan Notification"; + } + } private String buildEmail(String notificationType,String memberName, String notificationMessage) { @@ -239,6 +316,7 @@ private String buildEmail(String notificationType,String memberName, String noti "
\n" + "\n" + "
"; + } } diff --git a/src/main/resources/application-development.properties b/src/main/resources/application-development.properties index b757e3b..94719e8 100644 --- a/src/main/resources/application-development.properties +++ b/src/main/resources/application-development.properties @@ -2,6 +2,8 @@ ## Create a Database and add database name where "Add_Your_Database_Name" is below ## Make this file as it was earlier before commiting code + +# Change this connection string to this format: jdbc:mysql://{ip_address}:{port}/{database_name} spring.datasource.url=jdbc:mysql://localhost:3306/Add_Your_Database_Name ## Add your Database Username and Password @@ -10,4 +12,6 @@ spring.datasource.password= Add_Your_Password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto=update -spring.jpa.show-sql=true \ No newline at end of file +spring.jpa.show-sql=true + +logging.level.org.springframework.security = TRACE \ No newline at end of file