新增书籍表、列表/搜索、管理员/馆员维护入口
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
package com.mzh.library.service;
|
||||
|
||||
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.BookStatus;
|
||||
import com.mzh.library.entity.Permission;
|
||||
import com.mzh.library.entity.Role;
|
||||
import com.mzh.library.exception.DaoException;
|
||||
import com.mzh.library.service.impl.BookServiceImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
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 final class BookServiceCheck {
|
||||
private static final String UNAVAILABLE_MESSAGE =
|
||||
"Book service is temporarily unavailable. Please try again later.";
|
||||
|
||||
private BookServiceCheck() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Logger.getLogger(BookServiceImpl.class.getName()).setLevel(Level.OFF);
|
||||
|
||||
InMemoryBookDao dao = new InMemoryBookDao();
|
||||
BookService service = new BookServiceImpl(dao);
|
||||
AuthenticatedUser librarian = user(10L, Role.LIBRARIAN);
|
||||
AuthenticatedUser reader = user(20L, Role.READER);
|
||||
|
||||
ServiceResult<Long> invalidInventory = service.createBook(librarian,
|
||||
book(0L, "BK-1000", "Invalid Copies", "Test Author", 1L, 2, 3, BookStatus.AVAILABLE));
|
||||
require(!invalidInventory.isSuccessful(), "invalid inventory should fail");
|
||||
require(invalidInventory.getErrors().containsKey("availableCopies"),
|
||||
"available copies greater than total should be rejected");
|
||||
|
||||
ServiceResult<Long> denied = service.createBook(reader,
|
||||
book(0L, "BK-1001", "Reader Write", "Test Author", 1L, 1, 1, BookStatus.AVAILABLE));
|
||||
require(!denied.isSuccessful(), "reader write should fail");
|
||||
require("You do not have permission to manage books.".equals(denied.getMessage()),
|
||||
"reader write should return permission message");
|
||||
|
||||
ServiceResult<Long> created = service.createBook(librarian,
|
||||
book(0L, "BK-1002", "Service Test", "Test Author", 1L, 2, 1, BookStatus.AVAILABLE));
|
||||
require(created.isSuccessful(), "librarian should create a valid book");
|
||||
long createdId = created.getData();
|
||||
|
||||
ServiceResult<Long> duplicate = service.createBook(librarian,
|
||||
book(0L, "BK-1002", "Duplicate", "Test Author", 1L, 1, 1, BookStatus.AVAILABLE));
|
||||
require(!duplicate.isSuccessful(), "duplicate identifier should fail");
|
||||
require(duplicate.getErrors().containsKey("identifier"), "duplicate should target identifier field");
|
||||
|
||||
ServiceResult<Void> updated = service.updateBook(librarian,
|
||||
book(createdId, "BK-1003", "Service Test Updated", "Test Author", 1L, 3, 3, BookStatus.AVAILABLE));
|
||||
require(updated.isSuccessful(), "librarian should update a valid book");
|
||||
require(dao.findById(createdId).get().getAvailableCopies() == 3, "update should persist available copies");
|
||||
|
||||
ServiceResult<List<Book>> search = service.searchBooks(new BookSearchCriteria("BK-1003", "", "", null));
|
||||
require(search.isSuccessful(), "search should succeed");
|
||||
require(search.getData().size() == 1, "search should find updated identifier");
|
||||
|
||||
ServiceResult<Void> deleted = service.deleteBook(librarian, createdId);
|
||||
require(deleted.isSuccessful(), "librarian should delete a book");
|
||||
require(!dao.findById(createdId).isPresent(), "delete should remove the record");
|
||||
|
||||
BookService failingService = new BookServiceImpl(new FailingBookDao());
|
||||
ServiceResult<List<Book>> unavailable = failingService.searchBooks(new BookSearchCriteria());
|
||||
require(!unavailable.isSuccessful(), "DAO failure should not escape service");
|
||||
require(UNAVAILABLE_MESSAGE.equals(unavailable.getMessage()), "DAO failure should map to safe message");
|
||||
}
|
||||
|
||||
private static AuthenticatedUser user(long id, Role role) {
|
||||
return new AuthenticatedUser(id, role.getCode(), role.getDisplayName(), role,
|
||||
role == Role.READER
|
||||
? EnumSet.of(Permission.VIEW_CATALOG, Permission.BORROW_BOOKS)
|
||||
: EnumSet.of(Permission.MANAGE_BOOKS, Permission.VIEW_CATALOG));
|
||||
}
|
||||
|
||||
private static Book book(long id, String identifier, String title, String author, long categoryId,
|
||||
int totalCopies, int availableCopies, BookStatus status) {
|
||||
Book book = new Book();
|
||||
book.setId(id);
|
||||
book.setIdentifier(identifier);
|
||||
book.setTitle(title);
|
||||
book.setAuthor(author);
|
||||
book.setCategoryId(categoryId);
|
||||
book.setTotalCopies(totalCopies);
|
||||
book.setAvailableCopies(availableCopies);
|
||||
book.setStatus(status);
|
||||
return book;
|
||||
}
|
||||
|
||||
private static void require(boolean condition, String message) {
|
||||
if (!condition) {
|
||||
throw new AssertionError(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InMemoryBookDao implements BookDao {
|
||||
private final Map<Long, Book> books = new LinkedHashMap<>();
|
||||
private long nextId = 1L;
|
||||
|
||||
@Override
|
||||
public List<BookCategory> findAllCategories() {
|
||||
BookCategory category = new BookCategory();
|
||||
category.setId(1L);
|
||||
category.setName("Computer Science");
|
||||
return Collections.singletonList(category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Book> search(BookSearchCriteria criteria) {
|
||||
List<Book> matches = new ArrayList<>();
|
||||
for (Book book : books.values()) {
|
||||
if (matches(criteria.getIdentifier(), book.getIdentifier())
|
||||
&& matches(criteria.getTitle(), book.getTitle())
|
||||
&& matches(criteria.getAuthor(), book.getAuthor())
|
||||
&& (criteria.getCategoryId() == null || criteria.getCategoryId() == book.getCategoryId())) {
|
||||
matches.add(copy(book));
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Book> findById(long id) {
|
||||
return Optional.ofNullable(books.get(id)).map(this::copy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Book> findByIdentifier(String identifier) {
|
||||
for (Book book : books.values()) {
|
||||
if (book.getIdentifier().equals(identifier)) {
|
||||
return Optional.of(copy(book));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long create(Book book) {
|
||||
long id = nextId++;
|
||||
Book stored = copy(book);
|
||||
stored.setId(id);
|
||||
books.put(id, stored);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Book book) {
|
||||
if (!books.containsKey(book.getId())) {
|
||||
return false;
|
||||
}
|
||||
books.put(book.getId(), copy(book));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(long id) {
|
||||
return books.remove(id) != null;
|
||||
}
|
||||
|
||||
private boolean matches(String filter, String value) {
|
||||
return filter == null || filter.isEmpty() || value.contains(filter);
|
||||
}
|
||||
|
||||
private Book copy(Book source) {
|
||||
Book copy = book(source.getId(), source.getIdentifier(), source.getTitle(), source.getAuthor(),
|
||||
source.getCategoryId(), source.getTotalCopies(), source.getAvailableCopies(), source.getStatus());
|
||||
copy.setCategoryName(source.getCategoryName());
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FailingBookDao implements BookDao {
|
||||
@Override
|
||||
public List<BookCategory> findAllCategories() {
|
||||
throw new DaoException("Simulated category failure", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Book> search(BookSearchCriteria criteria) {
|
||||
throw new DaoException("Simulated search failure", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Book> findById(long id) {
|
||||
throw new DaoException("Simulated find failure", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Book> findByIdentifier(String identifier) {
|
||||
throw new DaoException("Simulated find failure", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long create(Book book) {
|
||||
throw new DaoException("Simulated create failure", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Book book) {
|
||||
throw new DaoException("Simulated update failure", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(long id) {
|
||||
throw new DaoException("Simulated delete failure", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user