新增书籍表、列表/搜索、管理员/馆员维护入口
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
package com.mzh.library.service.impl;
|
||||
|
||||
import com.mzh.library.dao.BookDao;
|
||||
import com.mzh.library.entity.AuthenticatedUser;
|
||||
import com.mzh.library.entity.Book;
|
||||
import com.mzh.library.entity.BookCategory;
|
||||
import com.mzh.library.entity.BookSearchCriteria;
|
||||
import com.mzh.library.entity.Permission;
|
||||
import com.mzh.library.exception.DaoException;
|
||||
import com.mzh.library.service.BookService;
|
||||
import com.mzh.library.service.PermissionPolicy;
|
||||
import com.mzh.library.service.ServiceResult;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BookServiceImpl implements BookService {
|
||||
private static final Logger LOGGER = Logger.getLogger(BookServiceImpl.class.getName());
|
||||
private static final String UNAVAILABLE_MESSAGE =
|
||||
"Book service is temporarily unavailable. Please try again later.";
|
||||
private static final String VALIDATION_MESSAGE = "Please correct the highlighted book fields.";
|
||||
private static final String DENIED_MESSAGE = "You do not have permission to manage books.";
|
||||
|
||||
private final BookDao bookDao;
|
||||
private final PermissionPolicy permissionPolicy;
|
||||
|
||||
public BookServiceImpl(BookDao bookDao) {
|
||||
this(bookDao, new PermissionPolicy());
|
||||
}
|
||||
|
||||
public BookServiceImpl(BookDao bookDao, PermissionPolicy permissionPolicy) {
|
||||
this.bookDao = bookDao;
|
||||
this.permissionPolicy = permissionPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<List<BookCategory>> listCategories() {
|
||||
try {
|
||||
return ServiceResult.success(bookDao.findAllCategories());
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to list book categories", ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<List<Book>> searchBooks(BookSearchCriteria criteria) {
|
||||
BookSearchCriteria normalized = criteria == null ? new BookSearchCriteria() : criteria;
|
||||
if (normalized.getCategoryId() != null && normalized.getCategoryId() <= 0) {
|
||||
Map<String, String> errors = new LinkedHashMap<>();
|
||||
errors.put("categoryId", "Select a valid category.");
|
||||
return ServiceResult.validationFailure("Please correct the catalog search filters.", errors);
|
||||
}
|
||||
|
||||
try {
|
||||
return ServiceResult.success(bookDao.search(normalized));
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to search books", ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Optional<Book>> findBook(long id) {
|
||||
if (id <= 0) {
|
||||
return ServiceResult.failure("Select a valid book.");
|
||||
}
|
||||
|
||||
try {
|
||||
return ServiceResult.success(bookDao.findById(id));
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to load book id=" + id, ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Long> createBook(AuthenticatedUser actor, Book book) {
|
||||
if (!canManageBooks(actor)) {
|
||||
return ServiceResult.failure(DENIED_MESSAGE);
|
||||
}
|
||||
|
||||
normalize(book);
|
||||
Map<String, String> errors = validate(book, false);
|
||||
if (!errors.isEmpty()) {
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
try {
|
||||
if (bookDao.findByIdentifier(book.getIdentifier()).isPresent()) {
|
||||
errors.put("identifier", "Book identifier is already in use.");
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
long id = bookDao.create(book);
|
||||
LOGGER.info("Created book id=" + id + " actorId=" + actor.getId());
|
||||
return ServiceResult.success(id, "Book created.");
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to create book actorId=" + actor.getId(), ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Void> updateBook(AuthenticatedUser actor, Book book) {
|
||||
if (!canManageBooks(actor)) {
|
||||
return ServiceResult.failure(DENIED_MESSAGE);
|
||||
}
|
||||
|
||||
normalize(book);
|
||||
Map<String, String> errors = validate(book, true);
|
||||
if (!errors.isEmpty()) {
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
try {
|
||||
Optional<Book> existingWithIdentifier = bookDao.findByIdentifier(book.getIdentifier());
|
||||
if (existingWithIdentifier.isPresent() && existingWithIdentifier.get().getId() != book.getId()) {
|
||||
errors.put("identifier", "Book identifier is already in use.");
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
if (!bookDao.update(book)) {
|
||||
return ServiceResult.failure("Book was not found.");
|
||||
}
|
||||
|
||||
LOGGER.info("Updated book id=" + book.getId() + " actorId=" + actor.getId());
|
||||
return ServiceResult.success(null, "Book updated.");
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to update book id=" + book.getId() + " actorId=" + actor.getId(), ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Void> deleteBook(AuthenticatedUser actor, long id) {
|
||||
if (!canManageBooks(actor)) {
|
||||
return ServiceResult.failure(DENIED_MESSAGE);
|
||||
}
|
||||
if (id <= 0) {
|
||||
return ServiceResult.failure("Select a valid book.");
|
||||
}
|
||||
|
||||
try {
|
||||
if (!bookDao.delete(id)) {
|
||||
return ServiceResult.failure("Book was not found.");
|
||||
}
|
||||
|
||||
LOGGER.info("Deleted book id=" + id + " actorId=" + actor.getId());
|
||||
return ServiceResult.success(null, "Book deleted.");
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to delete book id=" + id + " actorId=" + actor.getId(), ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canManageBooks(AuthenticatedUser actor) {
|
||||
return actor != null && permissionPolicy.allows(actor.getRole(), Permission.MANAGE_BOOKS);
|
||||
}
|
||||
|
||||
private void normalize(Book book) {
|
||||
if (book == null) {
|
||||
return;
|
||||
}
|
||||
book.setIdentifier(trim(book.getIdentifier()));
|
||||
book.setTitle(trim(book.getTitle()));
|
||||
book.setAuthor(trim(book.getAuthor()));
|
||||
}
|
||||
|
||||
private Map<String, String> validate(Book book, boolean requireId) {
|
||||
Map<String, String> errors = new LinkedHashMap<>();
|
||||
if (book == null) {
|
||||
errors.put("book", "Book details are required.");
|
||||
return errors;
|
||||
}
|
||||
|
||||
if (requireId && book.getId() <= 0) {
|
||||
errors.put("id", "Select a valid book.");
|
||||
}
|
||||
requireLength(errors, "identifier", book.getIdentifier(), "Book identifier", 64);
|
||||
requireLength(errors, "title", book.getTitle(), "Title", 200);
|
||||
requireLength(errors, "author", book.getAuthor(), "Author", 120);
|
||||
if (book.getCategoryId() <= 0) {
|
||||
errors.put("categoryId", "Select a category.");
|
||||
}
|
||||
if (book.getTotalCopies() < 0) {
|
||||
errors.put("totalCopies", "Total copies cannot be negative.");
|
||||
}
|
||||
if (book.getAvailableCopies() < 0) {
|
||||
errors.put("availableCopies", "Available copies cannot be negative.");
|
||||
}
|
||||
if (book.getAvailableCopies() > book.getTotalCopies()) {
|
||||
errors.put("availableCopies", "Available copies cannot exceed total copies.");
|
||||
}
|
||||
if (book.getStatus() == null) {
|
||||
errors.put("status", "Select a status.");
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
private void requireLength(Map<String, String> errors, String field, String value, String label, int maxLength) {
|
||||
if (value == null || value.isEmpty()) {
|
||||
errors.put(field, label + " is required.");
|
||||
return;
|
||||
}
|
||||
if (value.length() > maxLength) {
|
||||
errors.put(field, label + " must be " + maxLength + " characters or fewer.");
|
||||
}
|
||||
}
|
||||
|
||||
private String trim(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user