Skip to content

Commit

Permalink
Feat: 시니또 탈퇴시 본인이 할당받은 서비스가 있으면 다시 대기상태로 전환되는 로직 추가, SET_NULL 설정 (#181)
Browse files Browse the repository at this point in the history
* feat: 콜백 서비스층에 탈퇴시 들어가는 로직 구현

* feat: 안부전화 서비스층에 탈퇴시 들어가는 로직 구현

* feat: 멤버 탈퇴 로직에 콜백, 안부전화 진행중인거 확인하고 진행중인거 있으면 대기상태로 전환하는 로직 추가

* fix: 안부전화는 시니어만 다르면 동시에 여러개 가능하다

* fix: CallbackRepository 잘못된 메서드 제거 후 의존하는 코드 수정

* feat: HelloCall 엔티티에 `@OnDelete(action = OnDeleteAction.SET_NULL)`

* feat: 개별 트랜잭션으로 분리

* feat: HelloCallTimeLog의 멤버속성에 `    @onDelete(action = OnDeleteAction.SET_NULL)
` 추가, 시니또이름 분기 추가

* fix: 개별 트랜잭션 적용. 기존에는 개별이 아니라 하나의 트랜잭션에 포함되어 있었다.
  • Loading branch information
zzoe2346 authored Nov 7, 2024
1 parent 40f045c commit 9295e8b
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
Expand Down Expand Up @@ -110,7 +111,7 @@ public void changeOldPendingCompleteToCompleteByPolicy() {
}
}

