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 구현 #19 #60

Merged
merged 15 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions backend/src/main/java/com/staccato/member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package com.staccato.member.domain;

import java.util.List;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;

import org.hibernate.annotations.SQLDelete;

import com.staccato.config.domain.BaseEntity;
import com.staccato.travel.domain.TravelMember;

import lombok.AccessLevel;
import lombok.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.staccato.member.service.dto.response;

import com.staccato.member.domain.Member;

public record MemberResponse(
Long memberId,
String nickName,
String memberImage
) {
public MemberResponse(Member member) {
this(member.getId(), member.getNickname(), member.getImageUrl());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.staccato.member.service.dto.response;

import java.util.List;

import com.staccato.member.domain.Member;

public record MemberResponses(List<MemberResponse> members) {
public static MemberResponses from(List<Member> members) {
return new MemberResponses(members.stream()
.map(MemberResponse::new)
.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
import jakarta.validation.Valid;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.staccato.config.auth.MemberId;
import com.staccato.travel.service.TravelService;
import com.staccato.travel.service.dto.request.TravelRequest;
import com.staccato.travel.service.dto.response.TravelDetailResponses;
import com.staccato.travel.service.dto.response.TravelResponse;

import lombok.RequiredArgsConstructor;
Expand All @@ -28,4 +31,11 @@ public ResponseEntity<Void> createTravel(@Valid @RequestBody TravelRequest trave
TravelResponse travelResponse = travelService.createTravel(travelRequest, memberId);
return ResponseEntity.created(URI.create("/travels/" + travelResponse.travelId())).build();
}

@GetMapping
public ResponseEntity<TravelDetailResponses> readAllTravels(
@MemberId Long memberId,
@RequestParam(value = "year", required = false) Integer year) {
return ResponseEntity.ok(travelService.readAllTravels(memberId, year));
}
}
7 changes: 7 additions & 0 deletions backend/src/main/java/com/staccato/travel/domain/Travel.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.staccato.config.domain.BaseEntity;
import com.staccato.exception.StaccatoException;
import com.staccato.member.domain.Member;

import lombok.AccessLevel;
import lombok.Builder;
Expand Down Expand Up @@ -62,4 +63,10 @@ private void validateDate(LocalDate startAt, LocalDate endAt) {
public void addTravelMember(TravelMember travelMember) {
travelMembers.add(travelMember);
}

public List<Member> getMates() {
return travelMembers.stream()
.map(TravelMember::getMember)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.staccato.travel.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.staccato.travel.domain.TravelMember;

public interface TravelMemberRepository extends JpaRepository<TravelMember, Long> {
List<TravelMember> findAllByMemberId(long memberId);

@Query("SELECT tm FROM TravelMember tm WHERE tm.member.id = :memberId AND YEAR(tm.travel.startAt) = :year")
List<TravelMember> findAllByMemberIdAndTravelStartAtYear(@Param("memberId") long memberId, @Param("year") int year);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.staccato.travel.service;

import java.util.List;
import java.util.Optional;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.staccato.member.domain.Member;
import com.staccato.member.repository.MemberRepository;
import com.staccato.travel.domain.Travel;
import com.staccato.travel.domain.TravelMember;
import com.staccato.travel.repository.TravelMemberRepostiory;
import com.staccato.travel.repository.TravelMemberRepository;
import com.staccato.travel.repository.TravelRepository;
import com.staccato.travel.service.dto.request.TravelRequest;
import com.staccato.travel.service.dto.response.TravelDetailResponses;
import com.staccato.travel.service.dto.response.TravelResponse;

import lombok.RequiredArgsConstructor;
Expand All @@ -19,7 +23,7 @@
@Transactional(readOnly = true)
public class TravelService {
private final TravelRepository travelRepository;
private final TravelMemberRepostiory travelMemberRepostiory;
private final TravelMemberRepository travelMemberRepository;
private final MemberRepository memberRepository;

@Transactional
Expand All @@ -29,17 +33,40 @@ public TravelResponse createTravel(TravelRequest travelRequest, Long memberId) {
return new TravelResponse(travel);
}

private TravelMember saveTravelMember(Long memberId, Travel travel) {
private void saveTravelMember(Long memberId, Travel travel) {
Member member = getMemberById(memberId);
TravelMember mate = TravelMember.builder()
.travel(travel)
.member(member)
.build();
return travelMemberRepostiory.save(mate);
travelMemberRepository.save(mate);
}

private Member getMemberById(long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("Invalid Operation"));
}

public TravelDetailResponses readAllTravels(long memberId, Integer year) {
return Optional.ofNullable(year)
.map(y -> readAllByYear(memberId, y))
.orElseGet(() -> readAll(memberId));
}

private TravelDetailResponses readAll(long memberId) {
List<TravelMember> travelMembers = travelMemberRepository.findAllByMemberId(memberId);
return getTravelDetailResponses(travelMembers);
}

private TravelDetailResponses readAllByYear(long memberId, Integer year) {
List<TravelMember> travelMembers = travelMemberRepository.findAllByMemberIdAndTravelStartAtYear(memberId, year);
return getTravelDetailResponses(travelMembers);
}

private TravelDetailResponses getTravelDetailResponses(List<TravelMember> travelMembers) {
List<Travel> travels = travelMembers.stream()
.map(TravelMember::getTravel)
.toList();
return TravelDetailResponses.from(travels);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.staccato.travel.service.dto.response;

import java.time.LocalDate;

import com.staccato.member.service.dto.response.MemberResponses;
import com.staccato.travel.domain.Travel;

public record TravelDetailResponse(
Long travelId,
String travelThumbnail,
String travelTitle,
String description,
LocalDate startAt,
LocalDate endAt,
MemberResponses mates
) {
public TravelDetailResponse(Travel travel) {
this(
travel.getId(),
travel.getThumbnailUrl(),
travel.getTitle(),
travel.getDescription(),
travel.getStartAt(),
travel.getEndAt(),
MemberResponses.from(travel.getMates())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.staccato.travel.service.dto.response;

import java.util.List;

import com.staccato.travel.domain.Travel;

public record TravelDetailResponses(
List<TravelDetailResponse> travels
) {
public static TravelDetailResponses from(List<Travel> travels) {
return new TravelDetailResponses(travels.stream()
.map(TravelDetailResponse::new)
.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
Expand Down Expand Up @@ -101,4 +103,63 @@ void failCreateTravel(TravelRequest travelRequest, String expectedMessage) {
.body("message", is(expectedMessage))
.body("status", is(HttpStatus.BAD_REQUEST.toString()));
}

@DisplayName("사용자의 모든 여행 상세 목록을 조회한다.")
@TestFactory
Stream<DynamicTest> findAllTravels() {
return Stream.of(
createTravel(2024),
createTravel(2024),
DynamicTest.dynamicTest("사용자가 타임라인을 조회하면 2개의 여행 목록이 조회된다.", () ->
RestAssured.given().log().all()
.contentType(ContentType.JSON)
.header(HttpHeaders.AUTHORIZATION, USER_AUTHORIZATION)
.when().log().all()
.get("/travels")
.then().log().all()
.assertThat().statusCode(HttpStatus.OK.value())
.body("travels.size()", is(2)))
);
}

@DisplayName("사용자가 2023년도에 다녀온 모든 여행 상세 목록을 조회한다.")
@TestFactory
Stream<DynamicTest> findAllTravelsOn2023() {
return Stream.of(
createTravel(2023),
createTravel(2024),
DynamicTest.dynamicTest("사용자가 타임라인에서 2023년도를 선택하면 1개의 여행 목록이 조회된다.", () ->
RestAssured.given().log().all()
.contentType(ContentType.JSON)
.header(HttpHeaders.AUTHORIZATION, USER_AUTHORIZATION)
.param("year", 2023)
.when().log().all()
.get("/travels")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 메시지는 "사용자가 타임라인을 조회하면 1개의 여행 목록이 조회된다." 가 맞는 것 같네요! (2 -> 1로 변경)

.then().log().all()
.assertThat().statusCode(HttpStatus.OK.value())
.body("size()", is(1)))
);
}

private DynamicTest createTravel(int year) {
return DynamicTest.dynamicTest("사용자가 새로운 여행 상세를 추가한다.", () ->
RestAssured.given().log().all()
.contentType(ContentType.JSON)
.header(HttpHeaders.AUTHORIZATION, USER_AUTHORIZATION)
.body(createTravelRequest(year))
.when().log().all()
.post("/travels")
.then().log().all()
.assertThat().statusCode(HttpStatus.CREATED.value())
.header(HttpHeaders.LOCATION, containsString("/travels/")));
}

private TravelRequest createTravelRequest(int year) {
return new TravelRequest(
"https://example.com/travels/geumohrm.jpg",
year + "여름 휴가",
"친구들과 함께한 여름 휴가 여행",
LocalDate.of(year, 7, 1),
LocalDate.of(year, 7, 10));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.staccato.travel.repository;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.LocalDate;
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.autoconfigure.orm.jpa.DataJpaTest;

import com.staccato.member.domain.Member;
import com.staccato.member.repository.MemberRepository;
import com.staccato.travel.domain.Travel;
import com.staccato.travel.domain.TravelMember;

@DataJpaTest
class TravelMemberRepositoryTest {
@Autowired
private TravelMemberRepository travelMemberRepository;
@Autowired
private MemberRepository memberRepository;
@Autowired
private TravelRepository travelRepository;

@DisplayName("사용자 식별자와 년도로 여행 상세 목록을 조회한다.")
@Test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DisplayName이 빠진 것 같아요!

void findAllByMemberIdAndTravelStartAtYear() {
// given
Member member = memberRepository.save(Member.builder().nickname("staccato").build());
Travel travel = travelRepository.save(createTravel(LocalDate.of(2023, 7, 1), LocalDate.of(2023, 7, 10)));
Travel travel2 = travelRepository.save(createTravel(LocalDate.of(2023, 12, 31), LocalDate.of(2024, 1, 10)));
Travel travel3 = travelRepository.save(createTravel(LocalDate.of(2024, 7, 1), LocalDate.of(2024, 7, 10)));
travelMemberRepository.save(new TravelMember(member, travel));
travelMemberRepository.save(new TravelMember(member, travel2));
travelMemberRepository.save(new TravelMember(member, travel3));

// when
List<TravelMember> result = travelMemberRepository.findAllByMemberIdAndTravelStartAtYear(member.getId(), 2023);

// then
assertThat(result).hasSize(2);
}

private static Travel createTravel(LocalDate startAt, LocalDate endAt) {
return Travel.builder()
.title("여행")
.startAt(startAt)
.endAt(endAt)
.build();
}
}
Loading