做读者档案、联系方式、借阅资格功能
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
package com.mzh.library.service.impl;
|
||||
|
||||
import com.mzh.library.dao.ReaderDao;
|
||||
import com.mzh.library.entity.AuthenticatedUser;
|
||||
import com.mzh.library.entity.Permission;
|
||||
import com.mzh.library.entity.Reader;
|
||||
import com.mzh.library.entity.ReaderSearchCriteria;
|
||||
import com.mzh.library.entity.ReaderStatus;
|
||||
import com.mzh.library.exception.DaoException;
|
||||
import com.mzh.library.service.PermissionPolicy;
|
||||
import com.mzh.library.service.ReaderService;
|
||||
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.regex.Pattern;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ReaderServiceImpl implements ReaderService {
|
||||
private static final Logger LOGGER = Logger.getLogger(ReaderServiceImpl.class.getName());
|
||||
private static final String UNAVAILABLE_MESSAGE =
|
||||
"Reader service is temporarily unavailable. Please try again later.";
|
||||
private static final String VALIDATION_MESSAGE = "Please correct the highlighted reader fields.";
|
||||
private static final String SEARCH_VALIDATION_MESSAGE = "Please correct the reader search filters.";
|
||||
private static final String DENIED_MESSAGE = "You do not have permission to manage readers.";
|
||||
private static final int MAX_BORROW_LIMIT = 50;
|
||||
private static final Pattern PHONE_PATTERN = Pattern.compile("(?=.*\\d)[0-9+()\\-\\s]{6,32}");
|
||||
private static final Pattern EMAIL_PATTERN = Pattern.compile("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$");
|
||||
|
||||
private final ReaderDao readerDao;
|
||||
private final PermissionPolicy permissionPolicy;
|
||||
|
||||
public ReaderServiceImpl(ReaderDao readerDao) {
|
||||
this(readerDao, new PermissionPolicy());
|
||||
}
|
||||
|
||||
public ReaderServiceImpl(ReaderDao readerDao, PermissionPolicy permissionPolicy) {
|
||||
this.readerDao = readerDao;
|
||||
this.permissionPolicy = permissionPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<List<Reader>> searchReaders(ReaderSearchCriteria criteria) {
|
||||
ReaderSearchCriteria normalized = criteria == null ? new ReaderSearchCriteria() : criteria;
|
||||
Map<String, String> errors = validateSearch(normalized);
|
||||
if (!errors.isEmpty()) {
|
||||
return ServiceResult.validationFailure(SEARCH_VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
try {
|
||||
return ServiceResult.success(readerDao.search(normalized));
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to search readers", ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Optional<Reader>> findReader(long id) {
|
||||
if (id <= 0) {
|
||||
return ServiceResult.failure("Select a valid reader.");
|
||||
}
|
||||
|
||||
try {
|
||||
return ServiceResult.success(readerDao.findById(id));
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to load reader id=" + id, ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Long> createReader(AuthenticatedUser actor, Reader reader) {
|
||||
if (!canManageReaders(actor)) {
|
||||
return ServiceResult.failure(DENIED_MESSAGE);
|
||||
}
|
||||
|
||||
normalize(reader);
|
||||
Map<String, String> errors = validate(reader, false);
|
||||
if (!errors.isEmpty()) {
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
try {
|
||||
if (readerDao.findByIdentifier(reader.getIdentifier()).isPresent()) {
|
||||
errors.put("identifier", "Reader identifier is already in use.");
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
if (reader.getUserId() != null && readerDao.findByUserId(reader.getUserId()).isPresent()) {
|
||||
errors.put("userId", "Linked account is already assigned to a reader profile.");
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
long id = readerDao.create(reader);
|
||||
LOGGER.info("Created reader id=" + id + " actorId=" + actor.getId());
|
||||
return ServiceResult.success(id, "Reader profile created.");
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to create reader actorId=" + actor.getId(), ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Void> updateReader(AuthenticatedUser actor, Reader reader) {
|
||||
if (!canManageReaders(actor)) {
|
||||
return ServiceResult.failure(DENIED_MESSAGE);
|
||||
}
|
||||
|
||||
normalize(reader);
|
||||
Map<String, String> errors = validate(reader, true);
|
||||
if (!errors.isEmpty()) {
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
|
||||
try {
|
||||
Optional<Reader> existingWithIdentifier = readerDao.findByIdentifier(reader.getIdentifier());
|
||||
if (existingWithIdentifier.isPresent() && existingWithIdentifier.get().getId() != reader.getId()) {
|
||||
errors.put("identifier", "Reader identifier is already in use.");
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
if (reader.getUserId() != null) {
|
||||
Optional<Reader> existingWithUser = readerDao.findByUserId(reader.getUserId());
|
||||
if (existingWithUser.isPresent() && existingWithUser.get().getId() != reader.getId()) {
|
||||
errors.put("userId", "Linked account is already assigned to a reader profile.");
|
||||
return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (!readerDao.update(reader)) {
|
||||
return ServiceResult.failure("Reader profile was not found.");
|
||||
}
|
||||
|
||||
LOGGER.info("Updated reader id=" + reader.getId() + " actorId=" + actor.getId());
|
||||
return ServiceResult.success(null, "Reader profile updated.");
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to update reader id=" + reader.getId() + " actorId=" + actor.getId(), ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceResult<Void> deactivateReader(AuthenticatedUser actor, long id) {
|
||||
if (!canManageReaders(actor)) {
|
||||
return ServiceResult.failure(DENIED_MESSAGE);
|
||||
}
|
||||
if (id <= 0) {
|
||||
return ServiceResult.failure("Select a valid reader.");
|
||||
}
|
||||
|
||||
try {
|
||||
if (!readerDao.deactivate(id)) {
|
||||
return ServiceResult.failure("Reader profile was not found.");
|
||||
}
|
||||
|
||||
LOGGER.info("Deactivated reader id=" + id + " actorId=" + actor.getId());
|
||||
return ServiceResult.success(null, "Reader profile deactivated.");
|
||||
} catch (DaoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to deactivate reader id=" + id + " actorId=" + actor.getId(), ex);
|
||||
return ServiceResult.failure(UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canManageReaders(AuthenticatedUser actor) {
|
||||
return actor != null && permissionPolicy.allows(actor.getRole(), Permission.MANAGE_READERS);
|
||||
}
|
||||
|
||||
private void normalize(Reader reader) {
|
||||
if (reader == null) {
|
||||
return;
|
||||
}
|
||||
reader.setIdentifier(trim(reader.getIdentifier()));
|
||||
reader.setFullName(trim(reader.getFullName()));
|
||||
reader.setPhone(trim(reader.getPhone()));
|
||||
reader.setEmail(trim(reader.getEmail()));
|
||||
}
|
||||
|
||||
private Map<String, String> validateSearch(ReaderSearchCriteria criteria) {
|
||||
Map<String, String> errors = new LinkedHashMap<>();
|
||||
if (criteria.getStatusCode() != null && !criteria.getStatusCode().isEmpty()) {
|
||||
try {
|
||||
criteria.setStatusCode(ReaderStatus.fromCode(criteria.getStatusCode()).getCode());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
errors.put("status", "Select a valid status.");
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
private Map<String, String> validate(Reader reader, boolean requireId) {
|
||||
Map<String, String> errors = new LinkedHashMap<>();
|
||||
if (reader == null) {
|
||||
errors.put("reader", "Reader details are required.");
|
||||
return errors;
|
||||
}
|
||||
|
||||
if (requireId && reader.getId() <= 0) {
|
||||
errors.put("id", "Select a valid reader.");
|
||||
}
|
||||
requireLength(errors, "identifier", reader.getIdentifier(), "Reader identifier", 64);
|
||||
requireLength(errors, "fullName", reader.getFullName(), "Full name", 100);
|
||||
if (reader.getUserId() != null && reader.getUserId() <= 0) {
|
||||
errors.put("userId", "Linked account ID must be positive.");
|
||||
}
|
||||
validateContact(errors, reader);
|
||||
if (reader.getStatus() == null) {
|
||||
errors.put("status", "Select a status.");
|
||||
}
|
||||
if (reader.getMaxBorrowCount() < 1 || reader.getMaxBorrowCount() > MAX_BORROW_LIMIT) {
|
||||
errors.put("maxBorrowCount", "Max borrow count must be between 1 and " + MAX_BORROW_LIMIT + ".");
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
private void validateContact(Map<String, String> errors, Reader reader) {
|
||||
String phone = reader.getPhone();
|
||||
String email = reader.getEmail();
|
||||
if ((phone == null || phone.isEmpty()) && (email == null || email.isEmpty())) {
|
||||
errors.put("phone", "Phone or email is required.");
|
||||
return;
|
||||
}
|
||||
if (phone != null && !phone.isEmpty() && !PHONE_PATTERN.matcher(phone).matches()) {
|
||||
errors.put("phone", "Phone must include a digit and use 6 to 32 digits or common phone symbols.");
|
||||
}
|
||||
if (email != null && !email.isEmpty() && !EMAIL_PATTERN.matcher(email).matches()) {
|
||||
errors.put("email", "Email must be a valid address.");
|
||||
}
|
||||
}
|
||||
|
||||
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