From 14b730c9730a7bfba249020fc54e434209245a32 Mon Sep 17 00:00:00 2001 From: kunsanglee Date: Sat, 20 Jul 2024 15:16:27 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20MemberActionFactory=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MemberActionFactory.java | 46 +++--- .../application/MemberActionService.java | 19 ++- .../java/server/haengdong/domain/Action.java | 10 ++ .../server/haengdong/domain/MemberAction.java | 8 +- .../application/MemberActionFactoryTest.java | 147 ++++++++++++++++++ .../application/MemberActionServiceTest.java | 10 +- 6 files changed, 204 insertions(+), 36 deletions(-) rename server/src/main/java/server/haengdong/{domain => application}/MemberActionFactory.java (53%) create mode 100644 server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java diff --git a/server/src/main/java/server/haengdong/domain/MemberActionFactory.java b/server/src/main/java/server/haengdong/application/MemberActionFactory.java similarity index 53% rename from server/src/main/java/server/haengdong/domain/MemberActionFactory.java rename to server/src/main/java/server/haengdong/application/MemberActionFactory.java index d9e921d01..85b3cbac3 100644 --- a/server/src/main/java/server/haengdong/domain/MemberActionFactory.java +++ b/server/src/main/java/server/haengdong/application/MemberActionFactory.java @@ -1,65 +1,63 @@ -package server.haengdong.domain; +package server.haengdong.application; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import server.haengdong.application.request.MemberActionSaveAppRequest; -import server.haengdong.application.request.MemberActionSaveListAppRequest; -import server.haengdong.persistence.ActionRepository; +import server.haengdong.application.request.MemberActionsSaveAppRequest; +import server.haengdong.domain.Action; +import server.haengdong.domain.MemberAction; +import server.haengdong.domain.MemberActionStatus; +import server.haengdong.domain.MemberGroupIdProvider; @RequiredArgsConstructor @Component public class MemberActionFactory { - private final ActionRepository actionRepository; private final MemberGroupIdProvider memberGroupIdProvider; public List createMemberActions( - MemberActionSaveListAppRequest request, + MemberActionsSaveAppRequest request, List memberActions, - Event event + Action action ) { + memberActions.sort(Comparator.comparing(MemberAction::getSequence)); validateActions(request, memberActions); + Long memberGroupId = memberGroupIdProvider.createGroupId(); - long lastSequence = getLastSequence(event); - List memberActionList = new ArrayList<>(); + List createdMemberActions = new ArrayList<>(); List actions = request.actions(); for (MemberActionSaveAppRequest appRequest : actions) { - Action action = new Action(event, ++lastSequence); MemberAction memberAction = appRequest.toMemberAction(action, memberGroupId); - memberActionList.add(memberAction); + createdMemberActions.add(memberAction); + action = action.next(); } - return memberActionList; + + return createdMemberActions; } - private void validateActions(MemberActionSaveListAppRequest request, List memberActions) { + private void validateActions(MemberActionsSaveAppRequest request, List memberActions) { for (MemberActionSaveAppRequest action : request.actions()) { validateAction(memberActions, action); } } private void validateAction(List memberActions, MemberActionSaveAppRequest action) { - if (!isAvailableAction(memberActions, action.name(), action.status())) { + MemberActionStatus memberActionStatus = MemberActionStatus.of(action.status()); + if (isInvalidStatus(memberActions, action.name(), memberActionStatus)) { throw new IllegalArgumentException(); } } - private long getLastSequence(Event event) { - return actionRepository.findLastByEvent(event) - .map(Action::getSequence) - .orElse(0L); - } - - private boolean isAvailableAction(List actions, String name, String status) { - MemberActionStatus memberActionStatus = MemberActionStatus.of(status); + private boolean isInvalidStatus(List actions, String name, MemberActionStatus status) { for (int i = actions.size() - 1; i >= 0; i--) { MemberAction action = actions.get(i); if (action.isSameName(name)) { - return action.isAvailable(memberActionStatus); + return action.isSameStatus(status); } } - - return MemberActionStatus.isMemberStatusIn(memberActionStatus); + return MemberActionStatus.IN != status; } } diff --git a/server/src/main/java/server/haengdong/application/MemberActionService.java b/server/src/main/java/server/haengdong/application/MemberActionService.java index 5e0b607c7..491981b06 100644 --- a/server/src/main/java/server/haengdong/application/MemberActionService.java +++ b/server/src/main/java/server/haengdong/application/MemberActionService.java @@ -4,10 +4,11 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import server.haengdong.application.request.MemberActionSaveListAppRequest; +import server.haengdong.application.request.MemberActionsSaveAppRequest; +import server.haengdong.domain.Action; import server.haengdong.domain.Event; import server.haengdong.domain.MemberAction; -import server.haengdong.domain.MemberActionFactory; +import server.haengdong.persistence.ActionRepository; import server.haengdong.persistence.EventRepository; import server.haengdong.persistence.MemberActionRepository; @@ -16,17 +17,25 @@ @Service public class MemberActionService { + private final MemberActionFactory memberActionFactory; private final MemberActionRepository memberActionRepository; private final EventRepository eventRepository; - private final MemberActionFactory memberActionFactory; + private final ActionRepository actionRepository; @Transactional - public void saveMemberAction(String token, MemberActionSaveListAppRequest request) { + public void saveMemberAction(String token, MemberActionsSaveAppRequest request) { Event event = eventRepository.findByToken(token) .orElseThrow(() -> new IllegalArgumentException("event not found")); List findMemberActions = memberActionRepository.findAllByEvent(event); - List memberActions = memberActionFactory.createMemberActions(request, findMemberActions, event); + Action action = createStartAction(event); + List memberActions = memberActionFactory.createMemberActions(request, findMemberActions, action); memberActionRepository.saveAll(memberActions); } + + private Action createStartAction(Event event) { + return actionRepository.findLastByEvent(event) + .map(Action::next) + .orElse(Action.createFirst(event)); + } } diff --git a/server/src/main/java/server/haengdong/domain/Action.java b/server/src/main/java/server/haengdong/domain/Action.java index 7a47b1de5..be294dac3 100644 --- a/server/src/main/java/server/haengdong/domain/Action.java +++ b/server/src/main/java/server/haengdong/domain/Action.java @@ -15,6 +15,8 @@ @Entity public class Action { + private static final long FIRST_SEQUENCE = 1L; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -28,4 +30,12 @@ public Action(Event event, Long sequence) { this.event = event; this.sequence = sequence; } + + public static Action createFirst(Event event) { + return new Action(event, FIRST_SEQUENCE); + } + + public Action next() { + return new Action(event, sequence + 1); + } } diff --git a/server/src/main/java/server/haengdong/domain/MemberAction.java b/server/src/main/java/server/haengdong/domain/MemberAction.java index b7076bd8c..eb8d8fdec 100644 --- a/server/src/main/java/server/haengdong/domain/MemberAction.java +++ b/server/src/main/java/server/haengdong/domain/MemberAction.java @@ -43,7 +43,11 @@ public boolean isSameName(String name) { return memberName.equals(name); } - public boolean isAvailable(MemberActionStatus memberActionStatus) { - return status.isOpposite(memberActionStatus); + public boolean isSameStatus(MemberActionStatus memberActionStatus) { + return status == memberActionStatus; + } + + public Long getSequence() { + return action.getSequence(); } } diff --git a/server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java b/server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java new file mode 100644 index 000000000..764caee91 --- /dev/null +++ b/server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java @@ -0,0 +1,147 @@ +package server.haengdong.application; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import server.haengdong.application.request.MemberActionSaveAppRequest; +import server.haengdong.application.request.MemberActionsSaveAppRequest; +import server.haengdong.domain.Action; +import server.haengdong.domain.Event; +import server.haengdong.domain.MemberAction; +import server.haengdong.domain.MemberActionStatus; +import server.haengdong.persistence.ActionRepository; +import server.haengdong.persistence.EventRepository; +import server.haengdong.persistence.MemberActionRepository; + +@SpringBootTest +class MemberActionFactoryTest { + + @Autowired + private MemberActionFactory memberActionFactory; + + @Autowired + private MemberActionRepository memberActionRepository; + + @Autowired + private ActionRepository actionRepository; + + @Autowired + private EventRepository eventRepository; + + @AfterEach + void tearDown() { + memberActionRepository.deleteAllInBatch(); + actionRepository.deleteAllInBatch(); + eventRepository.deleteAllInBatch(); + } + + @DisplayName("액션 ID를 기준으로 정렬한 상태로 새로운 멤버 액션 요청을 검증한다.") + @Test + void createMemberActionsTest() { + Event event = eventRepository.save(new Event("우당탕탕 행동대장 백엔드 회식", "토다리_토큰")); + Action action1 = new Action(event, 1L); + Action action2 = new Action(event, 2L); + MemberAction memberAction1 = new MemberAction(action1, "토다리", MemberActionStatus.IN, 1L); + MemberAction memberAction2 = new MemberAction(action2, "토다리", MemberActionStatus.OUT, 2L); + memberActionRepository.saveAll(List.of(memberAction1, memberAction2)); + + MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest( + List.of(new MemberActionSaveAppRequest("토다리", "OUT"))); + List unorderedMemberActions = Arrays.asList(memberAction2, memberAction1); + Action startAction = new Action(event, 3L); + + assertThatThrownBy( + () -> memberActionFactory.createMemberActions(request, unorderedMemberActions, startAction)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("현재 행사에 참여 중인 경우에 퇴장할 수 있다.") + @Test + void createMemberActionsTest1() { + Event event = eventRepository.save(new Event("test", "TOKEN")); + Action action = new Action(event, 1L); + MemberAction memberAction = new MemberAction(action, "토다리", MemberActionStatus.IN, 1L); + memberActionRepository.save(memberAction); + + MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest( + List.of(new MemberActionSaveAppRequest("토다리", "OUT"))); + Action startAction = new Action(event, 2L); + + assertThatCode(() -> memberActionFactory.createMemberActions(request, Arrays.asList(memberAction), startAction)) + .doesNotThrowAnyException(); + } + + @DisplayName("행사에서 퇴장한 경우에 입장할 수 있다.") + @Test + void createMemberActionsTest2() { + Event event = eventRepository.save(new Event("test", "TOKEN")); + Action action1 = new Action(event, 1L); + MemberAction memberAction1 = new MemberAction(action1, "토다리", MemberActionStatus.IN, 1L); + memberActionRepository.save(memberAction1); + Action action2 = new Action(event, 2L); + MemberAction memberAction2 = new MemberAction(action2, "토다리", MemberActionStatus.OUT, 2L); + memberActionRepository.save(memberAction2); + + MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest( + List.of(new MemberActionSaveAppRequest("토다리", "IN"))); + Action startAction = new Action(event, 3L); + + assertThatCode( + () -> memberActionFactory.createMemberActions(request, Arrays.asList(memberAction1, memberAction2), + startAction)) + .doesNotThrowAnyException(); + } + + @DisplayName("행사에 입장한 적 없는 경우에 입장할 수 있다.") + @Test + void createMemberActionsTest3() { + Event event = eventRepository.save(new Event("test", "TOKEN")); + Action action = new Action(event, 1L); + MemberAction memberAction = new MemberAction(action, "토다리", MemberActionStatus.IN, 1L); + memberActionRepository.save(memberAction); + + MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest( + List.of(new MemberActionSaveAppRequest("쿠키", "IN"))); + Action startAction = new Action(event, 2L); + + assertThatCode(() -> memberActionFactory.createMemberActions(request, Arrays.asList(memberAction), startAction)) + .doesNotThrowAnyException(); + } + + @DisplayName("행사에 입장하지 않았을 경우 퇴장할 수 없다.") + @Test + void createMemberActionTest4() { + Event event = eventRepository.save(new Event("test", "TOKEN")); + + MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest( + List.of(new MemberActionSaveAppRequest("쿠키", "OUT"))); + Action startAction = new Action(event, 2L); + + assertThatCode(() -> memberActionFactory.createMemberActions(request, new ArrayList<>(), startAction)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("행사에 이미 참여 중인 경우 다시 입장할 수 없다.") + @Test + void createMemberActionTest5() { + Event event = eventRepository.save(new Event("test", "TOKEN")); + Action action = new Action(event, 1L); + MemberAction memberAction = new MemberAction(action, "쿠키", MemberActionStatus.IN, 1L); + memberActionRepository.save(memberAction); + + MemberActionsSaveAppRequest request = new MemberActionsSaveAppRequest( + List.of(new MemberActionSaveAppRequest("쿠키", "IN"))); + Action startAction = new Action(event, 2L); + + assertThatCode(() -> memberActionFactory.createMemberActions(request, Arrays.asList(memberAction), startAction)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/server/src/test/java/server/haengdong/application/MemberActionServiceTest.java b/server/src/test/java/server/haengdong/application/MemberActionServiceTest.java index 16a9d1553..73a7c3d9d 100644 --- a/server/src/test/java/server/haengdong/application/MemberActionServiceTest.java +++ b/server/src/test/java/server/haengdong/application/MemberActionServiceTest.java @@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import server.haengdong.application.request.MemberActionSaveAppRequest; -import server.haengdong.application.request.MemberActionSaveListAppRequest; +import server.haengdong.application.request.MemberActionsSaveAppRequest; import server.haengdong.domain.Action; import server.haengdong.domain.Event; import server.haengdong.domain.MemberAction; @@ -48,7 +48,7 @@ void saveMemberActionTest() { MemberAction memberAction = new MemberAction(action, "망쵸", MemberActionStatus.IN, 1L); memberActionRepository.save(memberAction); - assertThatCode(() -> memberActionService.saveMemberAction("TOKEN", new MemberActionSaveListAppRequest( + assertThatCode(() -> memberActionService.saveMemberAction("TOKEN", new MemberActionsSaveAppRequest( List.of(new MemberActionSaveAppRequest("망쵸", "OUT"))))) .doesNotThrowAnyException(); } @@ -65,15 +65,15 @@ void saveMemberActionTest1() { MemberAction memberActionTwo = new MemberAction(actionTwo, "망쵸", MemberActionStatus.OUT, 1L); memberActionRepository.save(memberActionTwo); - assertThatCode(() -> memberActionService.saveMemberAction("TOKEN", new MemberActionSaveListAppRequest( + assertThatCode(() -> memberActionService.saveMemberAction("TOKEN", new MemberActionsSaveAppRequest( List.of(new MemberActionSaveAppRequest("망쵸", "IN"))))) .doesNotThrowAnyException(); } - @DisplayName("입장하지 않았을 경우 들어올 수 없다") + @DisplayName("행사에 입장하지 않았을 경우 퇴장할 수 없다") @Test void saveMemberActionTest2() { - MemberActionSaveListAppRequest appRequest = new MemberActionSaveListAppRequest( + MemberActionsSaveAppRequest appRequest = new MemberActionsSaveAppRequest( List.of(new MemberActionSaveAppRequest("TOKEN", "OUT"))); assertThatCode(() -> memberActionService.saveMemberAction("TOKEN", appRequest))