Skip to content

Commit

Permalink
Merge pull request #32 from Team-Going/feature/30
Browse files Browse the repository at this point in the history
[feat] 여행 TODO 생성 API 구현
  • Loading branch information
SunwoongH authored Jan 8, 2024
2 parents 10940da + a6e34b0 commit 8cc7002
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.doorip.todo.api;

import lombok.RequiredArgsConstructor;
import org.doorip.common.ApiResponse;
import org.doorip.common.ApiResponseUtil;
import org.doorip.message.SuccessMessage;
import org.doorip.todo.dto.request.TodoCreateRequest;
import org.doorip.todo.service.TodoService;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

@RequiredArgsConstructor
@RequestMapping("/api/todos")
@Controller
public class TodoApiController {
private final TodoService todoService;

@PostMapping
public ResponseEntity<ApiResponse<?>> createTripTodo(@RequestBody final TodoCreateRequest request) {
todoService.createTripTodo(request);
return ApiResponseUtil.success(SuccessMessage.CREATED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.doorip.todo.dto.request;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.time.LocalDate;
import java.util.List;

public record TodoCreateRequest(
Long tripId,
String title,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate endDate,
List<Long> allocators,
String memo,
boolean secret
) {
}
64 changes: 64 additions & 0 deletions doorip-api/src/main/java/org/doorip/todo/service/TodoService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.doorip.todo.service;

import lombok.RequiredArgsConstructor;
import org.doorip.exception.EntityNotFoundException;
import org.doorip.exception.InvalidValueException;
import org.doorip.message.ErrorMessage;
import org.doorip.todo.domain.Secret;
import org.doorip.todo.domain.Todo;
import org.doorip.todo.dto.request.TodoCreateRequest;
import org.doorip.todo.repository.TodoRepository;
import org.doorip.trip.domain.Participant;
import org.doorip.trip.domain.Trip;
import org.doorip.trip.repository.ParticipantRepository;
import org.doorip.trip.repository.TripRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static org.doorip.todo.domain.Allocator.createAllocator;

@RequiredArgsConstructor
@Transactional
@Service
public class TodoService {
private final TripRepository tripRepository;
private final ParticipantRepository participantRepository;
private final TodoRepository todoRepository;

public void createTripTodo(TodoCreateRequest request) {
validateAllocators(request.allocators());
Trip findTrip = getTrip(request.tripId());
Todo todo = createTodo(request, findTrip);
createAllocators(request.allocators(), todo);
todoRepository.save(todo);
}

private void validateAllocators(List<Long> allocators) {
if (allocators.isEmpty()) {
throw new InvalidValueException(ErrorMessage.INVALID_ALLOCATOR_COUNT);
}
}

private Trip getTrip(Long tripId) {
return tripRepository.findById(tripId)
.orElseThrow(() -> new EntityNotFoundException(ErrorMessage.TRIP_NOT_FOUND));
}

private Todo createTodo(TodoCreateRequest request, Trip trip) {
return Todo.createTodo(request.title(), request.endDate(), request.memo(), Secret.of(request.secret()), trip);
}

private void createAllocators(List<Long> allocators, Todo todo) {
allocators.forEach(participantId -> {
Participant findParticipant = getParticipant(participantId);
createAllocator(todo, findParticipant);
});
}

private Participant getParticipant(Long participantId) {
return participantRepository.findById(participantId)
.orElseThrow(() -> new EntityNotFoundException(ErrorMessage.PARTICIPANT_NOT_FOUND));
}
}
3 changes: 3 additions & 0 deletions doorip-api/src/main/resources/db/migration/V3__ddl.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
alter table allocator drop foreign key allocator_ibfk_1;
drop index participant_id on allocator;
alter table allocator add constraint allocator_ibfk_1 foreign key (participant_id) references participant (participant_id);
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum ErrorMessage {
BAD_REQUEST(HttpStatus.BAD_REQUEST, "e4000", "잘못된 요청입니다."),
INVALID_PLATFORM_TYPE(HttpStatus.BAD_REQUEST, "e4001", "유효하지 않은 플랫폼 타입입니다."),
INVALID_REQUEST_PARAMETER_VALUE(HttpStatus.BAD_REQUEST, "e4002", "유효하지 않은 요청 파라미터 값입니다."),
INVALID_ALLOCATOR_COUNT(HttpStatus.BAD_REQUEST, "e4003", "여행 TODO를 생성하기 위해 최소 1명 이상의 배정자가 필요합니다."),

/**
* 401 Unauthorized
Expand Down Expand Up @@ -44,6 +45,8 @@ public enum ErrorMessage {
ENTITY_NOT_FOUND(HttpStatus.NOT_FOUND, "e4040", "대상을 찾을 수 없습니다."),
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "e4041", "존재하지 않는 회원입니다."),
REFRESH_TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "e4042", "리프레쉬 토큰을 찾을 수 없습니다."),
TRIP_NOT_FOUND(HttpStatus.NOT_FOUND, "e4043", "존재하지 않는 여행입니다."),
PARTICIPANT_NOT_FOUND(HttpStatus.NOT_FOUND, "e4044", "존재하지 않는 참여자입니다."),

/**
* 405 Method Not Allowed
Expand Down
30 changes: 27 additions & 3 deletions doorip-domain/src/main/java/org/doorip/todo/domain/Allocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,33 @@ public class Allocator extends BaseTimeEntity {
@Column(name = "allocator_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "participant_id")
private Todo todo;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "todo_id")
private Todo todo;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "participant_id")
private Participant participant;

public static Allocator createAllocator(Todo todo, Participant participant) {
Allocator allocator = Allocator.builder()
.build();
allocator.changeTodo(todo);
allocator.changeParticipant(participant);
return allocator;
}

private void changeTodo(Todo todo) {
if (this.todo != null) {
this.todo.removeAllocator(this);
}
this.todo = todo;
todo.addAllocator(this);
}

private void changeParticipant(Participant participant) {
if (this.participant != null) {
this.participant.removeAllocator(null);
}
this.participant = participant;
participant.addAllocator(this);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package org.doorip.todo.domain;

public enum Secret {
OUR, MY
OUR, MY;

public static Secret of(boolean isSecret) {
if (isSecret) {
return OUR;
}
return MY;
}
}
30 changes: 29 additions & 1 deletion doorip-domain/src/main/java/org/doorip/todo/domain/Todo.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,34 @@ public class Todo extends BaseTimeEntity {
@JoinColumn(name = "trip_id")
private Trip trip;
@Builder.Default
@OneToMany(mappedBy = "todo", cascade = CascadeType.REMOVE)
@OneToMany(mappedBy = "todo", cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
private List<Allocator> allocators = new ArrayList<>();

public static Todo createTodo(String title, LocalDate endDate, String memo, Secret secret, Trip trip) {
Todo todo = Todo.builder()
.title(title)
.endDate(endDate)
.memo(memo)
.secret(secret)
.progress(Progress.INCOMPLETE)
.build();
todo.changeTrip(trip);
return todo;
}

private void changeTrip(Trip trip) {
if (this.trip != null) {
this.trip.removeTodo(this);
}
this.trip = trip;
trip.addTodo(this);
}

public void addAllocator(Allocator allocator) {
allocators.add(allocator);
}

public void removeAllocator(Allocator allocator) {
allocators.remove(allocator);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.doorip.todo.repository;

import org.doorip.todo.domain.Todo;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TodoRepository extends JpaRepository<Todo, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import org.doorip.todo.domain.Allocator;
import org.doorip.user.domain.User;

import java.util.ArrayList;
import java.util.List;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(access = AccessLevel.PRIVATE)
Expand Down Expand Up @@ -35,6 +38,15 @@ public class Participant extends BaseTimeEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "trip_id")
private Trip trip;
@OneToOne(mappedBy = "participant", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
private Allocator allocator;
@Builder.Default
@OneToMany(mappedBy = "participant", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
private List<Allocator> allocators = new ArrayList<>();

public void addAllocator(Allocator allocator) {
allocators.add(allocator);
}

public void removeAllocator(Allocator allocator) {
allocators.remove(allocator);
}
}
8 changes: 8 additions & 0 deletions doorip-domain/src/main/java/org/doorip/trip/domain/Trip.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,12 @@ public class Trip extends BaseTimeEntity {
@Builder.Default
@OneToMany(mappedBy = "trip", cascade = CascadeType.REMOVE)
private List<Todo> todos = new ArrayList<>();

public void addTodo(Todo todo) {
todos.add(todo);
}

public void removeTodo(Todo todo) {
todos.remove(todo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.doorip.trip.repository;

import org.doorip.trip.domain.Participant;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ParticipantRepository extends JpaRepository<Participant, Long> {
}

0 comments on commit 8cc7002

Please sign in to comment.