HouseholdInvitationService.java
package com.hwhub.backend.application.service;
import com.hwhub.backend.application.service.notification.NotificationPublisher;
import com.hwhub.backend.domain.enums.HouseholdMemberStatus;
import com.hwhub.backend.domain.enums.InvitationStatus;
import com.hwhub.backend.domain.enums.ProgramType;
import com.hwhub.backend.domain.model.HouseholdInvitationModel;
import com.hwhub.backend.domain.model.HouseholdMemberModel;
import com.hwhub.backend.domain.model.UserModel;
import com.hwhub.backend.domain.repository.HouseholdInvitationRepository;
import com.hwhub.backend.domain.repository.HouseholdMemberRepository;
import com.hwhub.backend.domain.repository.UserRepository;
import com.hwhub.backend.presentation.rest.common.ResourceNotFoundException;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class HouseholdInvitationService {
private final UserRepository userRepository;
private final HouseholdInvitationRepository invRepository;
private final HouseholdMemberRepository memberRepository;
private final HouseholdMemberService memberService;
private final HouseholdAuthorizationService authorizationService;
private final NotificationPublisher notificationPublisher;
@Transactional(readOnly = true)
public HouseholdInvitationModel getInvitation(String token) {
HouseholdInvitationModel model = invRepository.selectByToken(token);
if (Objects.isNull(model)) {
throw new ResourceNotFoundException("invitation.notFound");
}
return model;
}
@Transactional(readOnly = true)
public List<HouseholdInvitationModel> getInvitations(Long householdId, Long userId) {
// 認可チェック
if (!authorizationService.canAccessHousehold(householdId, userId)) {
throw new AccessDeniedException(
"User does not belong to household: userId=" + userId + ", householdId=" + householdId);
}
return invRepository.selectByHouseholdId(householdId);
}
@Transactional
public void acceptInvitation(String token, Long userId) {
HouseholdInvitationModel inv = invRepository.selectByToken(token);
if (Objects.isNull(inv)) {
throw new ResourceNotFoundException("invitation.notFound");
}
if (inv.isTerminal()) {
throw new ResourceNotFoundException("invitation.alreadyHandled");
}
if (inv.isExpired()) {
inv.setStatus(InvitationStatus.EXPIRED.getCode());
invRepository.update(inv, userId, ProgramType.ONL_HLDINVI.getCode());
throw new ResourceNotFoundException("invitation.expired");
}
UserModel user = userRepository.findById(userId).orElse(null);
if (Objects.isNull(user)) {
throw new ResourceNotFoundException("user.notFound");
}
// メンバー追加
HouseholdMemberModel member = memberRepository.findById(inv.getHouseholdId(), userId);
if (member == null
|| !Objects.equals(member.getStatus(), HouseholdMemberStatus.ACTIVE.getCode())) {
memberService.createMember(inv.getHouseholdId(), userId, user.getDisplayName(), userId);
} else {
// 既にActiveで存在するため
throw new IllegalStateException("already registered");
}
// Invitation更新
inv.setStatus(InvitationStatus.ACCEPTED.getCode());
inv.setAcceptedUserId(userId);
invRepository.update(inv, userId, ProgramType.ONL_HLDINVI.getCode());
// 通知
notificationPublisher.publishAcceptInvitation(
inv.getHouseholdId(), userId, inv.getInviterUserId(), ProgramType.ONL_HLDINVI.getCode());
}
@Transactional
public void declineInvitation(String token, Long userId) {
HouseholdInvitationModel inv = invRepository.selectByToken(token);
if (Objects.isNull(inv)) {
throw new ResourceNotFoundException("invitation.notFound");
}
if (inv.isTerminal()) {
// 既に対応済みの場合何もしない
return;
}
if (inv.isExpired()) {
inv.setStatus(InvitationStatus.EXPIRED.getCode());
} else {
inv.setStatus(InvitationStatus.DECLINED.getCode());
}
inv.setAcceptedUserId(userId);
invRepository.update(inv, userId, ProgramType.ONL_HLDINVI.getCode());
// 通知
notificationPublisher.publishDeclineInvitation(
inv.getHouseholdId(), userId, inv.getInviterUserId(), ProgramType.ONL_HLDINVI.getCode());
}
@Transactional
public void revokeInvitation(String token, Long userId) {
HouseholdInvitationModel inv = invRepository.selectByToken(token);
if (Objects.isNull(inv)) {
throw new ResourceNotFoundException("invitation.notFound");
}
if (inv.isTerminal()) {
// 既に対応済みの場合何もしない
return;
}
inv.setStatus(InvitationStatus.REVOKED.getCode());
invRepository.update(inv, userId, ProgramType.ONL_HLDINVI.getCode());
}
@Transactional
public HouseholdInvitationModel createInvitation(Long householdId, String email, Long userId) {
// 認可チェック
if (!authorizationService.canAccessHousehold(householdId, userId)) {
throw new AccessDeniedException(
"User does not belong to household: userId=" + userId + ", householdId=" + householdId);
}
// 既存のinvitation取得しかぶっていないか
long count = invRepository.countByActiveEmail(householdId, email);
if (count > 0) {
throw new IllegalStateException("invitation.alreadyExists");
}
HouseholdInvitationModel inv = HouseholdInvitationModel.create(householdId, userId, email);
return invRepository.insert(inv, userId, ProgramType.ONL_HLDINVI.getCode());
}
}