diff --git a/doorip-api/src/main/java/org/doorip/common/Constants.java b/doorip-api/src/main/java/org/doorip/common/Constants.java index ad7e715..eb6ec3f 100644 --- a/doorip-api/src/main/java/org/doorip/common/Constants.java +++ b/doorip-api/src/main/java/org/doorip/common/Constants.java @@ -6,4 +6,6 @@ public abstract class Constants { public static final String CHARACTER_TYPE = "utf-8"; public static final String INCOMPLETE = "incomplete"; public static final String COMPLETE = "complete"; + public static final String OUR = "our"; + public static final String MY = "my"; } diff --git a/doorip-api/src/main/java/org/doorip/trip/api/TodoApiController.java b/doorip-api/src/main/java/org/doorip/trip/api/TodoApiController.java index 160a3bf..f63d3c9 100644 --- a/doorip-api/src/main/java/org/doorip/trip/api/TodoApiController.java +++ b/doorip-api/src/main/java/org/doorip/trip/api/TodoApiController.java @@ -1,17 +1,18 @@ package org.doorip.trip.api; import lombok.RequiredArgsConstructor; +import org.doorip.auth.UserId; import org.doorip.common.ApiResponse; import org.doorip.common.ApiResponseUtil; import org.doorip.message.SuccessMessage; import org.doorip.trip.dto.request.TodoCreateRequest; +import org.doorip.trip.dto.response.TodoGetResponse; import org.doorip.trip.service.TodoService; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RequiredArgsConstructor @RequestMapping("/api/trips") @@ -25,4 +26,13 @@ public ResponseEntity> createTripTodo(@PathVariable final Long tr todoService.createTripTodo(tripId, request); return ApiResponseUtil.success(SuccessMessage.CREATED); } + + @GetMapping("/{tripId}/todos") + public ResponseEntity> getTripTodos(@UserId final Long userId, + @PathVariable final Long tripId, + @RequestParam final String category, + @RequestParam final String progress) { + final List response = todoService.getTripTodos(userId, tripId, category, progress); + return ApiResponseUtil.success(SuccessMessage.OK, response); + } } diff --git a/doorip-api/src/main/java/org/doorip/trip/dto/response/TodoAllocatorResponse.java b/doorip-api/src/main/java/org/doorip/trip/dto/response/TodoAllocatorResponse.java new file mode 100644 index 0000000..74664c2 --- /dev/null +++ b/doorip-api/src/main/java/org/doorip/trip/dto/response/TodoAllocatorResponse.java @@ -0,0 +1,24 @@ +package org.doorip.trip.dto.response; + +import lombok.AccessLevel; +import lombok.Builder; +import org.doorip.trip.domain.Allocator; +import org.doorip.trip.domain.Participant; +import org.doorip.user.domain.User; + +import java.util.Objects; + +@Builder(access = AccessLevel.PRIVATE) +public record TodoAllocatorResponse( + String name, + boolean isOwner +) { + public static TodoAllocatorResponse of(Long userId, Allocator allocator) { + Participant participant = allocator.getParticipant(); + User user = participant.getUser(); + return TodoAllocatorResponse.builder() + .name(user.getName()) + .isOwner(Objects.equals(user.getId(), userId)) + .build(); + } +} diff --git a/doorip-api/src/main/java/org/doorip/trip/dto/response/TodoGetResponse.java b/doorip-api/src/main/java/org/doorip/trip/dto/response/TodoGetResponse.java new file mode 100644 index 0000000..440bb56 --- /dev/null +++ b/doorip-api/src/main/java/org/doorip/trip/dto/response/TodoGetResponse.java @@ -0,0 +1,30 @@ +package org.doorip.trip.dto.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AccessLevel; +import lombok.Builder; +import org.doorip.trip.domain.Secret; +import org.doorip.trip.domain.Todo; + +import java.time.LocalDate; +import java.util.List; + +@Builder(access = AccessLevel.PRIVATE) +public record TodoGetResponse( + Long todoId, + String title, + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") + LocalDate endDate, + List allocators, + boolean secret +) { + public static TodoGetResponse of(Todo todo, List allocators) { + return TodoGetResponse.builder() + .todoId(todo.getId()) + .title(todo.getTitle()) + .endDate(todo.getEndDate()) + .allocators(allocators) + .secret(Secret.of(todo.getSecret())) + .build(); + } +} diff --git a/doorip-api/src/main/java/org/doorip/trip/service/TodoService.java b/doorip-api/src/main/java/org/doorip/trip/service/TodoService.java index 5859b22..f3a86fb 100644 --- a/doorip-api/src/main/java/org/doorip/trip/service/TodoService.java +++ b/doorip-api/src/main/java/org/doorip/trip/service/TodoService.java @@ -1,32 +1,36 @@ package org.doorip.trip.service; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.doorip.common.Constants; import org.doorip.exception.EntityNotFoundException; import org.doorip.exception.InvalidValueException; import org.doorip.message.ErrorMessage; -import org.doorip.trip.domain.Participant; -import org.doorip.trip.domain.Secret; -import org.doorip.trip.domain.Todo; -import org.doorip.trip.domain.Trip; +import org.doorip.trip.domain.*; import org.doorip.trip.dto.request.TodoCreateRequest; +import org.doorip.trip.dto.response.TodoAllocatorResponse; +import org.doorip.trip.dto.response.TodoGetResponse; import org.doorip.trip.repository.ParticipantRepository; import org.doorip.trip.repository.TodoRepository; import org.doorip.trip.repository.TripRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; import static org.doorip.trip.domain.Allocator.createAllocator; +@Slf4j @RequiredArgsConstructor -@Transactional +@Transactional(readOnly = true) @Service public class TodoService { private final TripRepository tripRepository; private final ParticipantRepository participantRepository; private final TodoRepository todoRepository; + @Transactional public void createTripTodo(Long tripId, TodoCreateRequest request) { validateAllocators(request.allocators()); Trip findTrip = getTrip(tripId); @@ -35,6 +39,11 @@ public void createTripTodo(Long tripId, TodoCreateRequest request) { todoRepository.save(todo); } + public List getTripTodos(Long userId, Long tripId, String category, String progress) { + List todos = getTripTodosAccordingToCategoryAndProgress(userId, tripId, category, progress); + return getTripTodosResponse(userId, todos); + } + private void validateAllocators(List allocators) { if (allocators.isEmpty()) { throw new InvalidValueException(ErrorMessage.INVALID_ALLOCATOR_COUNT); @@ -57,8 +66,67 @@ private void createAllocators(List allocators, Todo todo) { }); } + private List getTripTodosAccordingToCategoryAndProgress(Long userId, Long tripId, String category, String progress) { + if (category.equals(Constants.OUR)) { + return getOurTodosAccordingToProgress(tripId, progress); + } else if (category.equals(Constants.MY)) { + return getMyTodosAccordingToProgress(userId, tripId, progress); + } + throw new InvalidValueException(ErrorMessage.INVALID_REQUEST_PARAMETER_VALUE); + } + + private List getTripTodosResponse(Long userId, List todos) { + List response = new ArrayList<>(); + todos.forEach(todo -> { + List allocators = todo.getAllocators(); + List allocatorResponses = getAllocatorResponses(userId, allocators); + TodoAllocatorResponse ownerAllocator = getOwnerAllocator(allocatorResponses); + sortAllocators(ownerAllocator, allocatorResponses); + response.add(TodoGetResponse.of(todo, allocatorResponses)); + }); + return response; + } + private Participant getParticipant(Long participantId) { return participantRepository.findById(participantId) .orElseThrow(() -> new EntityNotFoundException(ErrorMessage.PARTICIPANT_NOT_FOUND)); } + + private List getOurTodosAccordingToProgress(Long tripId, String progress) { + if (progress.equals(Constants.INCOMPLETE)) { + return todoRepository.findOurTodoByTripId(tripId, Secret.OUR, Progress.INCOMPLETE); + } else if (progress.equals(Constants.COMPLETE)) { + return todoRepository.findOurTodoByTripId(tripId, Secret.OUR, Progress.COMPLETE); + } + throw new InvalidValueException(ErrorMessage.INVALID_REQUEST_PARAMETER_VALUE); + } + + private List getMyTodosAccordingToProgress(Long userId, Long tripId, String progress) { + if (progress.equals(Constants.INCOMPLETE)) { + return todoRepository.findMyTodoByTripId(tripId, userId, Progress.INCOMPLETE); + } else if (progress.equals(Constants.COMPLETE)) { + return todoRepository.findMyTodoByTripId(tripId, userId, Progress.COMPLETE); + } + throw new InvalidValueException(ErrorMessage.INVALID_REQUEST_PARAMETER_VALUE); + } + + private List getAllocatorResponses(Long userId, List allocators) { + return new ArrayList<>(allocators.stream() + .map(allocator -> TodoAllocatorResponse.of(userId, allocator)) + .toList()); + } + + private TodoAllocatorResponse getOwnerAllocator(List allocatorResponses) { + return allocatorResponses.stream() + .filter(TodoAllocatorResponse::isOwner) + .findFirst() + .orElse(null); + } + + private void sortAllocators(TodoAllocatorResponse ownerAllocator, List allocatorResponses) { + if (ownerAllocator != null) { + allocatorResponses.remove(ownerAllocator); + allocatorResponses.add(0, ownerAllocator); + } + } } diff --git a/doorip-api/src/main/java/org/doorip/trip/service/TripService.java b/doorip-api/src/main/java/org/doorip/trip/service/TripService.java index 5cf398a..f297bc9 100644 --- a/doorip-api/src/main/java/org/doorip/trip/service/TripService.java +++ b/doorip-api/src/main/java/org/doorip/trip/service/TripService.java @@ -1,6 +1,7 @@ package org.doorip.trip.service; import lombok.RequiredArgsConstructor; +import org.doorip.common.Constants; import org.doorip.exception.EntityNotFoundException; import org.doorip.exception.InvalidValueException; import org.doorip.message.ErrorMessage; @@ -10,9 +11,8 @@ import org.doorip.trip.dto.request.TripCreateRequest; import org.doorip.trip.dto.request.TripVerifyRequest; import org.doorip.trip.dto.response.TripCreateResponse; -import org.doorip.trip.dto.response.TripResponse; -import org.doorip.common.Constants; import org.doorip.trip.dto.response.TripGetResponse; +import org.doorip.trip.dto.response.TripResponse; import org.doorip.trip.repository.TripRepository; import org.doorip.user.domain.User; import org.doorip.user.repository.UserRepository; @@ -20,9 +20,8 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; -import java.util.UUID; - import java.util.List; +import java.util.UUID; @RequiredArgsConstructor @Transactional(readOnly = true) @@ -39,7 +38,6 @@ public TripCreateResponse createTripAndParticipant(Long userId, TripCreateReques Trip trip = createTrip(request, code); createParticipant(request, findUser, trip); tripRepository.save(trip); - return TripCreateResponse.of(trip); } diff --git a/doorip-domain/src/main/java/org/doorip/trip/domain/Secret.java b/doorip-domain/src/main/java/org/doorip/trip/domain/Secret.java index 3841196..5b3cf0b 100644 --- a/doorip-domain/src/main/java/org/doorip/trip/domain/Secret.java +++ b/doorip-domain/src/main/java/org/doorip/trip/domain/Secret.java @@ -9,4 +9,8 @@ public static Secret of(boolean isSecret) { } return MY; } + + public static boolean of(Secret secret) { + return !secret.equals(OUR); + } } diff --git a/doorip-domain/src/main/java/org/doorip/trip/repository/TodoRepository.java b/doorip-domain/src/main/java/org/doorip/trip/repository/TodoRepository.java index 932ce39..3eb8499 100644 --- a/doorip-domain/src/main/java/org/doorip/trip/repository/TodoRepository.java +++ b/doorip-domain/src/main/java/org/doorip/trip/repository/TodoRepository.java @@ -1,7 +1,39 @@ package org.doorip.trip.repository; + +import org.doorip.trip.domain.Progress; +import org.doorip.trip.domain.Secret; import org.doorip.trip.domain.Todo; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; public interface TodoRepository extends JpaRepository { + @Query("select d " + + "from Todo d " + + "join Trip i " + + "on d.trip = i " + + "where i.id = :tripId " + + "and d.progress = :progress " + + "and d.secret = :secret " + + "order by d.endDate") + List findOurTodoByTripId(@Param("tripId") Long tripId, @Param("secret") Secret secret, @Param("progress") Progress progress); + + @Query("select d " + + "from Todo d " + + "join Trip i " + + "on d.trip = i " + + "join Allocator a " + + "on a.todo = d " + + "join Participant p " + + "on a.participant = p " + + "join User u " + + "on p.user = u " + + "where i.id = :tripId " + + "and u.id = :userId " + + "and d.progress = :progress " + + "order by d.endDate") + List findMyTodoByTripId(@Param("tripId") Long tripId, @Param("userId") Long userId, @Param("progress") Progress progress); }