Skip to content

Commit 8e97bc5

Browse files
authored
✨ [Feat] 코스 관련 API 구현 (#95)
2 parents 3fbcecd + 7f5a1d2 commit 8e97bc5

33 files changed

+1244
-10
lines changed

build.gradle

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ dependencies {
5959
implementation 'org.springframework.boot:spring-boot-starter-mail'
6060
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
6161

62+
//Querydsl 추가
63+
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
64+
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
65+
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
66+
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
6267
// OAuth2
6368
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
6469

@@ -84,4 +89,8 @@ tasks.named('test') {
8489

8590
jar {
8691
enabled = false
87-
}
92+
}
93+
94+
clean {
95+
delete file('src/main/generated')
96+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.withtime.be.withtimebe.domain.date.controller.command;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
5+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
6+
import lombok.RequiredArgsConstructor;
7+
import org.namul.api.payload.response.DefaultResponse;
8+
import org.springframework.web.bind.annotation.*;
9+
import org.withtime.be.withtimebe.domain.date.converter.DateConverter;
10+
import org.withtime.be.withtimebe.domain.date.dto.request.DateRequestDTO;
11+
import org.withtime.be.withtimebe.domain.date.dto.response.DateResponseDTO;
12+
import org.withtime.be.withtimebe.domain.date.entity.DateCourseBookmark;
13+
import org.withtime.be.withtimebe.domain.date.entity.DatePlace;
14+
import org.withtime.be.withtimebe.domain.date.service.command.DateCommandService;
15+
import org.withtime.be.withtimebe.domain.member.entity.Member;
16+
import org.withtime.be.withtimebe.global.security.annotation.AuthenticatedMember;
17+
18+
import java.util.List;
19+
20+
21+
@RestController
22+
@RequiredArgsConstructor
23+
@RequestMapping("/api/v1/date-courses")
24+
public class DateCommandController {
25+
26+
private final DateCommandService dateCommandService;
27+
28+
@Operation(summary = "사용자 맞춤형 데이트 코스 생성 API by 제인", description = "데이트 코스 생성 API입니다")
29+
@ApiResponses(value = {
30+
@ApiResponse(responseCode = "200", description = "성공입니다")
31+
})
32+
@PostMapping("/")
33+
public DefaultResponse<DateResponseDTO.DateCourse> createDateCourse(
34+
@RequestBody DateRequestDTO.CreateDateCourse request
35+
// @AuthenticatedMember Member member
36+
){
37+
List<DatePlace> datePlaces = dateCommandService.createDateCourse(request);
38+
DateResponseDTO.DateCourse dateCourse = DateConverter.createDateCourseInfo(datePlaces);
39+
return DefaultResponse.created(dateCourse);
40+
}
41+
42+
@Operation(summary = "데이트 코스 북마크 생성 API by 제인",
43+
description = "직접 데이트 코스를 찾을 때 사용하는 데이트 코스 북마크 API입니다")
44+
@ApiResponses(value = {
45+
@ApiResponse(responseCode = "200", description = "성공입니다"),
46+
@ApiResponse(responseCode = "DATE_COURSE404_1",
47+
description = "해당 코스를 찾을 수 없습니다")
48+
})
49+
@PostMapping("/{dateCourseId}/bookmarks")
50+
public DefaultResponse<DateResponseDTO.DateCourseBookmark> createDateCourseBookmark(
51+
@PathVariable Long dateCourseId,
52+
@AuthenticatedMember Member member
53+
){
54+
DateCourseBookmark dateCourseBookmark = dateCommandService.createDateCourseBookmark(dateCourseId, member);
55+
DateResponseDTO.DateCourseBookmark responseDTO = DateConverter.createDateCourseBookmarkResponseDTO(dateCourseBookmark);
56+
return DefaultResponse.created(responseDTO);
57+
}
58+
59+
@Operation(summary = "데이트 코스 북마크 삭제 API by 제인",
60+
description = "데이트 코스 북마크 API입니다")
61+
@ApiResponses(value = {
62+
@ApiResponse(responseCode = "200", description = "성공입니다"),
63+
@ApiResponse(responseCode = "DATE_COURSE404_1",
64+
description = "해당 코스를 찾을 수 없습니다")
65+
})
66+
@DeleteMapping("/{dateCourseId}/bookmarks")
67+
public DefaultResponse<String> deleteDateCourseBookmark(
68+
@PathVariable Long dateCourseId,
69+
@AuthenticatedMember Member member
70+
){
71+
dateCommandService.deleteDateCourseBookmark(dateCourseId, member);
72+
return DefaultResponse.noContent();
73+
}
74+
75+
@Operation(summary = "데이트 코스 북마크 생성 API by 제인",
76+
description = "데이트 코스를 생성할 때 사용하는 데이트 코스 북마크 API입니다")
77+
@ApiResponses(value = {
78+
@ApiResponse(responseCode = "200", description = "성공입니다"),
79+
@ApiResponse(responseCode = "DATE_COURSE404_1",
80+
description = "해당 코스를 찾을 수 없습니다")
81+
})
82+
@PostMapping("/bookmarks")
83+
public DefaultResponse<DateResponseDTO.DateCourseBookmark> createDateCourseBookmarkWithGeneratedCourse(
84+
@RequestBody DateRequestDTO.SaveDateCourse request,
85+
@AuthenticatedMember Member member
86+
){
87+
DateCourseBookmark dateCourseBookmarkWithGeneratedCourse = dateCommandService.createDateCourseBookmarkWithGeneratedCourse(request, member);
88+
DateResponseDTO.DateCourseBookmark dateCourseBookmarkResponseDTO = DateConverter.createDateCourseBookmarkResponseDTO(dateCourseBookmarkWithGeneratedCourse);
89+
return DefaultResponse.created(dateCourseBookmarkResponseDTO);
90+
}
91+
92+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.withtime.be.withtimebe.domain.date.controller.query;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
5+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
6+
import lombok.RequiredArgsConstructor;
7+
import org.namul.api.payload.response.DefaultResponse;
8+
import org.springframework.data.domain.Page;
9+
import org.springframework.data.domain.Pageable;
10+
import org.springframework.data.web.PageableDefault;
11+
import org.springframework.web.bind.annotation.*;
12+
import org.withtime.be.withtimebe.domain.date.converter.DateConverter;
13+
import org.withtime.be.withtimebe.domain.date.dto.request.DateRequestDTO;
14+
import org.withtime.be.withtimebe.domain.date.dto.response.DateResponseDTO;
15+
import org.withtime.be.withtimebe.domain.date.entity.DateCourse;
16+
import org.withtime.be.withtimebe.domain.date.service.query.DateQueryService;
17+
import org.withtime.be.withtimebe.domain.member.entity.Member;
18+
import org.withtime.be.withtimebe.global.annotation.SwaggerPageable;
19+
import org.withtime.be.withtimebe.global.security.annotation.AuthenticatedMember;
20+
21+
@RestController
22+
@RequiredArgsConstructor
23+
@RequestMapping("/api/v1/date-courses")
24+
public class DateQueryController {
25+
26+
private final DateQueryService dateQueryService;
27+
28+
@Operation(summary = "데이트 코스 리스트 조회 API by 제인", description = "데이트 코스 전체 조회 API입니다.")
29+
@ApiResponses(value = {
30+
@ApiResponse(responseCode = "200", description = "성공입니다."),
31+
@ApiResponse(responseCode = "404", description = "DATE_COURSE404_1 : 해당하는 데이트 코스를 찾을 수 없습니다.")
32+
})
33+
@SwaggerPageable
34+
@PostMapping("/")
35+
public DefaultResponse<DateResponseDTO.DateCourseList> findDateCourses(
36+
@PageableDefault(page = 0, size = 10) Pageable pageable,
37+
@RequestBody DateRequestDTO.DateCourseSearchCond dateCourseSearchCond
38+
) {
39+
Page<DateCourse> dateCourses = dateQueryService.findDateCourses(dateCourseSearchCond, pageable);
40+
DateResponseDTO.DateCourseList response = DateConverter.createDateCourseList(dateCourses);
41+
return DefaultResponse.ok(response);
42+
}
43+
44+
@Operation(summary = "데이트 코스 북마크 리스트 조회 API by 제인", description = "데이트 코스 북마크 리스트 조회 API입니다")
45+
@ApiResponses(value = {
46+
@ApiResponse(responseCode = "200", description = "성공입니다"),
47+
@ApiResponse(responseCode = "DATE_COURSE_BOOKMARK404_1",
48+
description = "해당 코스를 찾을 수 없습니다")
49+
})
50+
@SwaggerPageable
51+
@PostMapping("/bookmarks")
52+
public DefaultResponse<DateResponseDTO.DateCourseList> findDateCourseBookmark(
53+
@PageableDefault(page = 0, size = 10) Pageable pageable,
54+
@RequestBody DateRequestDTO.DateCourseSearchCond dateCourseSearchCond,
55+
@AuthenticatedMember Member member
56+
){
57+
Page<DateCourse> bookmarkedDateCourses = dateQueryService.findDateCourseBookmarks(dateCourseSearchCond, pageable, member);
58+
DateResponseDTO.DateCourseList response = DateConverter.createDateCourseList(bookmarkedDateCourses);
59+
return DefaultResponse.ok(response);
60+
}
61+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package org.withtime.be.withtimebe.domain.date.converter;
2+
3+
import org.springframework.data.domain.Page;
4+
import org.withtime.be.withtimebe.domain.date.dto.request.DateRequestDTO;
5+
import org.withtime.be.withtimebe.domain.date.dto.response.DateResponseDTO;
6+
import org.withtime.be.withtimebe.domain.date.entity.DateCourse;
7+
import org.withtime.be.withtimebe.domain.date.entity.DateCourseBookmark;
8+
import org.withtime.be.withtimebe.domain.date.entity.DatePlace;
9+
import org.withtime.be.withtimebe.domain.date.entity.DatePlaceDateCourse;
10+
import org.withtime.be.withtimebe.domain.member.entity.Member;
11+
12+
import java.time.LocalDateTime;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
16+
public class DateConverter {
17+
18+
// DateCourse, Member -> DateCourseBookmark 엔티티 생성
19+
public static DateCourseBookmark createDateCourseBookmark(DateCourse dateCourse, Member member) {
20+
return DateCourseBookmark.builder()
21+
.member(member)
22+
.dateCourse(dateCourse)
23+
.build();
24+
}
25+
26+
// DateCourseBookmark -> DateCourseBookmark ResponseDTO 생성
27+
public static DateResponseDTO.DateCourseBookmark createDateCourseBookmarkResponseDTO(DateCourseBookmark dateCourseBookmark) {
28+
return DateResponseDTO.DateCourseBookmark.builder()
29+
.dateCourseId(dateCourseBookmark.getId())
30+
.build();
31+
}
32+
33+
// DateRequestDTO.SaveDateCourse -> DateCourse
34+
public static DateCourse createDateCourse(DateRequestDTO.SaveDateCourse dateCourse){
35+
return DateCourse.builder()
36+
.name(dateCourse.name())
37+
.build();
38+
}
39+
40+
// 나중에 생성한 정보를 리턴하는 데 사용,,? 근데 애초에 그 뭐야
41+
// builder()로 만들 때 잘 만들어주면 안되냐
42+
// List<DatePlace> -> DateResponseDTO.DateCourseInfo
43+
public static DateResponseDTO.DateCourse createDateCourseInfo(List<DatePlace> datePlaces){
44+
List<DateResponseDTO.DatePlace> datePlaceDtos = datePlaces.stream()
45+
.map(DateConverter::createDatePlace)
46+
.toList();
47+
48+
return DateResponseDTO.DateCourse.builder()
49+
.name(LocalDateTime.now().toLocalDate().toString())
50+
.build();
51+
}
52+
53+
// DatePlace -> DateResponseDTO.DatePlace
54+
public static DateResponseDTO.DatePlace createDatePlace(DatePlace datePlace) {
55+
return DateResponseDTO.DatePlace.builder()
56+
.name(datePlace.getName())
57+
.image(datePlace.getImage())
58+
.tel(datePlace.getTel())
59+
.averagePrice(datePlace.getAveragePrice())
60+
.information(datePlace.getInformation())
61+
.latitude(datePlace.getLatitude())
62+
.longitude(datePlace.getLongitude())
63+
.roadNameAddress(datePlace.getRoadNameAddress())
64+
.lotNumberAddress(datePlace.getLotNumberAddress())
65+
.placeType(datePlace.getPlaceType())
66+
.build();
67+
}
68+
69+
// DateResponseDTO.DateCourse -> DateResponseDTO.DateCourse
70+
public static DateResponseDTO.DateCourse createDateCourse(DateCourse dateCourse){
71+
72+
List<DateResponseDTO.DatePlace> datePlaces = dateCourse.getDatePlaceDateCourses().stream()
73+
.map(DatePlaceDateCourse::getDatePlace)
74+
.map(DateConverter::createDatePlace)
75+
.toList();
76+
77+
return DateResponseDTO.DateCourse.builder()
78+
.dateCourseId(dateCourse.getId())
79+
.name(dateCourse.getName())
80+
.datePlaces(datePlaces)
81+
.build();
82+
}
83+
84+
// Page<DateCourse> -> DateRequestDTO.DateCourseList
85+
public static DateResponseDTO.DateCourseList createDateCourseList(Page<DateCourse> dateCourses){
86+
List<DateResponseDTO.DateCourse> dateCourseList = dateCourses.stream()
87+
.map(DateConverter::createDateCourse)
88+
.toList();
89+
90+
return DateResponseDTO.DateCourseList.builder()
91+
.dateCourseList(dateCourseList)
92+
.totalPages(dateCourses.getTotalPages())
93+
.currentPage(dateCourses.getNumber())
94+
.currentSize(dateCourses.getSize())
95+
.hasNextPage(dateCourses.hasNext())
96+
.build();
97+
}
98+
99+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.withtime.be.withtimebe.domain.date.dto.request;
2+
3+
import com.fasterxml.jackson.annotation.JsonFormat;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.NotNull;
6+
import jakarta.validation.constraints.Size;
7+
import org.withtime.be.withtimebe.domain.date.entity.enums.DatePriceRange;
8+
import org.withtime.be.withtimebe.domain.date.entity.enums.DateTime;
9+
import org.withtime.be.withtimebe.domain.date.entity.enums.MealType;
10+
import org.withtime.be.withtimebe.domain.date.entity.enums.Transportation;
11+
12+
import java.time.LocalDateTime;
13+
import java.util.List;
14+
15+
public record DateRequestDTO() {
16+
17+
18+
public record CreateDateCourse(
19+
@NotNull(message = "예산을 선택해주세요")
20+
DatePriceRange budget,
21+
22+
@Size(min = 1, message = "최소 하나 이상의 값을 선택해주세요")
23+
@NotNull(message = "값이 비어있을 수 없습니다")
24+
List<String> datePlaces,
25+
26+
@NotNull(message = "데이트 시간을 선택해주세요")
27+
DateTime dateDurationTime,
28+
29+
List<MealType> mealPlan,
30+
31+
@NotBlank(message = "이동 수단을 선택해주세요")
32+
Transportation transportation,
33+
34+
@Size(min = 1, max = 3)
35+
@NotNull(message = "사용자 취향을 선택해주세요")
36+
List<String> userPreferredKeywords,
37+
38+
@JsonFormat(shape = JsonFormat.Shape.STRING,
39+
pattern = "yyyy-MM-dd'T'HH:mm:ss"
40+
)
41+
LocalDateTime startTime,
42+
43+
int attemptCount
44+
){}
45+
46+
public record SaveDateCourse(
47+
List<Long> datePlaceIds,
48+
String name
49+
){}
50+
51+
public record DateCourseSearchCond(
52+
DatePriceRange budget,
53+
List<String> datePlaces,
54+
DateTime dateDurationTime,
55+
List<MealType> mealTypes,
56+
Transportation transportation,
57+
List<String> userPreferredKeywords
58+
){}
59+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.withtime.be.withtimebe.domain.date.dto.response;
2+
3+
import lombok.Builder;
4+
import org.withtime.be.withtimebe.domain.date.entity.enums.PlaceType;
5+
6+
import java.util.List;
7+
8+
public record DateResponseDTO() {
9+
10+
@Builder
11+
public record DateCourseBookmark(
12+
Long dateCourseId
13+
){}
14+
15+
@Builder
16+
public record DatePlace(
17+
String name,
18+
String image,
19+
String tel,
20+
Integer averagePrice,
21+
String information,
22+
double latitude,
23+
double longitude,
24+
String roadNameAddress,
25+
String lotNumberAddress,
26+
PlaceType placeType
27+
){}
28+
29+
@Builder
30+
public record DateCourse(
31+
Long dateCourseId,
32+
String name,
33+
List<DateResponseDTO.DatePlace> datePlaces
34+
){}
35+
36+
@Builder
37+
public record DateCourseList(
38+
List<DateResponseDTO.DateCourse> dateCourseList,
39+
Integer totalPages,
40+
Integer currentPage,
41+
Integer currentSize,
42+
Boolean hasNextPage
43+
){}
44+
}

0 commit comments

Comments
 (0)