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] 여행 생성 API 구현 #31

Merged
merged 10 commits into from
Jan 8, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@
import org.doorip.common.ApiResponse;
import org.doorip.common.ApiResponseUtil;
import org.doorip.message.SuccessMessage;
import org.doorip.trip.dto.request.TripCreateRequest;
import org.doorip.trip.dto.response.TripCreateResponse;
import org.doorip.trip.dto.response.TripGetResponse;
import org.doorip.trip.service.TripService;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RequestMapping("/api/trips")
@Controller
public class TripApiController {
private final TripService tripService;

@PostMapping
public ResponseEntity<ApiResponse<?>> createTrip(@UserId final Long userId,
@RequestBody final TripCreateRequest request) {
TripCreateResponse response = tripService.createTripAndParticipant(userId, request);
return ApiResponseUtil.success(SuccessMessage.CREATED, response);
}

@GetMapping
public ResponseEntity<ApiResponse<?>> getTrips(@UserId final Long userId, @RequestParam final String progress) {
public ResponseEntity<ApiResponse<?>> getTrips(@UserId final Long userId,
@RequestParam final String progress) {
final TripGetResponse response = tripService.getTrips(userId, progress);
return ApiResponseUtil.success(SuccessMessage.OK, response);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.doorip.trip.dto.request;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.time.LocalDate;

public record TripCreateRequest(
String title,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate startDate,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate endDate,
int styleA,
int styleB,
int styleC,
int styleD,
int styleE
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.doorip.trip.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import org.doorip.trip.domain.Trip;

import java.time.LocalDate;

import static java.time.Period.between;

public record TripCreateResponse(
Long tripId,
String title,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate startDate,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate endDate,
String code,
int day
) {

public static TripCreateResponse of(Trip trip) {
return new TripCreateResponse(
trip.getId(),
trip.getTitle(),
trip.getStartDate(),
trip.getEndDate(),
trip.getCode(),
between(LocalDate.now(), trip.getStartDate()).getDays()
);
}
}
61 changes: 56 additions & 5 deletions doorip-api/src/main/java/org/doorip/trip/service/TripService.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
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;
import org.doorip.trip.domain.Participant;
import org.doorip.trip.domain.Role;
import org.doorip.trip.domain.Trip;
import org.doorip.trip.dto.request.TripCreateRequest;
import org.doorip.trip.dto.response.TripCreateResponse;
import org.doorip.trip.repository.ParticipantRepository;
import org.doorip.common.Constants;
import org.doorip.trip.dto.response.TripGetResponse;
import org.doorip.trip.repository.TripRepository;
import org.doorip.user.domain.User;
Expand All @@ -14,24 +19,61 @@
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.UUID;

import static org.doorip.trip.domain.Participant.createParticipant;
import static org.doorip.trip.domain.Trip.createTrip;
import java.util.List;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class TripService {
private final UserRepository userRepository;
private final TripRepository tripRepository;
private final ParticipantRepository participantRepository;
private final UserRepository userRepository;

@Transactional
public TripCreateResponse createTripAndParticipant(Long userId, TripCreateRequest request) {
User findUser = getUser(userId);
validateDate(request.startDate(), request.endDate());
String code = createCode();
Trip trip = createTrip(request, code);
createParticipant(request, findUser, trip);
tripRepository.save(trip);

return TripCreateResponse.of(trip);
}

public TripGetResponse getTrips(Long userId, String progress) {
User findUser = getUser(userId);
List<Trip> trips = getTripsAccordingToProgress(userId, progress);
return TripGetResponse.of(findUser.getName(), trips);
}

private User getUser(Long userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException(ErrorMessage.USER_NOT_FOUND));
private void validateDate(LocalDate startDate, LocalDate endDate) {
if (endDate.isBefore(LocalDate.now()) || endDate.isBefore(startDate)) {
throw new InvalidValueException(ErrorMessage.INVALID_DATE_TYPE);
}
}

private String createCode() {
String code;
do {
String uuid = UUID.randomUUID().toString();
code = uuid.substring(0, 6);
} while (isDuplicateCode(code));

return code;
}

private Trip createTrip(TripCreateRequest request, String code) {
return Trip.createTrip(request.title(), request.startDate(), request.endDate(), code);
}

private void createParticipant(TripCreateRequest request, User user, Trip trip) {
Participant.createParticipant(Role.HOST, request.styleA(), request.styleB(),
request.styleC(), request.styleD(), request.styleE(), user, trip);
}

private List<Trip> getTripsAccordingToProgress(Long userId, String progress) {
Expand All @@ -42,4 +84,13 @@ private List<Trip> getTripsAccordingToProgress(Long userId, String progress) {
}
throw new InvalidValueException(ErrorMessage.INVALID_REQUEST_PARAMETER_VALUE);
}

private User getUser(Long userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException(ErrorMessage.USER_NOT_FOUND));
}

private boolean isDuplicateCode(String code) {
return tripRepository.existsByCode(code);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum ErrorMessage {
INVALID_PLATFORM_TYPE(HttpStatus.BAD_REQUEST, "e4001", "유효하지 않은 플랫폼 타입입니다."),
INVALID_REQUEST_PARAMETER_VALUE(HttpStatus.BAD_REQUEST, "e4002", "유효하지 않은 요청 파라미터 값입니다."),
INVALID_ALLOCATOR_COUNT(HttpStatus.BAD_REQUEST, "e4003", "여행 TODO를 생성하기 위해 최소 1명 이상의 배정자가 필요합니다."),
INVALID_DATE_TYPE(HttpStatus.BAD_REQUEST, "e4004", "유효하지 않은 날짜 타입입니다."),

/**
* 401 Unauthorized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,30 @@ public class Participant extends BaseTimeEntity {
@OneToMany(mappedBy = "participant", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
private List<Allocator> allocators = new ArrayList<>();

public static Participant createParticipant(Role role, int styleA, int styleB, int styleC, int styleD, int styleE, User user, Trip trip){
Participant participant = Participant.builder()
.role(role)
.styleA(styleA)
.styleB(styleB)
.styleC(styleC)
.styleD(styleD)
.styleE(styleE)
.user(user)
.trip(trip)
.build();
participant.changeTrip(trip);

return participant;
}

public void changeTrip(Trip trip) {
if (this.trip != null) {
this.trip.removeParticipant(this);
}
this.trip = trip;
trip.addParticipant(this);
}

public void addAllocator(Allocator allocator) {
allocators.add(allocator);
}
Expand Down
19 changes: 18 additions & 1 deletion doorip-domain/src/main/java/org/doorip/trip/domain/Trip.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,29 @@ public class Trip extends BaseTimeEntity {
@Column(nullable = false)
private String code;
@Builder.Default
@OneToMany(mappedBy = "trip", cascade = CascadeType.REMOVE)
@OneToMany(mappedBy = "trip", cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
private List<Participant> participants = new ArrayList<>();
@Builder.Default
@OneToMany(mappedBy = "trip", cascade = CascadeType.REMOVE)
private List<Todo> todos = new ArrayList<>();

public static Trip createTrip(String title, LocalDate startDate, LocalDate endDate, String code) {
return Trip.builder()
.title(title)
.startDate(startDate)
.endDate(endDate)
.code(code)
.build();
}

public void addParticipant(Participant participant) {
participants.add(participant);
}

public void removeParticipant(Participant participant) {
participants.remove(participant);
}

public void addTodo(Todo todo) {
todos.add(todo);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ public interface TripRepository extends JpaRepository<Trip, Long> {
"and datediff(t.endDate, :now) < 0 " +
"order by datediff(:now, t.endDate)")
List<Trip> findCompleteTripsByUserId(@Param("userId") Long userId, @Param("now") LocalDate now);

boolean existsByCode(String code);
}