Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 여행 TODO 전체 조회 API 구현 #47

Merged
merged 10 commits into from
Jan 9, 2024
2 changes: 2 additions & 0 deletions doorip-api/src/main/java/org/doorip/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -25,4 +26,13 @@ public ResponseEntity<ApiResponse<?>> createTripTodo(@PathVariable final Long tr
todoService.createTripTodo(tripId, request);
return ApiResponseUtil.success(SuccessMessage.CREATED);
}

@GetMapping("/{tripId}/todos")
public ResponseEntity<ApiResponse<?>> getTripTodos(@UserId final Long userId,
@PathVariable final Long tripId,
@RequestParam final String category,
@RequestParam final String progress) {
final List<TodoGetResponse> response = todoService.getTripTodos(userId, tripId, category, progress);
return ApiResponseUtil.success(SuccessMessage.OK, response);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<TodoAllocatorResponse> allocators,
boolean secret
) {
public static TodoGetResponse of(Todo todo, List<TodoAllocatorResponse> allocators) {
return TodoGetResponse.builder()
.todoId(todo.getId())
.title(todo.getTitle())
.endDate(todo.getEndDate())
.allocators(allocators)
.secret(Secret.of(todo.getSecret()))
.build();
}
}
78 changes: 73 additions & 5 deletions doorip-api/src/main/java/org/doorip/trip/service/TodoService.java
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -35,6 +39,11 @@ public void createTripTodo(Long tripId, TodoCreateRequest request) {
todoRepository.save(todo);
}

public List<TodoGetResponse> getTripTodos(Long userId, Long tripId, String category, String progress) {
List<Todo> todos = getTripTodosAccordingToCategoryAndProgress(userId, tripId, category, progress);
return getTripTodosResponse(userId, todos);
}

private void validateAllocators(List<Long> allocators) {
if (allocators.isEmpty()) {
throw new InvalidValueException(ErrorMessage.INVALID_ALLOCATOR_COUNT);
Expand All @@ -57,8 +66,67 @@ private void createAllocators(List<Long> allocators, Todo todo) {
});
}

private List<Todo> 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<TodoGetResponse> getTripTodosResponse(Long userId, List<Todo> todos) {
List<TodoGetResponse> response = new ArrayList<>();
todos.forEach(todo -> {
List<Allocator> allocators = todo.getAllocators();
List<TodoAllocatorResponse> 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<Todo> 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<Todo> 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<TodoAllocatorResponse> getAllocatorResponses(Long userId, List<Allocator> allocators) {
return new ArrayList<>(allocators.stream()
.map(allocator -> TodoAllocatorResponse.of(userId, allocator))
.toList());
}

private TodoAllocatorResponse getOwnerAllocator(List<TodoAllocatorResponse> allocatorResponses) {
return allocatorResponses.stream()
.filter(TodoAllocatorResponse::isOwner)
.findFirst()
.orElse(null);
}

private void sortAllocators(TodoAllocatorResponse ownerAllocator, List<TodoAllocatorResponse> allocatorResponses) {
if (ownerAllocator != null) {
allocatorResponses.remove(ownerAllocator);
allocatorResponses.add(0, ownerAllocator);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,19 +11,17 @@
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;
import org.springframework.stereotype.Service;
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)
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ public static Secret of(boolean isSecret) {
}
return MY;
}

public static boolean of(Secret secret) {
return !secret.equals(OUR);
}
}
Original file line number Diff line number Diff line change
@@ -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<Todo, Long> {
@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<Todo> 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<Todo> findMyTodoByTripId(@Param("tripId") Long tripId, @Param("userId") Long userId, @Param("progress") Progress progress);
}