@Transactional
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void completeCallbackIndividually(Callback callback) {

pointService.earnPoint(callback.getAssignedMemberId(), CALLBACK_PRICE, PointLog.Content.COMPLETE_CALLBACK_AND_EARN);
Expand Down Expand Up @@ -219,4 +220,16 @@ public CallbackForSinittoResponse getCallbackForSinitto(Long memberId, Long call
return new CallbackForSinittoResponse(callback.getId(), callback.getSeniorName(), callback.getPostTime(), callback.getStatus(), callback.getSeniorId(), false, "");
}

@Transactional
public void cancelAssignedCallbackIfInProgress(Member member) {

Callback callback = callbackRepository.findByAssignedMemberIdAndStatus(member.getId(), Callback.Status.IN_PROGRESS)
.orElse(null);

if (callback != null) {
callback.cancelAssignment();
callback.changeStatusToWaiting();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class HelloCall {
private List<TimeSlot> timeSlots = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "member_id")
@OnDelete(action = OnDeleteAction.SET_NULL)
private Member member;
@OneToMany(mappedBy = "helloCall")
private List<HelloCallTimeLog> helloCallTimeLogs = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class HelloCallTimeLog {
@JoinColumn(name = "helloCall_id")
private HelloCall helloCall;
@ManyToOne
@OnDelete(action = OnDeleteAction.CASCADE)
@OnDelete(action = OnDeleteAction.SET_NULL)
@JoinColumn(name = "sinitto_id")
private Member member;

Expand Down Expand Up @@ -56,6 +56,9 @@ public void setEndDateAndTime(LocalDateTime endDateAndTime) {
}

public String getSinittoName() {
if (member == null) {
return "탈퇴한 시니또";
}
return this.member.getName();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ public interface HelloCallRepository extends JpaRepository<HelloCall, Long> {
List<HelloCall> findAllBySeniorIn(List<Senior> seniors);

boolean existsBySeniorAndStatusIn(Senior senior, List<HelloCall.Status> statuses);

List<HelloCall> findByMemberAndStatus(Member member, HelloCall.Status status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
Expand Down Expand Up @@ -320,4 +321,20 @@ public List<HelloCallResponse> readOwnHelloCallBySinitto(Long memberId) {
return helloCallResponses;
}

@Transactional
public void cancelAssignedHelloCallIfInProgress(Member member) {

List<HelloCall> helloCalls = helloCallRepository.findByMemberAndStatus(member, HelloCall.Status.IN_PROGRESS);

changeHelloCall(helloCalls);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void changeHelloCall(List<HelloCall> helloCalls) {
for (HelloCall helloCall : helloCalls) {
helloCall.changeStatusToWaiting();
helloCall.setMember(null);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import com.example.sinitto.auth.service.KakaoApiService;
import com.example.sinitto.auth.service.KakaoTokenService;
import com.example.sinitto.auth.service.TokenService;
import com.example.sinitto.callback.service.CallbackService;
import com.example.sinitto.common.exception.ConflictException;
import com.example.sinitto.common.exception.NotFoundException;
import com.example.sinitto.common.resolver.MemberIdProvider;
import com.example.sinitto.helloCall.service.HelloCallService;
import com.example.sinitto.member.dto.RegisterResponse;
import com.example.sinitto.member.entity.Member;
import com.example.sinitto.member.repository.MemberRepository;
Expand All @@ -29,14 +31,18 @@ public class MemberService implements MemberIdProvider {
private final KakaoTokenService kakaoTokenService;
private final PointRepository pointRepository;
private final RedisTemplate<String, Object> redisTemplate;
private final CallbackService callbackService;
private final HelloCallService helloCallService;

public MemberService(MemberRepository memberRepository, TokenService tokenService, KakaoApiService kakaoApiService, KakaoTokenService kakaoTokenService, PointRepository pointRepository, RedisTemplate<String, Object> redisTemplate) {
public MemberService(MemberRepository memberRepository, TokenService tokenService, KakaoApiService kakaoApiService, KakaoTokenService kakaoTokenService, PointRepository pointRepository, RedisTemplate<String, Object> redisTemplate, CallbackService callbackService, HelloCallService helloCallService) {
this.memberRepository = memberRepository;
this.tokenService = tokenService;
this.kakaoApiService = kakaoApiService;
this.kakaoTokenService = kakaoTokenService;
this.pointRepository = pointRepository;
this.redisTemplate = redisTemplate;
this.callbackService = callbackService;
this.helloCallService = helloCallService;
}

@Override
Expand Down Expand Up @@ -101,7 +107,7 @@ public void memberLogout(Long memberId) {
}
}

public void deleteMember(Long memberId){
public void deleteMember(Long memberId) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다."));

Expand All @@ -111,6 +117,12 @@ public void deleteMember(Long memberId){
redisTemplate.delete(member.getEmail());
}

if (member.isSinitto()) {
callbackService.cancelAssignedCallbackIfInProgress(member);
helloCallService.cancelAssignedHelloCallIfInProgress(member);
}

memberRepository.deleteById(memberId);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -529,4 +529,43 @@ void getCallbackForSinitto4() {
assertEquals("", result.seniorPhoneNumber());
}
}

@Nested
@DisplayName("시니또가 탈퇴시 진행중인 콜백이 있으면 취소시키는 로직 테스트")
class CancelAssignedCallbackIfInProgress {

@Test
@DisplayName("진행중인 콜백이 있으면, 콜백을 대기상태로 전환시켜야한다.")
void cancelAssignedCallbackIfInProgress1() {
//given
Member member = mock(Member.class);
Callback callback = mock(Callback.class);

when(callbackRepository.findByAssignedMemberIdAndStatus(anyLong(), any(Callback.Status.class))).thenReturn(Optional.of(callback));

//when
callbackService.cancelAssignedCallbackIfInProgress(member);

//then
verify(callback, atLeastOnce()).cancelAssignment();
verify(callback, atLeastOnce()).changeStatusToWaiting();
}

@Test
@DisplayName("진행중인 콜백이 없으면, 아무것도 하지 않아야한다.")
void cancelAssignedCallbackIfInProgress2() {
//given
Member member = mock(Member.class);
Callback callback = mock(Callback.class);

when(callbackRepository.findByAssignedMemberIdAndStatus(anyLong(), any(Callback.Status.class))).thenReturn(Optional.empty());

//when
callbackService.cancelAssignedCallbackIfInProgress(member);

//then
verify(callback, never()).cancelAssignment();
verify(callback, never()).changeStatusToWaiting();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -864,4 +864,39 @@ void readOwnHelloCallBySinittoTestWhenMemberIsNotExist() {
//when, then
assertThrows(NotFoundException.class, () -> helloCallService.readOwnHelloCallBySinitto(memberId));
}

@Test
@DisplayName("시니또 탈퇴시 할당받은 안부전화가 있으면 안부전화 엔티티를 대기상태로 바꾸고, 멤버도 null 로 교체한다.")
void cancelAssignedHelloCallIfInProgressTest1() {
// given
Member member = mock(Member.class);
HelloCall helloCall = mock(HelloCall.class);

when(helloCallRepository.findByMemberAndStatus(member, HelloCall.Status.IN_PROGRESS)).thenReturn(List.of(helloCall));

// when
helloCallService.cancelAssignedHelloCallIfInProgress(member);

// then
verify(helloCall, times(1)).changeStatusToWaiting();
verify(helloCall, times(1)).setMember(null);
}

@Test
@DisplayName("시니또 탈퇴시 할당받은 안부전화가 없으면 아무것도 하지 않는다.")
void cancelAssignedHelloCallIfInProgressTest2() {
// given
Member member = mock(Member.class);
HelloCall helloCall = mock(HelloCall.class);

when(helloCallRepository.findByMemberAndStatus(member, HelloCall.Status.IN_PROGRESS)).thenReturn(List.of());

// when
helloCallService.cancelAssignedHelloCallIfInProgress(member);

// then
verify(helloCall, never()).changeStatusToWaiting();
verify(helloCall, never()).setMember(null);
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.example.sinitto.member.service;

import com.example.sinitto.auth.service.TokenService;
import com.example.sinitto.callback.service.CallbackService;
import com.example.sinitto.common.exception.ConflictException;
import com.example.sinitto.common.exception.NotFoundException;
import com.example.sinitto.helloCall.service.HelloCallService;
import com.example.sinitto.member.dto.RegisterResponse;
import com.example.sinitto.member.entity.Member;
import com.example.sinitto.member.repository.MemberRepository;
Expand Down Expand Up @@ -34,11 +36,16 @@ public class MemberServiceTest {
@Mock
RedisTemplate<String, Object> redisTemplate;
@Mock
private HashOperations<String, Object, Object> hashOperations;
HashOperations<String, Object, Object> hashOperations;
@InjectMocks
MemberService memberService;
@Mock
private ValueOperations<String, String> valueOperations;
ValueOperations<String, String> valueOperations;
@Mock
CallbackService callbackService;
@Mock
HelloCallService helloCallService;


@Test
@DisplayName("getMemberIdByToken 메소드 테스트")
Expand Down Expand Up @@ -143,7 +150,52 @@ void memberLogoutTestWhenMemberIsNull() {

@Test
@DisplayName("deleteMember 메소드 테스트")
void deleteMemberTest(){
void deleteMemberTest() {
//given
Long memberId = 1L;
String email = "[email protected]";
Member member = mock(Member.class);
String storedRefreshToken = "testRefreshToken";

when(memberRepository.findById(memberId)).thenReturn(Optional.of(member));
when(member.getEmail()).thenReturn(email);
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
when(redisTemplate.opsForHash().get(member.getEmail(), "refreshToken")).thenReturn(storedRefreshToken);

//when
memberService.deleteMember(memberId);

//when
verify(memberRepository, times(1)).deleteById(memberId);
}

@Test
@DisplayName("deleteMember 메소드 테스트 - 탈퇴 신청한게 시니또인 경우")
void deleteMemberTest2() {
//given
Long memberId = 1L;
String email = "[email protected]";
Member member = mock(Member.class);
String storedRefreshToken = "testRefreshToken";

when(memberRepository.findById(memberId)).thenReturn(Optional.of(member));
when(member.getEmail()).thenReturn(email);
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
when(redisTemplate.opsForHash().get(member.getEmail(), "refreshToken")).thenReturn(storedRefreshToken);

when(member.isSinitto()).thenReturn(true);
//when
memberService.deleteMember(memberId);

//when
verify(memberRepository, times(1)).deleteById(memberId);
verify(callbackService, times(1)).cancelAssignedCallbackIfInProgress(any(Member.class));
verify(helloCallService, times(1)).cancelAssignedHelloCallIfInProgress(any(Member.class));
}

@Test
@DisplayName("deleteMember 메소드 테스트 - 탈퇴 신청한게 보호자인 경우")
void deleteMemberTest3() {
//given
Long memberId = 1L;
String email = "[email protected]";
Expand All @@ -155,10 +207,13 @@ void deleteMemberTest(){
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
when(redisTemplate.opsForHash().get(member.getEmail(), "refreshToken")).thenReturn(storedRefreshToken);

when(member.isSinitto()).thenReturn(false);
//when
memberService.deleteMember(memberId);

//when
verify(memberRepository, times(1)).deleteById(memberId);
verify(callbackService, times(0)).cancelAssignedCallbackIfInProgress(any(Member.class));
verify(helloCallService, times(0)).cancelAssignedHelloCallIfInProgress(any(Member.class));
}
}

0 comments on commit 9295e8b

Please sign in to comment.