diff --git a/server/src/main/java/server/haengdong/application/EventService.java b/server/src/main/java/server/haengdong/application/EventService.java index dee023fbc..94c9dbda1 100644 --- a/server/src/main/java/server/haengdong/application/EventService.java +++ b/server/src/main/java/server/haengdong/application/EventService.java @@ -1,10 +1,18 @@ package server.haengdong.application; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import server.haengdong.application.request.EventAppRequest; +import server.haengdong.application.response.ActionAppResponse; import server.haengdong.application.response.EventAppResponse; import server.haengdong.application.response.EventDetailAppResponse; +import server.haengdong.domain.action.BillAction; +import server.haengdong.domain.action.BillActionRepository; +import server.haengdong.domain.action.MemberAction; +import server.haengdong.domain.action.MemberActionRepository; import server.haengdong.domain.event.Event; import server.haengdong.domain.event.EventRepository; import server.haengdong.domain.event.EventTokenProvider; @@ -17,6 +25,8 @@ public class EventService { private final EventRepository eventRepository; private final EventTokenProvider eventTokenProvider; + private final BillActionRepository billActionRepository; + private final MemberActionRepository memberActionRepository; public EventAppResponse saveEvent(EventAppRequest request) { String token = eventTokenProvider.createToken(); @@ -32,4 +42,46 @@ public EventDetailAppResponse findEvent(String token) { return EventDetailAppResponse.of(event); } + + public List findActions(String token) { + Event event = eventRepository.findByToken(token).orElseThrow(() -> new IllegalArgumentException("")); + + List billActions = billActionRepository.findByAction_Event(event).stream() + .sorted(Comparator.comparing(BillAction::getSequence)).toList(); + List memberActions = memberActionRepository.findAllByEvent(event).stream() + .sorted(Comparator.comparing(MemberAction::getSequence)).toList(); + + return getActionAppResponses(billActions, memberActions); + } + + private List getActionAppResponses( + List billActions, + List memberActions + ) { + int billActionIndex = 0; + int memberActionIndex = 0; + List actionAppResponses = new ArrayList<>(); + + while (billActionIndex < billActions.size() && memberActionIndex < memberActions.size()) { + BillAction billAction = billActions.get(billActionIndex); + MemberAction memberAction = memberActions.get(memberActionIndex); + if (billAction.getSequence() < memberAction.getSequence()) { + actionAppResponses.add(ActionAppResponse.of(billAction)); + billActionIndex++; + } else { + actionAppResponses.add(ActionAppResponse.of(memberAction)); + memberActionIndex++; + } + } + while (billActionIndex < billActions.size()) { + BillAction billAction = billActions.get(billActionIndex++); + actionAppResponses.add(ActionAppResponse.of(billAction)); + } + while (memberActionIndex < memberActions.size()) { + MemberAction memberAction = memberActions.get(memberActionIndex++); + actionAppResponses.add(ActionAppResponse.of(memberAction)); + } + + return actionAppResponses; + } } diff --git a/server/src/main/java/server/haengdong/application/response/ActionAppResponse.java b/server/src/main/java/server/haengdong/application/response/ActionAppResponse.java new file mode 100644 index 000000000..a1abcc35b --- /dev/null +++ b/server/src/main/java/server/haengdong/application/response/ActionAppResponse.java @@ -0,0 +1,54 @@ +package server.haengdong.application.response; + +import server.haengdong.domain.action.BillAction; +import server.haengdong.domain.action.MemberAction; +import server.haengdong.domain.action.MemberActionStatus; + +public record ActionAppResponse( + Long actionId, + String name, + Long price, + Long sequence, + ActionType actionType +) { + + public static ActionAppResponse of(BillAction billAction) { + return new ActionAppResponse( + billAction.getAction().getId(), + billAction.getTitle(), + billAction.getPrice(), + billAction.getSequence(), + ActionType.BILL + ); + } + + public static ActionAppResponse of(MemberAction memberAction) { + MemberActionStatus status = memberAction.getStatus(); + + return new ActionAppResponse( + memberAction.getAction().getId(), + memberAction.getMemberName(), + null, + memberAction.getSequence(), + ActionType.of(status) + ); + } + + public String actionTypeName() { + return actionType.name(); + } + + public enum ActionType { + BILL, + IN, + OUT, + ; + + private static ActionType of(MemberActionStatus memberActionStatus) { + if (MemberActionStatus.IN == memberActionStatus) { + return IN; + } + return OUT; + } + } +} diff --git a/server/src/main/java/server/haengdong/presentation/EventController.java b/server/src/main/java/server/haengdong/presentation/EventController.java index 260253e8b..9fbe098ce 100644 --- a/server/src/main/java/server/haengdong/presentation/EventController.java +++ b/server/src/main/java/server/haengdong/presentation/EventController.java @@ -12,6 +12,7 @@ import server.haengdong.presentation.request.EventSaveRequest; import server.haengdong.presentation.response.EventDetailResponse; import server.haengdong.presentation.response.EventResponse; +import server.haengdong.presentation.response.StepResponse; @RequiredArgsConstructor @RestController @@ -32,4 +33,11 @@ public ResponseEntity findEvent(@PathVariable("eventId") St return ResponseEntity.ok(eventDetailResponse); } + + @GetMapping("/api/events/{eventId}/actions") + public ResponseEntity findActions(@PathVariable("eventId") String token) { + StepResponse stepResponse = StepResponse.of(eventService.findActions(token)); + + return ResponseEntity.ok(stepResponse); + } } diff --git a/server/src/main/java/server/haengdong/presentation/response/ActionResponse.java b/server/src/main/java/server/haengdong/presentation/response/ActionResponse.java new file mode 100644 index 000000000..a58e60186 --- /dev/null +++ b/server/src/main/java/server/haengdong/presentation/response/ActionResponse.java @@ -0,0 +1,20 @@ +package server.haengdong.presentation.response; + +import server.haengdong.application.response.ActionAppResponse; + +public record ActionResponse( + Long actionId, + String name, + Long price, + Long sequence +) { + + public static ActionResponse of(ActionAppResponse actionAppResponse) { + return new ActionResponse( + actionAppResponse.actionId(), + actionAppResponse.name(), + actionAppResponse.price(), + actionAppResponse.sequence() + ); + } +} diff --git a/server/src/main/java/server/haengdong/presentation/response/ActionsResponse.java b/server/src/main/java/server/haengdong/presentation/response/ActionsResponse.java new file mode 100644 index 000000000..188c48dc9 --- /dev/null +++ b/server/src/main/java/server/haengdong/presentation/response/ActionsResponse.java @@ -0,0 +1,28 @@ +package server.haengdong.presentation.response; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import server.haengdong.application.response.ActionAppResponse; + +public record ActionsResponse( + String type, + String stepName, + Set members, + List actions +) { + + public static ActionsResponse of(List actions, Set members) { + List actionResponses = actions.stream() + .map(ActionResponse::of) + .toList(); + + String actionType = actions.get(0).actionTypeName(); + return new ActionsResponse( + actionType, + null, + new HashSet<>(members), + actionResponses + ); + } +} diff --git a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java new file mode 100644 index 000000000..76f92ad53 --- /dev/null +++ b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java @@ -0,0 +1,63 @@ +package server.haengdong.presentation.response; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import server.haengdong.application.response.ActionAppResponse; + +public record StepResponse( + List steps +) { + + public static StepResponse of(List actions) { + List actionsResponse = new ArrayList<>(); + Set members = new HashSet<>(); + ActionAppResponse firstAction = getFirstAction(actions); + List group = new ArrayList<>(); + group.add(firstAction); + String currentActionType = firstAction.actionTypeName(); + members.add(firstAction.name()); + + for (int i = 1; i < actions.size(); i++) { + ActionAppResponse action = actions.get(i); + String typeName = action.actionTypeName(); + if (currentActionType.equals(typeName)) { + if (typeName.equals("IN")) { + members.add(action.name()); + } + if (typeName.equals("OUT")) { + members.remove(action.name()); + } + group.add(action); + continue; + } + if (currentActionType.equals("BILL")) { + actionsResponse.add(ActionsResponse.of(group, members)); + } else { + actionsResponse.add(ActionsResponse.of(group, Set.of())); + } + currentActionType = typeName; + group.clear(); + if (typeName.equals("IN")) { + members.add(action.name()); + } + if (typeName.equals("OUT")) { + members.remove(action.name()); + } + group.add(action); + } + + if (currentActionType.equals("BILL")) { + actionsResponse.add(ActionsResponse.of(group, members)); + } else { + actionsResponse.add(ActionsResponse.of(group, null)); + } + + return new StepResponse(actionsResponse); + } + + private static ActionAppResponse getFirstAction(List actions) { + return actions.get(0); + } +} diff --git a/server/src/test/java/server/haengdong/application/EventServiceTest.java b/server/src/test/java/server/haengdong/application/EventServiceTest.java index 31b1c650e..5a3e8bce3 100644 --- a/server/src/test/java/server/haengdong/application/EventServiceTest.java +++ b/server/src/test/java/server/haengdong/application/EventServiceTest.java @@ -2,8 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; import static org.mockito.BDDMockito.given; +import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,8 +13,16 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import server.haengdong.application.request.EventAppRequest; +import server.haengdong.application.response.ActionAppResponse; import server.haengdong.application.response.EventAppResponse; import server.haengdong.application.response.EventDetailAppResponse; +import server.haengdong.domain.action.Action; +import server.haengdong.domain.action.ActionRepository; +import server.haengdong.domain.action.BillAction; +import server.haengdong.domain.action.BillActionRepository; +import server.haengdong.domain.action.MemberAction; +import server.haengdong.domain.action.MemberActionRepository; +import server.haengdong.domain.action.MemberActionStatus; import server.haengdong.domain.event.Event; import server.haengdong.domain.event.EventRepository; import server.haengdong.domain.event.EventTokenProvider; @@ -29,8 +39,20 @@ class EventServiceTest { @Autowired private EventRepository eventRepository; + @Autowired + private ActionRepository actionRepository; + + @Autowired + private BillActionRepository billActionRepository; + + @Autowired + private MemberActionRepository memberActionRepository; + @AfterEach void tearDown() { + billActionRepository.deleteAllInBatch(); + memberActionRepository.deleteAllInBatch(); + actionRepository.deleteAllInBatch(); eventRepository.deleteAllInBatch(); } @@ -56,4 +78,33 @@ void findEventTest() { assertThat(eventDetailAppResponse.eventName()).isEqualTo("행동대장 회식"); } + + @DisplayName("행사에 속한 모든 액션을 조회한다.") + @Test + void findActionsTest() { + Event event = new Event("행동대장 회식", "웨디_토큰"); + Action action = new Action(event, 1L); + MemberAction memberAction = new MemberAction(action, "토다리", MemberActionStatus.IN, 1L); + Action action1 = new Action(event, 2L); + MemberAction memberAction1 = new MemberAction(action1, "쿠키", MemberActionStatus.IN, 1L); + Action action2 = new Action(event, 3L); + BillAction billAction = new BillAction(action2, "뽕나무쟁이족발", 30000L); + eventRepository.save(event); + memberActionRepository.saveAll(List.of(memberAction, memberAction1)); + billActionRepository.save(billAction); + + List actionAppResponses = eventService.findActions("웨디_토큰"); + + assertThat(actionAppResponses).hasSize(3) + .extracting(ActionAppResponse::actionId, + ActionAppResponse::name, + ActionAppResponse::price, + ActionAppResponse::sequence, + ActionAppResponse::actionTypeName) + .containsExactly( + tuple(1L, "토다리", null, 1L, "IN"), + tuple(2L, "쿠키", null, 2L, "IN"), + tuple(3L, "뽕나무쟁이족발", 30000L, 3L, "BILL") + ); + } } diff --git a/server/src/test/java/server/haengdong/presentation/response/StepResponseTest.java b/server/src/test/java/server/haengdong/presentation/response/StepResponseTest.java new file mode 100644 index 000000000..aac78691c --- /dev/null +++ b/server/src/test/java/server/haengdong/presentation/response/StepResponseTest.java @@ -0,0 +1,57 @@ +package server.haengdong.presentation.response; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.ArrayList; +import java.util.List; +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.response.ActionAppResponse; +import server.haengdong.application.response.ActionAppResponse.ActionType; + +@SpringBootTest +class StepResponseTest { + + @Autowired + private ObjectMapper objectMapper; + + @DisplayName("") + @Test + void test() throws JsonProcessingException { + List actionAppResponse = new ArrayList<>(); + + // IN actions + ActionAppResponse actionAppResponse1 = new ActionAppResponse(3L, "망쵸", null, 3L, ActionType.IN); + actionAppResponse.add(actionAppResponse1); + ActionAppResponse actionAppResponse2 = new ActionAppResponse(4L, "백호", null, 4L, ActionType.IN); + actionAppResponse.add(actionAppResponse2); + + // BILL step 1 + ActionAppResponse actionAppResponse3 = new ActionAppResponse(1L, "감자탕", 10000L, 1L, ActionType.BILL); + actionAppResponse.add(actionAppResponse3); + ActionAppResponse actionAppResponse4 = new ActionAppResponse(2L, "인생네컷", 10000L, 2L, ActionType.BILL); + actionAppResponse.add(actionAppResponse4); + + // IN actions + ActionAppResponse actionAppResponse5 = new ActionAppResponse(5L, "소하", null, 5L, ActionType.IN); + actionAppResponse.add(actionAppResponse5); + ActionAppResponse actionAppResponse6 = new ActionAppResponse(6L, "웨디", null, 6L, ActionType.IN); + actionAppResponse.add(actionAppResponse6); + + // OUT actions + ActionAppResponse actionAppResponse7 = new ActionAppResponse(7L, "망쵸", null, 7L, ActionType.OUT); + actionAppResponse.add(actionAppResponse7); + ActionAppResponse actionAppResponse8 = new ActionAppResponse(8L, "백호", null, 8L, ActionType.OUT); + actionAppResponse.add(actionAppResponse8); + + // BILL step 2 + ActionAppResponse actionAppResponse9 = new ActionAppResponse(9L, "노래방", 20000L, 10L, ActionType.BILL); + actionAppResponse.add(actionAppResponse9); + + // StepResponse creation + StepResponse stepResponse = StepResponse.of(actionAppResponse); + System.out.println("stepResponse = " + stepResponse); + } +}