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 = "读者服务暂时不可用,请稍后重试。"; private static final String VALIDATION_MESSAGE = "请修正高亮的读者字段。"; private static final String SEARCH_VALIDATION_MESSAGE = "请修正读者检索筛选条件。"; private static final String DENIED_MESSAGE = "您无权管理读者。"; 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> searchReaders(ReaderSearchCriteria criteria) { ReaderSearchCriteria normalized = criteria == null ? new ReaderSearchCriteria() : criteria; Map 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> findReader(long id) { if (id <= 0) { return ServiceResult.failure("请选择有效的读者。"); } 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 createReader(AuthenticatedUser actor, Reader reader) { if (!canManageReaders(actor)) { return ServiceResult.failure(DENIED_MESSAGE); } normalize(reader); Map errors = validate(reader, false); if (!errors.isEmpty()) { return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors); } try { if (readerDao.findByIdentifier(reader.getIdentifier()).isPresent()) { errors.put("identifier", "读者编号已被使用。"); return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors); } if (reader.getUserId() != null && readerDao.findByUserId(reader.getUserId()).isPresent()) { errors.put("userId", "关联账户已绑定到其他读者档案。"); return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors); } long id = readerDao.create(reader); LOGGER.info("Created reader id=" + id + " actorId=" + actor.getId()); return ServiceResult.success(id, "读者档案已创建。"); } catch (DaoException ex) { LOGGER.log(Level.SEVERE, "Unable to create reader actorId=" + actor.getId(), ex); return ServiceResult.failure(UNAVAILABLE_MESSAGE); } } @Override public ServiceResult updateReader(AuthenticatedUser actor, Reader reader) { if (!canManageReaders(actor)) { return ServiceResult.failure(DENIED_MESSAGE); } normalize(reader); Map errors = validate(reader, true); if (!errors.isEmpty()) { return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors); } try { Optional existingWithIdentifier = readerDao.findByIdentifier(reader.getIdentifier()); if (existingWithIdentifier.isPresent() && existingWithIdentifier.get().getId() != reader.getId()) { errors.put("identifier", "读者编号已被使用。"); return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors); } if (reader.getUserId() != null) { Optional existingWithUser = readerDao.findByUserId(reader.getUserId()); if (existingWithUser.isPresent() && existingWithUser.get().getId() != reader.getId()) { errors.put("userId", "关联账户已绑定到其他读者档案。"); return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors); } } if (!readerDao.update(reader)) { return ServiceResult.failure("未找到读者档案。"); } LOGGER.info("Updated reader id=" + reader.getId() + " actorId=" + actor.getId()); return ServiceResult.success(null, "读者档案已更新。"); } 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 deactivateReader(AuthenticatedUser actor, long id) { if (!canManageReaders(actor)) { return ServiceResult.failure(DENIED_MESSAGE); } if (id <= 0) { return ServiceResult.failure("请选择有效的读者。"); } try { if (!readerDao.deactivate(id)) { return ServiceResult.failure("未找到读者档案。"); } LOGGER.info("Deactivated reader id=" + id + " actorId=" + actor.getId()); return ServiceResult.success(null, "读者档案已停用。"); } 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 validateSearch(ReaderSearchCriteria criteria) { Map errors = new LinkedHashMap<>(); if (criteria.getStatusCode() != null && !criteria.getStatusCode().isEmpty()) { try { criteria.setStatusCode(ReaderStatus.fromCode(criteria.getStatusCode()).getCode()); } catch (IllegalArgumentException ex) { errors.put("status", "请选择有效的状态。"); } } return errors; } private Map validate(Reader reader, boolean requireId) { Map errors = new LinkedHashMap<>(); if (reader == null) { errors.put("reader", "请填写读者详情。"); return errors; } if (requireId && reader.getId() <= 0) { errors.put("id", "请选择有效的读者。"); } requireLength(errors, "identifier", reader.getIdentifier(), "读者编号", 64); requireLength(errors, "fullName", reader.getFullName(), "姓名", 100); if (reader.getUserId() != null && reader.getUserId() <= 0) { errors.put("userId", "关联账户 ID 必须为正数。"); } validateContact(errors, reader); if (reader.getStatus() == null) { errors.put("status", "请选择状态。"); } if (reader.getMaxBorrowCount() < 1 || reader.getMaxBorrowCount() > MAX_BORROW_LIMIT) { errors.put("maxBorrowCount", "最大借阅数量必须在 1 到 " + MAX_BORROW_LIMIT + " 之间。"); } return errors; } private void validateContact(Map errors, Reader reader) { String phone = reader.getPhone(); String email = reader.getEmail(); if ((phone == null || phone.isEmpty()) && (email == null || email.isEmpty())) { errors.put("phone", "请填写电话或邮箱。"); return; } if (phone != null && !phone.isEmpty() && !PHONE_PATTERN.matcher(phone).matches()) { errors.put("phone", "电话必须包含数字,并使用 6 到 32 位数字或常见电话符号。"); } if (email != null && !email.isEmpty() && !EMAIL_PATTERN.matcher(email).matches()) { errors.put("email", "邮箱格式不正确。"); } } private void requireLength(Map errors, String field, String value, String label, int maxLength) { if (value == null || value.isEmpty()) { errors.put(field, "请填写" + label + "。"); return; } if (value.length() > maxLength) { errors.put(field, label + "不能超过 " + maxLength + " 个字符。"); } } private String trim(String value) { return value == null ? "" : value.trim(); } }