Skip to content

Commit

Permalink
refactor: MemberActionFactory 코드 리팩터링
Browse files Browse the repository at this point in the history
  • Loading branch information
kunsanglee committed Jul 20, 2024
1 parent ad15f5b commit 14b730c
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -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<MemberAction> createMemberActions(
MemberActionSaveListAppRequest request,
MemberActionsSaveAppRequest request,
List<MemberAction> memberActions,
Event event
Action action
) {
memberActions.sort(Comparator.comparing(MemberAction::getSequence));
validateActions(request, memberActions);

Long memberGroupId = memberGroupIdProvider.createGroupId();
long lastSequence = getLastSequence(event);
List<MemberAction> memberActionList = new ArrayList<>();
List<MemberAction> createdMemberActions = new ArrayList<>();
List<MemberActionSaveAppRequest> 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<MemberAction> memberActions) {
private void validateActions(MemberActionsSaveAppRequest request, List<MemberAction> memberActions) {
for (MemberActionSaveAppRequest action : request.actions()) {
validateAction(memberActions, action);
}
}

private void validateAction(List<MemberAction> 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<MemberAction> actions, String name, String status) {
MemberActionStatus memberActionStatus = MemberActionStatus.of(status);
private boolean isInvalidStatus(List<MemberAction> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<MemberAction> findMemberActions = memberActionRepository.findAllByEvent(event);
List<MemberAction> memberActions = memberActionFactory.createMemberActions(request, findMemberActions, event);
Action action = createStartAction(event);
List<MemberAction> 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));
}
}
10 changes: 10 additions & 0 deletions server/src/main/java/server/haengdong/domain/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
@Entity
public class Action {

private static final long FIRST_SEQUENCE = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<MemberAction> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand All @@ -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))
Expand Down

0 comments on commit 14b730c

Please sign in to comment.